ZoneDateTime
および他のフィールドを含まない文字列からzone
を解析する方法は?
これは再現するためのSpockでのテストです:
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())
}
}
最初の2つのテストは成功しましたが、他のすべてのテストはDateTimeParseExceptionで失敗しました:
時間とゾーンをデフォルトに設定して不完全な日付を解析するにはどうすればよいですか?
_ISO_ZONED_DATE_TIME
_フォーマッタはゾーンまたはオフセット情報を予期しているため、解析は失敗します。ゾーン情報と時間部分の両方にオプションの部分があるDateTimeFormatter
を作成する必要があります。 ZonedDateTimeFormatter
をリバースエンジニアリングしてオプションのタグを追加するのはそれほど難しくありません。
次に、フォーマッタのparseBest()
メソッドを使用してString
を解析します。次に、最適とは言えない解析結果に対して、必要なデフォルトを使用してZonedDateTime
を作成できます。
_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());
_
フォーマッタにはwithZone()
method があり、不足しているタイムゾーンを提供するために呼び出すことができます。
ZonedDateTime.parse(
value,
DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(ZoneId.systemDefault()))
バグがあったことを覚えておいてください。バグが完全に機能するには8u20以降が必要です。