【问题标题】:Parsing a long string to a LocalDate variable将长字符串解析为 LocalDate 变量
【发布时间】:2019-07-24 03:07:47
【问题描述】:

我最近需要获取一个字符串并将其转换为 Java 中的 LocalDate 变量;我知道这是可能且简单的,但这是我没有看到的:

上下文:我正在使用 exiftool 通过 Java 运行时执行从图像中提取元数据。 问题是 exiftool 提供了下一个字符串:

2019:02:28 11:37:47-06:00

这不是您通常的标准 ISO 格式(或者至少我认为不是)。我不知道这是否使用了任何已经实现的格式标准。 因此,为了解决我的问题,我创建了下一个用于手动解析的代码:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ssZ", Locale.ENGLISH);
LocalDate date = LocalDate.parse(VAR_WITH_STR_TO_CONVERT, formatter);

我认为这是手动转换的一个很好的实现,但是这段代码会生成下一个异常:

> Text '2019:02:28 11:37:47-06:00' could not be parsed at index 19
at java.time.format.DateTimeFormatter.parseResolved0(Unknown Source)
at java.time.format.DateTimeFormatter.parse(Unknown Source)
at java.time.LocalDate.parse(Unknown Source)

我认为问题在于我提供要转换的格式字符串,但是在检查 DateTimeFormatter 文档后,我认为我有正确的字符串。

yyyy:MM:dd HH:mm:ssZ

我没有想法,有人知道这里的问题吗?

【问题讨论】:

    标签: java date datetime localdate


    【解决方案1】:

    2019:02:28 11:37:47-06:00

    这不是您常用的标准 ISO 格式

    类似于 ISO 8601:

    • 将中间的空格字符替换为T 字符。
    • 将前两个冒号替换为连字符。

    字符串操作

    您可以通过在 SPACE 上拆分字符串,替换第一部分中的前两个冒号,然后重新缝合在一起来做到这一点。

    String input = "2019:02:28 11:37:47-06:00";
    String[] parts = input.split(" ");
    String inputModified = parts[0].replace( ":" , "-" ) + "T" + parts[1] ;
    

    格式化代码:xxxxx

    或者您可以使用DateTimeFormatter 对象定义自定义格式模式。使用五个 x 代码字符来偏移时分和可选秒,并带有冒号字符分隔符。

    DateTimeFormatter f = DateTimeFormatter.ofPattern( "uuuu:MM:dd HH:mm:ssxxxxx") ;
    

    code running live at IdeOne.com 中查看此格式化程序。

    OffsetDateTime

    LocalDate.parse

    不,首先解析所有内容,而不仅仅是日期部分,以防万一您在某个时候发现它很有价值。解析为 OffsetDateTime,因为您的输入表示与 UTC 的偏移量(小时-分钟-秒),但不是完整的时区 (Continent/Region)。

    OffsetDateTime odt = OffsetDateTime.parse( input ) ;
    

    显然,您想要仅日期部分,没有时间,也没有与 UTC 的偏移量。假设您想要在该偏移量中看到的日期,只需调用 toLocalDate

    LocalDate ld = odt.toLocalDate() ;
    

    ld.toString(): 2019-02-28

    或者您可能想要在其他地方看到的日期。请记住,对于任何特定时刻,日期和时间在全球范围内因时区而异。在Asia/Japan 中可能是“明天”,而在America/Edmonton 中可能是“昨天”。

    UTC

    如果您想要以 UTC 显示的日期:

    OffsetDateTime odtUtc = odt.withOffsetSameInstant( ZoneOffset.UTC ) ;
    LocalDate ld = odtUTc.toLocalDate() ;
    

    ZonedDateTime

    或者您可能想要在特定时区看到的日期。

    ZoneId z = ZoneId.of( "Pacific/Auckland" ) ;
    ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;
    LocalDate ld = zdt.toLocalDate() ;
    

    提示:如果您可以确定日期部分中冒号的来源,请向他们介绍ISO 8601 标准。

    【讨论】:

    • “修复”日期字符串不是正确的答案,当DateTimeFormatter 可以解析值而不修复它时。所需要的只是正确的模式字符串。问题中没有任何内容表明 supposed 是 ISO 日期字符串。 OP 只是承认 OP 不相信它实际上是一个(这是正确的)。修复日期字符串只会使代码复杂化和模糊。
    • @Andreas 给每个他/她自己的。在处理错误/古怪的数据时,我通常在处理之前先清理。此外,该问题的作者表达了对 ISO 8601 的兴趣,因此我认为明确输入和标准之间的差异是有帮助的。最后,我将您的评论铭记于心,并在 DateTimeFormatter 中添加了必要的自定义格式模式,并在 IdeOne.com 上提供了一个实时代码示例。
    • 最宽松的模式是 XXXXXZZZZZ,因为它支持可选的秒数并支持 UTC 的 Z
    【解决方案2】:

    TL;DR对于时区,使用XXX,而不是Z,解析像-06:00 这样的值。


    文档,即DateTimeFormatter的javadoc,指定:

    偏移 X 和 x:这会根据模式字母的数量来格式化偏移。

    • 一个字母只输出小时,例如“+01”,除非分钟不为零,在这种情况下分钟也会输出,例如“+0130”。
    • 两个字母输出小时和分钟,不带冒号,例如“+0130”。
    • 三个字母输出小时和分钟,带冒号,如'+01:30'。
    • 四个字母输出小时和分钟以及可选的秒,不带冒号,例如“+013015”。
    • 五个字母输出小时和分钟以及可选的秒,带有冒号,例如'+01:30:15'。
    • 六个或更多字母会引发 IllegalArgumentException。

    当要输出的偏移量为零时,模式字母'X'(大写)将输出'Z',而模式字母'x'(小写)将输出'+00','+0000 ',或'+00:00'。

    偏移 Z:这会根据模式字母的数量来格式化偏移。

    • 一个、两个或三个字母输出小时和分钟,不带冒号,例如'+0130'。当偏移量为零时,输出将为“+0000”。
    • 四个字母输出完整形式的本地化偏移量,相当于四个字母的 Offset-O。如果偏移量为零,则输出将是相应的本地化偏移量文本。
    • 五个字母输出小时、分钟,如果非零,则可选秒,带冒号。如果偏移量为零,则输出“Z”。
    • 六个或更多字母会引发 IllegalArgumentException。

    如您所见,您使用了 1 Z,这意味着 -0600

    要解析带有小时和分钟(可选秒)、 冒号的时区,您需要突出显示的模式之一:

    XXX      UTC is `Z`.
    XXXXX    UTC is `Z`. Optional seconds.
    xxx      UTC is `+00:00`
    xxxxx    UTC is `+00:00`. Optional seconds.
    ZZZZZ    UTC is `Z`. Optional seconds.
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-10-14
      • 2017-05-12
      • 1970-01-01
      • 2018-03-08
      • 1970-01-01
      • 2015-03-19
      • 2015-02-06
      相关资源
      最近更新 更多