2016-06-26 24 views
7

Sto provando a ripulire un mix di vari codici attorno alla gestione datetime verso lo spazio dei nomi solo Java 8 java.time. In questo momento ho un piccolo problema con il valore predefinito DateTimeFormatter per Instant. Il formattatore DateTimeFormatter.ISO_INSTANT mostra solo i millisecondi quando non sono uguali a zero.java.time.DateTimeFormatter: Need ISO_INSTANT che esegue sempre i millisecondi

L'epoca è resa come 1970-01-01T00:00:00Z anziché 1970-01-01T00:00:00.000Z.

Ho effettuato un test unitario per spiegare il problema e il modo in cui è necessario che le date finali siano confrontate l'una con l'altra.

@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))); 
    } 
} 
+3

Anche il formato predefinito restituisce nanosecondi se sono diversi da zero. Se vuoi sempre millisecondi (e mai nanosecondi), perché non usare semplicemente un formattatore personalizzato, come 'DateTimeFormatter formatter = DateTimeFormatter.ofPattern (" aaaa-MM-gg'T'HH: mm: ss.SSSX "). WithZone (ZoneId .di ("UTC")); '? – mihi

+0

Volevo stare il più vicino possibile al formattatore usato per 'Instant :: toString()'. Forse avrei dovuto usare solo uno schema di corde. – Florent

risposta

7

OK, ho guardato il codice fonte e è abbastanza semplice:

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

Spero che funzioni per tutti gli scenari, e può aiutare qualcun altro. Non esitare ad aggiungere una risposta migliore/più pulita.

Giusto per spiegare da dove proviene, in the JDK's code,

ISO_INSTANT si definisce in questo modo:

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

E DateTimeFormatterBuilder::appendInstant è dichiarato come:

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

e il costruttore InstantPrinterParser firma è :

InstantPrinterParser(int fractionalDigits) 
+1

Sarebbe bello se ci si collegasse al codice sorgente esaminato. –

+0

Quindi, come possiamo cambiare questo per includere solo ms e non nano – user1172490

+0

@BasilBourque Ho aggiunto il riferimento al codice sorgente – Florent

0

Il accepted Answer by Florent è corretto e buono.

Voglio solo aggiungere qualche chiarimento.

Il modulo di formattazione menzionato, DateTimeFormatter.ISO_INSTANT, è l'impostazione predefinita solo per la classe Instant. Altre classi come OffsetDateTime e ZonedDateTime possono utilizzare altri formattatori per impostazione predefinita.

Le classi java.time offrono una risoluzione fino a nanosecond, granularità molto più sottile di milliseconds. Ciò significa fino a 9 cifre nella frazione decimale anziché solo 3 cifre.

Il comportamento di DateTimeFormatter.ISO_INSTANT varia in base al numero di cifre nella frazione decimale. Come dice il documento (sottolineatura mia):

Durante la formattazione, il secondo minuto viene sempre emesso. Il nano di secondo restituisce zero, tre, sei o nove cifre come necessario.

Quindi, a seconda del valore dei dati contenuti all'interno dell'oggetto Instant, è possibile vedere una delle seguenti uscite:

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 è destinata ad essere la elementare di base di java.time. Usalo frequentemente per il trasferimento di dati, l'archiviazione dei dati e lo scambio di dati. Quando si generano rappresentazioni String dei dati per la presentazione agli utenti, utilizzare OffsetDateTime o ZonedDateTime.