web-dev-qa-db-fra.com

Java analyse de date avec une précision en microsecondes ou nanosecondes

Selon la documentation de la classe SimpleDateFormat , Java ne prend pas en charge la granularité temporelle au-dessus des millisecondes dans ses modèles de date.

Donc, une chaîne de date comme

  • 2015-05-09 00: 10: 23.999750900 // Les 9 derniers chiffres indiquent les nanosecondes

une fois analysé via le modèle

  • aaaa-MM-jj HH: mm: ss.SSSSSSSSS // 9 symboles `` S ''

interprète en fait le nombre entier après le . symbole en (près d'un milliard!) millisecondes et non en nanosecondes, ce qui donne la date

  • 2015-05-20 21:52:53 UTC

soit plus de 11 jours à l'avance. Étonnamment, l'utilisation d'un plus petit nombre de symboles S entraîne toujours l'analyse des 9 chiffres (au lieu, disons, des 3 les plus à gauche pour .SSS).

Il existe 2 façons de gérer correctement ce problème:

  • Utiliser le prétraitement des chaînes
  • Utiliser une implémentation SimpleDateFormat personnalisée

Y aurait-il un autre moyen d'obtenir une solution correcte en fournissant simplement un modèle à l'implémentation standard de SimpleDateFormat, sans aucune autre modification de code ou manipulation de chaîne?

21
PNS

tl; dr

LocalDateTime.parse(                 // With resolution of nanoseconds, represent the idea of a date and time somewhere, unspecified. Does *not* represent a moment, is *not* a point on the timeline. To determine an actual moment, place this date+time into context of a time zone (apply a `ZoneId` to get a `ZonedDateTime`). 
    "2015-05-09 00:10:23.999750900"  // A `String` nearly in standard ISO 8601 format.
    .replace( " " , "T" )            // Replace SPACE in middle with `T` to comply with ISO 8601 standard format.
)                                    // Returns a `LocalDateTime` object.

Nan

Non, vous ne pouvez pas utiliser SimpleDateFormat pour gérer nanosecondes .

Mais votre prémisse que…

Java ne prend pas en charge la granularité temporelle supérieure à millisecondes dans ses modèles de date

… N'est plus vrai depuis Java 8 , 9, 10 et versions ultérieures avec Java.time classes intégrées. Et ce n'est pas vraiment le cas de Java 6 et Java 7 non plus, car la plupart des fonctionnalités Java.timesont portées en retour .

Java.time

SimpleDateFormat et les classes Java.util.Date/.Calendar Associées sont désormais dépassées par le nouveau package Java.time trouvé dans Java 8 ( Tutoriel ).

Les nouvelles classes Java.time prennent en charge la résolution nanoseconde . Cette prise en charge comprend l'analyse et la génération de neuf chiffres de fraction de seconde. Par exemple, lorsque vous utilisez l'API Java.time.formatDateTimeFormatter, la lettre de modèle S indique une "fraction de la seconde" plutôt que des "millisecondes", et elle peut faire face à la nanoseconde valeurs.

Instant

Par exemple, la classe Instant représente un moment dans [~ # ~] utc [~ # ~] . Sa méthode toString génère un objet String en utilisant le format standard ISO 8601 . Le Z à la fin signifie UTC, prononcé "Zulu".

instant.toString()  // Generate a `String` representing this moment, using standard ISO 8601 format.

2013-08-20T12: 34: 56.123456789Z

Notez que la capture du moment actuel dans Java 8 est limitée à une résolution en millisecondes. Les classes Java.time peuvent hold une valeur en nanosecondes, mais ne peuvent déterminer l'heure actuelle qu'en millisecondes. Cette limitation est due à l'implémentation de Clock. Dans Java 9 et versions ultérieures, une nouvelle implémentation Clock peut saisir l'instant actuel avec une résolution plus fine, en fonction des limites de votre matériel hôte et de votre système d'exploitation, généralement microsecondes dans mon expérience.

Instant instant = Instant.now() ;  // Capture the current moment. May be in milliseconds or microseconds rather than the maximum resolution of nanoseconds.

LocalDateTime

Votre exemple de chaîne d'entrée de 2015-05-09 00:10:23.999750900 N'a pas d'indicateur de fuseau horaire ou de décalage par rapport à UTC. Cela signifie que cela not ​​représente un moment, est not ​​un point sur la chronologie. Au lieu de cela, il représente potentiels moments sur une plage d'environ 26 à 27 heures, la plage de fuseaux horaires à travers le monde.

Analyse une telle entrée comme un objet LocalDateTime. Tout d'abord, remplacez l'espace au milieu par un T pour respecter le format ISO 8601, utilisé par défaut lors de l'analyse/génération de chaînes. Il n'est donc pas nécessaire de spécifier un modèle de formatage.

LocalDateTime ldt = 
        LocalDateTime.parse( 
            "2015-05-09 00:10:23.999750900".replace( " " , "T" )  // Replace SPACE in middle with `T` to comply with ISO 8601 standard format.
        ) 
;

Java.sql.Timestamp

La classe Java.sql.Timestamp gère également la résolution en nanosecondes, mais de manière maladroite. Il est généralement préférable de faire votre travail dans les classes Java.time. Plus besoin de réutiliser Timestamp à partir de JDBC 4.2 et versions ultérieures.

myPreparedStatement.setObject( … , instant ) ;

Et la récupération.

Instant instant = myResultSet.getObject( … , Instant.class ) ;

OffsetDateTime

La prise en charge de Instant n'est pas requise par la spécification JDBC, mais OffsetDateTime l'est. Donc, si le code ci-dessus échoue avec votre pilote JDBC, utilisez ce qui suit.

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ; 
myPreparedStatement.setObject( … , odt ) ;

Et la récupération.

Instant instant = myResultSet.getObject( … , OffsetDateTime.class ).toInstant() ;

Si vous utilisez un ancien pilote JDBC antérieur à 4.2, vous pouvez utiliser les méthodes toInstant et from pour revenir en arrière entre Java.sql.Timestamp et Java.time. Ces nouvelles méthodes de conversion ont été ajoutées aux anciennes anciennes classes.

Table of date-time types in Java (both legacy and modern) and in standard SQL


À propos Java.time

Le cadre Java.time est intégré dans Java 8 et versions ultérieures. Ces classes remplacent les anciennes classes de date-heure héritées , telles que Java.util.Date , Calendar , & SimpleDateFormat .

Le projet Joda-Time , désormais en mode de maintenance , conseille la migration vers le Java.time classes.

Pour en savoir plus, consultez le Tutoriel Oracle . Et recherchez Stack Overflow pour de nombreux exemples et explications. La spécification est JSR 310 .

Vous pouvez échanger Java.time objets directement avec votre base de données. Utilisez un pilote JDBC compatible avec JDBC 4.2 ou version ultérieure. Pas besoin de chaînes, pas besoin de classes Java.sql.*.

Où obtenir les classes Java.time?

Le projet ThreeTen-Extra étend Java.time avec des classes supplémentaires. Ce projet est un terrain d'essai pour de futurs ajouts possibles à Java.time. Vous pouvez trouver ici des classes utiles telles que Interval , YearWeek , YearQuarter et plus .

27
Basil Bourque