2014-12-04 14 views
7

Jak przeanalizować ZoneDateTime z ciągu, który nie zawiera zone i innych pól?Jak przeanalizować ZonedDateTime ze strefą domyślną?

Oto test Spocka do reprodukcji:

import spock.lang.Specification 
import spock.lang.Unroll 

import java.time.ZoneId 
import java.time.ZoneOffset 
import java.time.ZonedDateTime 
import java.time.format.DateTimeFormatter 

@Unroll 
class ZonedDateTimeParsingSpec extends Specification { 
    def "DateTimeFormatter.ISO_ZONED_DATE_TIME parsing incomplete date: #value #expected"() { 
     expect: 
     ZonedDateTime.parse(value, DateTimeFormatter.ISO_ZONED_DATE_TIME) == expected 
     where: 
     value       | expected 
     '2014-04-23T04:30:45.123Z'  | ZonedDateTime.of(2014, 4, 23, 4, 30, 45, 123_000_000, ZoneOffset.UTC) 
     '2014-04-23T04:30:45.123+01:00' | ZonedDateTime.of(2014, 4, 23, 4, 30, 45, 123_000_000, ZoneOffset.ofHours(1)) 
     '2014-04-23T04:30:45.123'  | ZonedDateTime.of(2014, 4, 23, 4, 30, 45, 123_000_000, ZoneId.systemDefault()) 
     '2014-04-23T04:30'    | ZonedDateTime.of(2014, 4, 23, 4, 30, 0, 0, ZoneId.systemDefault()) 
     '2014-04-23'     | ZonedDateTime.of(2014, 4, 23, 0, 0, 0, 0, ZoneId.systemDefault()) 
    } 
} 

pierwsze dwa testy przeszły, wszyscy inni nie powiodło się z DateTimeParseException:

  • '2014-04-23T04: 30: 45,123' nie może być analizowany w indeksie 23
  • „2014-04-23T04: 30” nie mógłby być przetwarzany z indeksem 16
  • „23.4.2014” nie może być analizowany w indeksie 10

Jak analizować niekompletne daty z czasem i strefą ustawioną na wartość domyślną?

Odpowiedz

11

Ponieważ formatator oczekuje wartości linii lub przesunięcia, przetwarzanie nie powiedzie się. Będziesz musiał wykonać DateTimeFormatter, który ma opcjonalne części zarówno dla informacji o strefie, jak i części czasu. To nie jest zbyt trudne odwracanie inżynierii ZonedDateTimeFormatter i dodawanie opcjonalnych tagów.

Następnie należy przeanalizować String za pomocą metody parseBest() formatyzatora. Następnie, aby uzyskać mniej optymalne wyniki analizy, możesz utworzyć numer ZonedDateTime, używając dowolnych ustawień domyślnych.

DateTimeFormatter formatter = new DateTimeFormatterBuilder() 
     .parseCaseInsensitive() 
     .append(ISO_LOCAL_DATE) 
     .optionalStart()   // time made optional 
     .appendLiteral('T') 
     .append(ISO_LOCAL_TIME) 
     .optionalStart()   // zone and offset made optional 
     .appendOffsetId() 
     .optionalStart() 
     .appendLiteral('[') 
     .parseCaseSensitive() 
     .appendZoneRegionId() 
     .appendLiteral(']') 
     .optionalEnd() 
     .optionalEnd() 
     .optionalEnd() 
     .toFormatter(); 

TemporalAccessor temporalAccessor = formatter.parseBest(value, ZonedDateTime::from, LocalDateTime::from, LocalDate::from); 
if (temporalAccessor instanceof ZonedDateTime) { 
    return ((ZonedDateTime) temporalAccessor); 
} 
if (temporalAccessor instanceof LocalDateTime) { 
    return ((LocalDateTime) temporalAccessor).atZone(ZoneId.systemDefault()); 
} 
return ((LocalDate) temporalAccessor).atStartOfDay(ZoneId.systemDefault()); 
+0

Dzięki, to wygląda lepiej, ale nie rozwiązuje problemu:

 Caused by: java.time.DateTimeException: Unable to obtain ZoneId from TemporalAccessor: {},ISO resolved to 2014-04-23T04:30:45.123 of type java.time.format.Parsed 
Tak, to znowu nie powiedzie się, ponieważ nie mogę ustawić Domyślna strefa czasowa dla analizatora składni. To samo dotyczy innych części, dla daty egzaminu. Na przykład, w jaki sposób można parsować ciąg znaków, taki jak "23: 45", do ZonedDateTime z bieżącą datą i strefą czasową systemu? – stokito

+0

Program działał poprawnie przy użyciu języka Java 8u25 dla wszystkich 5 przykładowego ciągu znaków w pytaniu. Więc nie jestem pewien, jaki masz problem. – bowmore

+1

Problem polega na tym, że chciałbym dostać ZonedDateTime z LocalDateTime – stokito

8

Formater ma withZone()method, który można wywołać w celu uzupełnienia brakującej strefy czasowej.

ZonedDateTime.parse(
    value, 
    DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(ZoneId.systemDefault())) 

Pamiętaj, że wystąpił błąd, więc potrzebujesz 8u20 lub nowszego, aby działał w pełni.

+0

które nie wydają się działać (próbowałem z 8u25) – bowmore

+0

Metoda 'withZone()' zawsze nadpisuje strefę nawet jeśli zawiera ona w ciąg – stokito

+0

@stokito: problem występuje na ciągi, które nie mają żadnych informacji do strefy zostać sparsowane. – bowmore

Powiązane problemy