【问题标题】:Produce INSTANT from different date-string formats [duplicate]从不同的日期字符串格式生成 INSTANT [重复]
【发布时间】:2019-08-14 01:21:44
【问题描述】:

我需要将日期字符串转换为 Instant 以及我期望用户提供的这三种格式

  • yyyy-MM-dd
  • yyyy-MM
  • yyyy

yyyy-MM-dd 转换为瞬间并不难

Optional.ofNullable(request.getDate())
                .map(LocalDate::parse)
                .map(date -> date.atStartOfDay(ZoneId.of("America/New_York")).toInstant())
                .map(pojo::dateAsInstant)

但是,我正在努力寻找将其他两种格式解析为 java.time.Instant 的解决方案。有人可以帮忙吗?

【问题讨论】:

  • 那么,您希望将其他两个转换为哪个时刻?您需要知道您需要什么月份和日期。

标签: java date-parsing


【解决方案1】:

使用DateTimeFormatterBuilder 及其parseDefaulting,以及格式字符串中的可选部分。

本地日期

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
        .appendPattern("uuuu[-MM[-dd]]")
        .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
        .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
        .toFormatter(Locale.US);
System.out.println(LocalDate.parse("2019-08-13", fmt)); // Prints: 2019-08-13
System.out.println(LocalDate.parse("2019-08"   , fmt)); // Prints: 2019-08-01
System.out.println(LocalDate.parse("2019"      , fmt)); // Prints: 2019-01-01

或者不使用模式字符串的长版本。

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
        .appendValue(ChronoField.YEAR, 4)
        .optionalStart()
        .appendLiteral('-')
        .appendValue(ChronoField.MONTH_OF_YEAR, 2)
        .optionalStart()
        .appendLiteral('-')
        .appendValue(ChronoField.DAY_OF_MONTH, 2)
        .optionalEnd()
        .optionalEnd()
        .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
        .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
        .toFormatter(Locale.US);
System.out.println(LocalDate.parse("2019-08-13", fmt)); // Prints: 2019-08-13
System.out.println(LocalDate.parse("2019-08"   , fmt)); // Prints: 2019-08-01
System.out.println(LocalDate.parse("2019"      , fmt)); // Prints: 2019-01-01

分区日期时间

使用 2 个额外的默认值,您可以直接解析为 ZonedDateTime

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
        .appendPattern("uuuu[-MM[-dd]]")
        .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
        .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
        .parseDefaulting(ChronoField.SECOND_OF_DAY, 0)
        .toFormatter(Locale.US)
        .withZone(ZoneId.of("America/New_York"));
System.out.println(ZonedDateTime.parse("2019-08-13", fmt)); // 2019-08-13T00:00-04:00[America/New_York]
System.out.println(ZonedDateTime.parse("2019-08"   , fmt)); // 2019-08-01T00:00-04:00[America/New_York]
System.out.println(ZonedDateTime.parse("2019"      , fmt)); // 2019-01-01T00:00-05:00[America/New_York]

即时

Instant 没有采用格式化程序的parse 方法,因此解析调用略有不同。

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
        .appendPattern("uuuu[-MM[-dd]]")
        .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
        .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
        .parseDefaulting(ChronoField.SECOND_OF_DAY, 0)
        .toFormatter(Locale.US)
        .withZone(ZoneId.of("America/New_York"));
System.out.println(fmt.parse("2019-08-13", Instant::from)); // 2019-08-13T04:00:00Z
System.out.println(fmt.parse("2019-08"   , Instant::from)); // 2019-08-01T04:00:00Z
System.out.println(fmt.parse("2019"      , Instant::from)); // 2019-01-01T05:00:00Z

带有可选破折号的即时

使用模式无法使其正常工作,但使用很长的方式构建格式化程序可以正常工作。

DateTimeFormatter fmt = new DateTimeFormatterBuilder()
        .appendValue(ChronoField.YEAR, 4)
        .optionalStart()
        .optionalStart()
        .appendLiteral('-')
        .optionalEnd()
        .appendValue(ChronoField.MONTH_OF_YEAR, 2)
        .optionalStart()
        .optionalStart()
        .appendLiteral('-')
        .optionalEnd()
        .appendValue(ChronoField.DAY_OF_MONTH, 2)
        .optionalEnd()
        .optionalEnd()
        .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
        .parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
        .parseDefaulting(ChronoField.SECOND_OF_DAY, 0)
        .toFormatter(Locale.US)
        .withZone(ZoneId.of("America/New_York"));
System.out.println(fmt.parse("2019-08-13", Instant::from)); // 2019-08-13T04:00:00Z
System.out.println(fmt.parse("20190813"  , Instant::from)); // 2019-08-13T04:00:00Z
System.out.println(fmt.parse("2019-08"   , Instant::from)); // 2019-08-01T04:00:00Z
System.out.println(fmt.parse("201908"    , Instant::from)); // 2019-08-01T04:00:00Z
System.out.println(fmt.parse("2019"      , Instant::from)); // 2019-01-01T05:00:00Z

【讨论】:

  • 问题:为什么输出会有小时差异? 04:00:00 和 05:00:00 以及仅添加 SECOND_OF_DAY 而不添加 HOUR 和 MINUTES_OF_DAY 是如何工作的?
  • @AMagic 由于夏令时,营业时间不同。在America/New_York 时区,8 月是夏令时 (EDT),而 1 月是标准时间 (EST)。 --- 之所以有效是因为SECOND_OF_DAY (0-86399) 与SECOND_OF_MINUTE (0-59) 不同,类似于DAY_OF_YEAR (1-366) 与DAY_OF_MONTH (1- 31).
  • 我明白了。谢谢!我通过fmt.parse("2019-08-13", Instant::from).truncatedTo(ChronoUnit.DAYS) 解决了这个问题
  • 因此,这会解析带有破折号的日期。我应该怎么做才能将201908之类的东西解析成2019-08-01T00:00:00Z
  • @AMagic 不知道为什么,但是如果您使用长样式而不是模式来构建格式化程序,它会起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-27
  • 2012-05-09
  • 2012-01-07
  • 1970-01-01
  • 2021-11-24
相关资源
最近更新 更多