web-dev-qa-db-fra.com

Comment analyser ZonedDateTime avec la zone par défaut?

Comment analyser ZoneDateTime à partir d'une chaîne qui ne contient pas zone et d'autres champs?

Voici un test dans Spock pour reproduire:

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())
    }
}

Les deux premiers tests ont réussi, tous les autres ont échoué avec DateTimeParseException:

  • '2014-04-23T04: 30: 45.123' n'a pas pu être analysé à l'index 23
  • '2014-04-23T04: 30' n'a pas pu être analysé à l'index 16
  • '2014-04-23' n'a pas pu être analysé à l'index 10

Comment analyser des dates incomplètes avec l'heure et le fuseau définis par défaut?

14
Sergey Ponomarev

Étant donné que le formateur ISO_ZONED_DATE_TIME Attend des informations de zone ou de décalage, l'analyse échoue. Vous devrez créer un DateTimeFormatter qui comporte des parties facultatives pour les informations de zone et la partie de temps. Il n'est pas trop difficile de procéder à une rétro-ingénierie du ZonedDateTimeFormatter et d'ajouter des balises facultatives.

Ensuite, vous analysez la String en utilisant la méthode parseBest() du formateur. Ensuite, pour des résultats d'analyse sous-optimaux, vous pouvez créer le ZonedDateTime en utilisant la valeur par défaut de votre choix.

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());
15
bowmore

Le formateur a une withZone()méthode qui peut être appelée pour fournir le fuseau horaire manquant.

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

Gardez à l'esprit qu'il y avait un bug, vous avez donc besoin de 8u20 ou version ultérieure pour qu'il fonctionne pleinement.

14
JodaStephen