2015-08-18 23 views
6

Dlaczego wydaje się, że biblioteka JDK8 DateTime nie analizuje ważnych ciągów znaków daty iso8601? To dławiki na przesunięcia strefy czasowej wyrażone jak "+01" zamiast "+01: 00"java.time.ZonedDateTime.parse i iso8601?

To działa:

java.time.ZonedDateTime.parse("2015-08-18T00:00+01:00") 

ta rzuca wyjątek składniowy:

java.time.ZonedDateTime.parse("2015-08-18T00:00+01") 

od ISO8601 Strona wikipedia:

Przesunięcie od czasu UTC jest dołączane do czasu w taki sam sposób, jak "Z" znajdowało się wyżej w rm ± [hh]: [mm], ± [gg] [mm] lub ± [gg]. Jeśli więc opisywany czas to 1 godzina przed czasem UTC (np. W Berlinie w okresie zimowym), oznaczenie strefy będzie wynosić "+01: 00", "+0100" lub po prostu "+01" .

EDIT: Wygląda to na prawdziwy uzasadniony błąd w JDK.

https://bugs.openjdk.java.net/browse/JDK-8032051

Wow, po przetestowaniu tej nowej Data Godzina rzeczy przez lata, myślałem, że złapałem coś tak oczywistego. Sądziłem również, że typy autorów JDK są na tyle rygorystyczne, że używają lepiej zautomatyzowanego zestawu testów.

AKTUALIZACJA: Jest to całkowicie poprawione w aktualnej kompilacji jdk-9. Właśnie potwierdziłem. Dokładnie to samo polecenie parse pokazane powyżej zawodzi w aktualnej kompilacji jdk-8 i działa doskonale w jdk-9.

ADDENDUM: FWIW, RFC 3339 oparty na ISO-8601, nie dopuszcza tego krótkiego układu. Musisz określić minuty w przesunięciach strefy czasowej.

+0

Pytanie brzmi: ...? (to znaczy, może chcieć zmienić tytuł - zakładam, że chciałbyś wiedzieć, dlaczego, a może jak wykonać tę pracę). – StaxMan

+0

Strefy czasowe są o wiele bardziej skomplikowane niż tylko przesunięcie. Możesz być bezpieczny w tej sytuacji, ale ogólnie myślenie o strefach czasowych pod względem przesunięć jest praktyką, której należy unikać. – Necreaux

+0

@StaxMan, wyjaśnione dokładne pytanie ... Myślałem, że to oczywiste. – clay

Odpowiedz

-1

Jest to całkowicie poprawione w aktualnej kompilacji jdk-9. Właśnie potwierdziłem. Dokładnie to samo polecenie parse pokazane powyżej zawodzi w aktualnej kompilacji jdk-8 i działa doskonale w jdk-9.

Dzięki nowej jdk-9 shell:

➜ jdk-9 bin/jshell 
| Welcome to JShell -- Version 9-ea 
| For an introduction type: /help intro 

jshell> java.time.ZonedDateTime.parse("2015-08-18T00:00+01") 
$1 ==> 2015-08-18T00:00+01:00 
+0

Dlaczego jest to zaznaczone jako zaakceptowana odpowiedź? Pytanie pyta, dlaczego jest to uszkodzone w JDK8. "Działa w JDK9" nie jest odpowiedzią. – Jamie

+0

To błąd w JDK8, nie jest on naprawiony aż do JDK9 ... Powinny one przenieść poprawkę do 8, ale do celów tego Q/A, jest to ostateczny stan problemu. – clay

0

Kod, który jest używany jest dodawana przez DateTimeFormatterBuilder.appendZoneId() który pozwala formatów dla strefy czasowej, jak

Na przykład, co następuje będzie analizować:

"Europe/London"   -- ZoneId.of("Europe/London") 
"Z"      -- ZoneOffset.UTC 
"UT"      -- ZoneId.of("UT") 
"UTC"      -- ZoneId.of("UTC") 
"GMT"      -- ZoneId.of("GMT") 
"+01:30"     -- ZoneOffset.of("+01:30") 
"UT+01:30"    -- ZoneOffset.of("+01:30") 
"UTC+01:30"    -- ZoneOffset.of("+01:30") 
"GMT+01:30"    -- ZoneOffset.of("+01:30") 

Można zdefiniować własną datę format czasu, aby umożliwić przesunięcia godzin, ale wiele miejsc na świecie ma ułamki godziny, takie jak Nepal, które jest +05: 45 i Korei Północnej, która ostatnio zmieniła się na +08: 30

+0

"+01" jest standardowym skrótem dla "+01: 00". Rozumiem, że występują nierównomierne przesunięcia, takie jak "+05: 45", w którym to przypadku możesz tego użyć i nie możesz pominąć minut. – clay

+0

Po drugie, nie chcę pisać niestandardowych rozszerzeń ani definiować własnego "formatu daty". Chcę, aby biblioteka datetime analizowała standardowe ciągi iso8601 pobierane ze źródeł zewnętrznych. JDK8 nie może tego robić. – clay

+1

@ clay "JDK8 nie może tego robić." Kiedy JDK8 nie robi dokładnie tego, czego chcemy, jako programiści, dlatego tworzymy kod. –

3

You use to domyślny formater: ISO_OFFSET_DATE_TIME (ponieważ przeanalizowano 2015-08-18T00:00+01:00).

W dokumentacji:

ta zwraca niezmienny formatowania zdolną do formatowania i analizowania ISO-8601 rozszerzony przesunięcie formatu daty i godziny. [...]

Identyfikator przesunięcia. Jeśli przesunięcie ma sekundy, będą one obsługiwane, mimo że nie jest to część normy ISO-8601. W parsowaniu nie jest uwzględniana wielkość liter.

It's (używać tylko tego dla tej domyślnej formater):

Identyfikator jest niewielka zmienność na standard ISO-8601 sformatowany ciąg dla offsetu. Istnieją trzy formaty:

  • Z - dla UTC (ISO-8601)
  • + hh: mm lub HH: mm - jeżeli sekundy są zerowe (ISO-8601)
  • + hh: mm : ss lub -hh: mm: ss - jeśli sekundy są niezerowe (nie ISO-8601) (nie +hh jak ISO-8601).

Wydaje się java.time (JDK 8) Czy nie jest pełna wdraża ISO-8601 w ogóle.


to:

java.time.ZonedDateTime.parse("2015-08-18T00:00+01:00"); // works 

odpowiada w przybliżeniu od źródła (JDK):

DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder(); 
DateTimeFormatter formatter = builder 
     .parseCaseInsensitive() 
     .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME) 
     .appendOffsetId() 
     .toFormatter(); 

java.time.ZonedDateTime.parse("2015-08-18T00:00+01:00", formatter); // it's same 

Można tworzyć własne DataTimeFormatter z DateTimeFormatterBuilder.

DateTimeFormatterBuilder builder2 = new DateTimeFormatterBuilder(); 
DateTimeFormatter formatter2 = builder2.parseCaseInsensitive() 
     .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME) 
     .appendPattern("X") // eg.: 
     .toFormatter(); 

java.time.ZonedDateTime.parse("2015-08-18T00:00+01", formatter2); // here you set +01 

Zamiast appendOffsetId() użyć appendPattern(String pattern) i ustawić 'X' lub 'x'.

Teraz możesz korzystać z datatime 2015-08-18T00:00+01.


Lub ... Użyj domyślnego ISO_OFFSET_DATE_TIME i dodać postfix :00.

java.time.ZonedDateTime.parse("2015-08-18T00:00+01" + ":00"); 

Ale to ostatnie jest złe rozwiązanie.

0

Thx @mkczyk powiedział Zrobiłem własną formatowania do pracy. potrzebowałem parser dla data jest jak

  1. "2016-02-14T18: 32: 04.150Z"
  2. "2016-02-14T21: 32: 04,150 + 04"
  3. „2016-02- 14T21: 32: 04,150 + 04: 00"

private static final DateTimeFormatter DATE_TIME_NANOSECONDS_OFFSET_FORMATTER = new DateTimeFormatterBuilder().parseCaseInsensitive().append(ISO_LOCAL_DATE_TIME) .appendFraction(ChronoField.NANO_OF_SECOND, 0, 3, true) .appendOffset("+HH:mm", "Z") .toFormatter();

walczył trochę z appendfraction (.., .., .., true) uważa nanosekund jak przecinku sekund.

ZonedDateTime zdt = ZonedDateTime.parse(textToParse, DATE_TIME_NANOSECONDS_OFFSET_FORMATTER);

A potem wykorzystane, aby uzyskać czas UTC z niego.

zdt = ZonedDateTime.ofInstant(zdt.toInstant(), ZoneId.of("UTC"));