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:
Comment analyser des dates incomplètes avec l'heure et le fuseau définis par défaut?
É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());
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.