2016-06-26 20 views
7

Próbuję oczyścić miks różnych kodów związanych z zarządzaniem datetime tylko w przestrzeni nazw Java 8 java.time. W tej chwili mam mały problem z domyślnym DateTimeFormatter dla Instant. Formater DateTimeFormatter.ISO_INSTANT wyświetla tylko milisekundy, gdy nie są równe zeru.java.time.DateTimeFormatter: Potrzebujesz ISO_INSTANT, który zawsze wyrenderowuje milisekundy

Epoka jest wyświetlana jako 1970-01-01T00:00:00Z zamiast 1970-01-01T00:00:00.000Z.

Zrobiłem test jednostkowy, aby wyjaśnić problem i sposób, w jaki potrzebujemy do ostatecznych dat, które należy porównać jeden z drugim.

@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

Domyślny format będzie również wyświetlał nanosekundy tylko wtedy, gdy różnią się od zera. Jeśli zawsze potrzebujesz milisekund (i nigdy nie nanosekund), możesz po prostu użyć niestandardowego formatera, np. 'DateTimeFormatter formatter = DateTimeFormatter.ofPattern (" rrrr-MM-dd'T'HH: mm: ss.SSSX ") .zZone (ZoneId ofof ("UTC")); '? – mihi

+0

Chciałem pozostać jak najbliżej formatera użytego do 'Instant :: toString()'. Może powinienem rzeczywiście użyć wzoru smyczkowego. – Florent

Odpowiedz

7

OK, spojrzałem w kodzie źródłowym i to całkiem proste:

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

Mam nadzieję, że to działa dla wszystkich scenariuszy, a to może pomóc komuś innemu. Nie wahaj się, aby dodać lepszą/czystszą odpowiedź.

Wystarczy, aby wyjaśnić, skąd przychodzi, in the JDK's code,

ISO_INSTANT jest zdefiniowany następująco:

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

I DateTimeFormatterBuilder::appendInstant jest zadeklarowana jako:

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

A konstruktor InstantPrinterParser podpis jest :

InstantPrinterParser(int fractionalDigits) 
+1

Byłoby miło, gdyby połączyć się z badanym kodem źródłowym. –

+0

Jak to zmienić, aby uwzględnić tylko ms, a nie nano – user1172490

+0

@BasilBourque Dodałem odwołanie do kodu źródłowego – Florent

0

Model accepted Answer by Florent jest poprawny i dobry.

Chcę tylko dodać wyjaśnienia.

Wymieniony formater, DateTimeFormatter.ISO_INSTANT, jest domyślny tylko dla klasy Instant. Inne klasy, takie jak OffsetDateTime i ZonedDateTime, mogą domyślnie używać innych formaterów.

Klasy java.time oferują rozdzielczość do nanosecond, o dużo lepszej szczegółowości niż milliseconds. Oznacza to do 9 cyfr w ułamku dziesiętnym zamiast 3 cyfr.

Zachowanie DateTimeFormatter.ISO_INSTANT różni się w zależności od liczby cyfr w ułamku dziesiętnym. Jak mówi doc (podkreślenie moje):

Podczas formatowania sekunda minutowa jest zawsze wyprowadzana. Nano-of-sekund wyprowadza zero, trzy, sześć lub dziewięć cyfr w razie potrzeby.

Więc w zależności od wartości danych zawartych w obiekcie Instant, można zobaczyć żadnej z tych wyjść:

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

Klasa Instant ma być podstawowy blok budujący java.time. Używaj go często do przekazywania danych, przechowywania danych i wymiany danych. Podczas generowania reprezentacji String danych do prezentacji użytkownikom, należy użyć OffsetDateTime lub ZonedDateTime.