Format instantané en chaîne

227

J'essaie de formater un instantané en une chaîne en utilisant la nouvelle java 8 time-api et un modèle:

Instant instant = ...;
String out = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(instant);

En utilisant le code ci-dessus, j'obtiens une exception qui se plaint d'un champ non pris en charge:

java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: YearOfEra
    at java.time.Instant.getLong(Instant.java:608)
    at java.time.format.DateTimePrintContext.getValue(DateTimePrintContext.java:298)
    ...
Dag
la source

Réponses:

310

Fuseau horaire

Pour formater Instantun fuseau horaire est nécessaire. Sans fuseau horaire, le formateur ne sait pas comment convertir l'instant en champs date-heure humains et lève donc une exception.

Le fuseau horaire peut être ajouté directement au formateur à l'aide de withZone().

DateTimeFormatter formatter =
    DateTimeFormatter.ofLocalizedDateTime( FormatStyle.SHORT )
                     .withLocale( Locale.UK )
                     .withZone( ZoneId.systemDefault() );

Génération de chaîne

Utilisez maintenant ce formateur pour générer la représentation String de votre Instant.

Instant instant = Instant.now();
String output = formatter.format( instant );

Dump sur console.

System.out.println("formatter: " + formatter + " with zone: " + formatter.getZone() + " and Locale: " + formatter.getLocale() );
System.out.println("instant: " + instant );
System.out.println("output: " + output );

Lors de l'exécution.

formatter: Localized(SHORT,SHORT) with zone: US/Pacific and Locale: en_GB
instant: 2015-06-02T21:34:33.616Z
output: 02/06/15 14:34
JodaStephen
la source
51
Je vous remercie!! BTW l'exception "champ non pris en charge" pointant, par exemple, l'année, est spectaculairement obtuse. Peut-être que cette situation devrait être détectée et qu'une exception pointant directement vers l'ID de zone manquante dans l'Instant devrait être levée!
davidbak
1
Plus bizarrement, si vous incluez .withZone (par exemple, .withZone (ZoneId.of ("Z"))) et formatez un LocalDateTime, la zone est IGNORÉE! Donc, tant que .withZone () est inclus, le même formateur peut être utilisé à la fois pour Instant et pour LocalDateTime, sans affecter le temps affiché pour ce dernier.
Glenn
1
Pourquoi est-il si difficile d'accepter le fait qu'Instant possède une TimeZone qui est GMT?
Koray Tugay
1
@KorayTugay Parce que même si les commentaires pédant "Instant est déjà GMT" peuvent être vrais, ils sont loin d'être utiles face à une trace d'exception levée car le formatage d'un Instant sans spécifier de fuseau horaire ne fonctionne pas. Cela aurait été bien si le formateur était par défaut en GMT, mais bon.
Ti Strga
Maintenant, cela vous donne le yyyy-MM-dd hh:mm:ssformat:DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss").withZone(ZoneId.of("Europe/Paris"));
WesternGun
39
public static void main(String[] args) {

    DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
            .withZone(ZoneId.systemDefault());

    System.out.println(DATE_TIME_FORMATTER.format(new Date().toInstant()));

}
二叉树
la source
3
Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire sur la manière et la raison pour laquelle il résout le problème améliorerait la valeur à long terme de la réponse.
Alexander
1
Bien que cet extrait de code puisse résoudre la question, y compris une explication aide vraiment à améliorer la qualité de votre message. N'oubliez pas que vous répondrez à la question pour les lecteurs à l'avenir, et ces personnes pourraient ne pas connaître les raisons de votre suggestion de code.
Rosário Pereira Fernandes
23
DateTimeFormatter.ISO_INSTANT.format(Instant.now())

Cela vous évite d'avoir à convertir en UTC. Cependant, certains frameworks temporels d'autres langages peuvent ne pas prendre en charge les millisecondes, vous devriez donc faire

DateTimeFormatter.ISO_INSTANT.format(Instant.now().truncatedTo(ChronoUnit.SECONDS))
Archimedes Trajano
la source
Qu'entendez-vous par «les cadres temporels d'une autre langue»? ISO_INSTANT.format () sera-t-il automatiquement tronqué en secondes?
Guangtong Shen
Certains frameworks n'attendent que le temps pour atteindre les secondes car ils ne respectent pas la convention ISO complète et se cassent lorsqu'il y a des millisecondes dans le cadre de la chaîne d'entrée.
Archimedes Trajano
21

La Instantclasse ne contient pas d'informations sur la zone, elle stocke uniquement l'horodatage en millisecondes de l'époque UNIX, c'est-à-dire le 1er janvier 1070 de l'UTC. Ainsi, le formateur ne peut pas imprimer une date car la date est toujours imprimée pour le fuseau horaire concret. Vous devez définir le fuseau horaire sur le formateur et tout ira bien, comme ceci:

Instant instant = Instant.ofEpochMilli(92554380000L);
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT).withLocale(Locale.UK).withZone(ZoneOffset.UTC);
assert formatter.format(instant).equals("07/12/72 05:33");
assert instant.toString().equals("1972-12-07T05:33:00Z");
Sergey Ponomarev
la source
4

Ou si vous souhaitez toujours utiliser un formateur créé à partir d'un modèle, vous pouvez simplement utiliser LocalDateTime au lieu d'Instant:

LocalDateTime datetime = LocalDateTime.now();
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(datetime)
Nik.exe
la source
4

Les instances sont déjà en UTC et ont déjà un format de date par défaut aaaa-MM-jj . Si vous êtes satisfait de cela et que vous ne voulez pas jouer avec les fuseaux horaires ou le formatage, vous pouvez également le toString()faire:

Instant instant = Instant.now();
instant.toString()
output: 2020-02-06T18:01:55.648475Z


Vous ne voulez pas le T et le Z? (Z indique que cette date est UTC. Z signifie "Zulu" aka "Zero hour offset" aka UTC):

instant.toString().replaceAll("[TZ]", " ")
output: 2020-02-06 18:01:55.663763


Vous voulez des millisecondes au lieu de nanosecondes? (Vous pouvez donc le placer dans une requête SQL):

instant.truncatedTo(ChronoUnit.MILLIS).toString().replaceAll("[TZ]", " ")
output: 2020-02-06 18:01:55.664

etc.

cs_pupil
la source
-3
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy MM dd");
String text = date.toString(formatter);
LocalDate date = LocalDate.parse(text, formatter);

Je crois que cela pourrait aider, vous devrez peut-être utiliser une sorte de variation de la date locale au lieu de l'instantané


la source