tl; dr
LocalDate.now( ZoneId.of( "Africa/Tunis" ) )
.atStartOfDay( ZoneId.of( "Africa/Tunis" ) )
.toEpochSecond()
…
LocalDate.now( ZoneId.of( "Africa/Tunis" ) )
.plusDays( 1 )
.atStartOfDay( ZoneId.of( "Africa/Tunis" ) )
.toEpochSecond()
…
"SELECT * FROM orders WHERE placed >= ? AND placed < ? ; "
…
myPreparedStatement.setObject( 1 , start )
myPreparedStatement.setObject( 2 , stop )
java.time
Vous utilisez d'anciennes classes date-heure gênantes qui sont maintenant héritées, supplantées par les classes java.time modernes .
Apparemment, vous stockez un moment dans votre base de données dans une colonne d'un type entier. C'est malheureux. Vous devriez plutôt utiliser une colonne d'un type tel que la norme SQL TIMESTAMP WITH TIME ZONE
. Mais, pour cette réponse, nous travaillerons avec ce que nous avons.
Si vos enregistrements représentent des moments avec une résolution de quelques millisecondes et que vous voulez tous les enregistrements pour une journée entière, nous avons besoin d'une plage horaire. Nous devons avoir un moment de départ et un moment d'arrêt. Votre requête n'a qu'un seul critère date-heure où elle aurait dû avoir une paire.
La LocalDate
classe représente une valeur de date uniquement sans heure du jour et sans fuseau horaire.
Un fuseau horaire est crucial pour déterminer une date. Pour un moment donné, la date varie dans le monde entier par zone. Par exemple, quelques minutes après minuit à Paris France est un nouveau jour alors qu'il est encore «hier» à Montréal Québec .
Si aucun fuseau horaire n'est spécifié, la machine virtuelle Java applique implicitement son fuseau horaire par défaut actuel. Cette valeur par défaut peut changer à tout moment, vos résultats peuvent donc varier. Mieux vaut spécifier explicitement votre fuseau horaire souhaité / attendu comme argument.
Indiquez un nom de fuseau horaire approprié dans le format continent/region
, tels que America/Montreal
, Africa/Casablanca
ou Pacific/Auckland
. N'utilisez jamais l'abréviation de 3 à 4 lettres telle que EST
ou IST
car ce ne sont pas de vrais fuseaux horaires, non standardisés et même pas uniques (!).
ZoneId z = ZoneId.of( "America/Montreal" ) ;
LocalDate today = LocalDate.now( z ) ;
Si vous souhaitez utiliser le fuseau horaire par défaut actuel de la JVM, demandez-le et passez-le en argument. En cas d'omission, la valeur par défaut actuelle de la JVM est appliquée implicitement. Mieux vaut être explicite, car la valeur par défaut peut être modifiée à tout moment pendant l'exécution par n'importe quel code dans n'importe quel thread de n'importe quelle application dans la JVM.
ZoneId z = ZoneId.systemDefault() ; // Get JVM’s current default time zone.
Ou spécifiez une date. Vous pouvez définir le mois par un nombre, avec une numérotation sensée de 1 à 12 pour janvier-décembre.
LocalDate ld = LocalDate.of( 1986 , 2 , 23 ) ; // Years use sane direct numbering (1986 means year 1986). Months use sane numbering, 1-12 for January-December.
Ou, mieux, utilisez les Month
objets enum prédéfinis, un pour chaque mois de l'année. Conseil: utilisez ces Month
objets dans toute votre base de code plutôt qu'un simple nombre entier pour rendre votre code plus auto-documenté, garantir des valeurs valides et assurer la sécurité de type .
LocalDate ld = LocalDate.of( 1986 , Month.FEBRUARY , 23 ) ;
Avec un LocalDate
en main, nous devons ensuite transformer cela en une paire de moments, le début et la fin de la journée. Ne supposez pas que la journée commence à 00:00:00 heure de la journée. En raison d'anomalies telles que l'heure d'été (DST), la journée peut commencer à une autre heure telle que 01:00:00. Laissez donc java.time déterminer le premier moment de la journée. Nous passons un ZoneId
argument à LocalDate::atStartOfDay
pour rechercher de telles anomalies. Le résultat est un ZonedDateTime
.
ZonedDateTime zdtStart = ld.atStartOfDay( z ) ;
Généralement, la meilleure approche pour définir un intervalle de temps est l'approche semi-ouverte où le début est inclusif tandis que la fin est exclusive . Ainsi, une journée commence par son premier moment et court jusqu'au premier moment du lendemain, mais sans l'inclure.
ZonedDateTime zdtStop = ld.plusDays( 1 ).atStartOfDay( z ) ; // Determine the following date, and ask for the first moment of that day.
Notre requête pour une journée entière ne peut pas utiliser la commande SQL BETWEEN
. Cette commande est entièrement fermée ( []
) (le début et la fin sont inclusifs) où, comme nous le voulons, semi-ouvert ( [)
). Nous utilisons une paire de critères >=
et <
.
Votre colonne est mal nommée. Évitez les milliers de mots réservés par diverses bases de données. Utilisons placed
dans cet exemple.
Votre code doit avoir utilisé des ?
espaces réservés pour spécifier nos moments.
String sql = "SELECT * FROM orders WHERE placed >= ? AND placed < ? ; " ;
Mais nous avons des ZonedDateTime
objets en main, alors que votre base de données stocke apparemment des entiers comme indiqué ci-dessus. Si vous aviez défini correctement votre colonne, nous pourrions simplement passer les ZonedDateTime
objets avec n'importe quel pilote JDBC prenant en charge JDBC 4.2 ou version ultérieure.
Mais à la place, nous devons obtenir un compte à partir de l'époque en secondes entières. Je suppose que votre date de référence d'époque est le premier moment de 1970 en UTC. Méfiez-vous des pertes de données possibles, car la ZonedDateTime
classe est capable d'une résolution nanoseconde. Toute fraction de seconde sera tronquée dans les lignes suivantes.
long start = zdtStart().toEpochSecond() ; // Transform to a count of whole seconds since 1970-01-01T00:00:00Z.
long stop = zdtStop().toEpochSecond() ;
Nous sommes maintenant prêts à transmettre ces entiers à notre code SQL défini ci-dessus.
PreparedStatement ps = con.prepareStatement( sql );
ps.setObject( 1 , start ) ;
ps.setObject( 2 , stop ) ;
ResultSet rs = ps.executeQuery();
Lorsque vous récupérez vos valeurs entières dans le ResultSet
, vous pouvez les transformer en Instant
objets (toujours en UTC) ou en ZonedDateTime
objets (avec un fuseau horaire attribué).
Instant instant = rs.getObject( … , Instant.class ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
À propos de java.time
Le framework java.time est intégré à Java 8 et versions ultérieures. Ces classes supplantent les anciens gênants hérités des classes date-heure tels que java.util.Date
, Calendar
, et SimpleDateFormat
.
Le projet Joda-Time , désormais en mode maintenance , conseille la migration vers les classes java.time .
Pour en savoir plus, consultez le didacticiel Oracle . Et recherchez Stack Overflow pour de nombreux exemples et explications. La spécification est JSR 310 .
Où obtenir les classes java.time?
- Java SE 8 , Java SE 9 et versions ultérieures
- Intégré.
- Partie de l'API Java standard avec une implémentation groupée.
- Java 9 ajoute quelques fonctionnalités et corrections mineures.
- Java SE 6 et Java SE 7
- Une grande partie de la fonctionnalité java.time est rétroportée vers Java 6 et 7 dans ThreeTen-Backport .
- Android
Le projet ThreeTen-Extra étend java.time avec des classes supplémentaires. Ce projet est un terrain d'essai pour d'éventuels futurs ajouts à java.time. Vous trouverez peut - être des classes utiles ici, comme Interval
, YearWeek
, YearQuarter
et plus .