web-dev-qa-db-fra.com

java.time.DateTimeFormatter: Besoin ISO_INSTANT qui affiche toujours des millisecondes

J'essaie de nettoyer un mélange de divers codes autour de la gestion datetime pour seulement Java 8 Java.time espace de noms. En ce moment, j'ai un petit problème avec la valeur par défaut DateTimeFormatter pour Instant . Le DateTimeFormatter.ISO_INSTANT le formateur affiche uniquement les millisecondes quand elles ne sont pas égales à zéro.

L'époque est représentée par 1970-01-01T00:00:00Z au lieu de 1970-01-01T00:00:00.000Z.

J'ai fait un test unitaire pour expliquer le problème et comment nous devons comparer les dates finales les unes aux autres.

@Test
public void Java8Date() {
    DateTimeFormatter formatter = DateTimeFormatter.ISO_INSTANT;
    String Epoch, almostEpoch, afterEpoch;

    { // before Epoch
        Java.time.Instant instant = Java.time.Instant.ofEpochMilli(-1);
        almostEpoch = formatter.format(instant);
        assertEquals("1969-12-31T23:59:59.999Z", almostEpoch );
    }

    { // Epoch
        Java.time.Instant instant = Java.time.Instant.ofEpochMilli(0);
        Epoch = formatter.format(instant);
        // This fails, I get 1970-01-01T00:00:00Z instead
        assertEquals("1970-01-01T00:00:00.000Z", Epoch );
    }

    { // after Epoch
        Java.time.Instant instant = Java.time.Instant.ofEpochMilli(1);
        afterEpoch = formatter.format(instant);
        assertEquals("1970-01-01T00:00:00.001Z", afterEpoch );
    }

    // The end game is to make sure this rule is respected (this is how we order things in dynamo):
    assertTrue(Epoch.compareTo(almostEpoch) > 0);
    assertTrue(afterEpoch.compareTo(Epoch) > 0); // <-- This assert would also fail if the second assert fails

    { // to confirm we're not showing nanos
        assertEquals("1970-01-01T00:00:00.000Z", formatter.format(Instant.Epoch.plusNanos(1)));
        assertEquals("1970-01-01T00:00:00.001Z", formatter.format(Instant.Epoch.plusNanos(1000000)));
    }
}
17
Florent

OK, j'ai regardé le code source et c'est assez simple:

DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendInstant(3).toFormatter();

J'espère que cela fonctionne pour tous les scénarios et qu'il peut aider quelqu'un d'autre. N'hésitez pas à ajouter une réponse meilleure/plus propre.

Juste pour expliquer d'où ça vient, dans le code du JDK ,

ISO_INSTANT est défini comme ceci:

public static final DateTimeFormatter ISO_INSTANT;
static {
    ISO_INSTANT = new DateTimeFormatterBuilder()
            .parseCaseInsensitive()
            .appendInstant()
            .toFormatter(ResolverStyle.STRICT, null);
}

Et DateTimeFormatterBuilder::appendInstant est déclaré comme:

public DateTimeFormatterBuilder appendInstant() {
    appendInternal(new InstantPrinterParser(-2));
    return this;
}

Et la signature du constructeur InstantPrinterParser est:

InstantPrinterParser(int fractionalDigits)
17
Florent

--- Réponse acceptée par Florent est correcte et bonne.

Je veux juste ajouter quelques éclaircissements.

Le formateur mentionné, DateTimeFormatter.ISO_INSTANT , est par défaut uniquement pour la classe Instant . D'autres classes telles que OffsetDateTime et ZonedDateTime peuvent utiliser d'autres formateurs par défaut.

Les classes Java.time offrent une résolution jusqu'à nanoseconde , une granularité beaucoup plus fine que millisecondes . Cela signifie jusqu'à 9 chiffres dans la fraction décimale plutôt que simplement 3 chiffres.

Le comportement de DateTimeFormatter.ISO_INSTANT varie en fonction du nombre de chiffres dans la fraction décimale. Comme le dit le doc (soulignement le mien):

Lors du formatage, la seconde est toujours sortie. La nano-seconde génère zéro, trois, six ou neuf chiffres selon les besoins.

Ainsi, selon la valeur de données contenue dans l'objet Instant, vous pouvez voir l'une de ces sorties:

2011-12-03T10: 15: 30Z

2011-12-03T10: 15: 30.100Z

2011-12-03T10: 15: 30.120Z

2011-12-03T10: 15: 30.123Z

2011-12-03T10: 15: 30.123400Z

2011-12-03T10: 15: 30.123456Z

2011-12-03T10: 15: 30.123456780Z

2011-12-03T10: 15: 30.123456789Z

La classe Instant est censée être le bloc de construction de base de Java.time. Utilisez-le fréquemment pour le passage de données, le stockage de données et l'échange de données. Lors de la génération de représentations String des données à présenter aux utilisateurs, utilisez OffsetDateTime ou ZonedDateTime .

2
Basil Bourque