【问题标题】:How to parse ZonedDateTime with default zone?如何使用默认区域解析 ZonedDateTime?
【发布时间】:2015-02-02 07:56:17
【问题描述】:

如何从不包含zone和其他字段的字符串中解析ZoneDateTime

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

前两个测试通过,所有其他测试都失败并出现 DateTimeParseException:

  • '2014-04-23T04:30:45.123' 无法在索引 23 处解析
  • '2014-04-23T04:30' 无法在索引 16 处解析
  • '2014-04-23' 无法在索引 10 处解析

如何解析设置为默认时间和区域的不完整日期?

【问题讨论】:

    标签: java date java-8 java-time


    【解决方案1】:

    由于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());
    

    【讨论】:

    • 谢谢,它看起来更好,但没有解决问题:
       原因:java.time.DateTimeException:无法从 TemporalAccessor:{} 获取 ZoneId,ISO 解析为 2014-04 -23T04:30:45.123 of type java.time.format.Parsed 
      所以,它再次失败,因为我无法为解析器设置默认时区。其他部分也一样,例如日期。例如,如何将 '23:45' 这样的字符串解析为具有当前日期和系统时区的 ZonedDateTime?
    • 对于问题中的所有 5 个示例字符串,使用 java 8u25 运行良好。所以,不知道你的问题是什么。
    • 问题是我想将 ZonedDateTime 设置为 LocalDateTime
    • 但我的代码在所有情况下都返回ZonedDateTime:所有三个返回语句都返回ZonedDateTime
    • 啊哈,是的,你说得对,它有效,谢谢我会使用它:) 但无论如何,我希望有一些更明显和简单的方法来定义格式。不幸的是,正如我所见,JavaTime 目前不提供它
    【解决方案2】:

    格式化程序有一个withZone()method,可以调用它来提供缺少的时区。

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

    请记住,存在错误,因此您需要 8u20 或更高版本才能完全工作。

    【讨论】:

    • 方法withZone() 总是覆盖区域,即使它包含在字符串中
    • @stokito :问题出现在没有要解析的区域信息的字符串上。
    • @bowmore 问题是我需要“默认或首选时区”而不是覆盖它
    猜你喜欢
    • 2018-02-23
    • 2016-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-03
    • 2019-02-17
    • 1970-01-01
    相关资源
    最近更新 更多