编程语言
首页 > 编程语言> > java-使用不必要的时间和时区解析和格式化LocalDate

java-使用不必要的时间和时区解析和格式化LocalDate

作者:互联网

编辑:

我打开了一个错误,并已被Oracle确认.您可以在此处遵循分辨率:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8216414

我正在与一个LDAP存储库交互,该存储库以如下所示的时间和时区存储一个人的生日:

>如果生日是“ 27-12-2018”,则LDAP字符串是“ 20181227000000 0000”.

我找不到使用相同模式解析和格式化生日的方法.

以下代码适用于格式化,但不适用于解析:

LocalDate date = LocalDate.of(2018, 12, 27);
String pattern = "yyyyMMdd'000000+0000'";
DateTimeFormatter birthdateFormat = DateTimeFormatter.ofPattern(pattern);

// Outputs correctly 20181227000000+0000
date.format(birthdateFormat);

// Throw a DatetimeParseException at index 0
date = LocalDate.parse("20181227000000+0000", birthdateFormat);

以下代码可很好地用于解析,但不适用于格式化

LocalDate date = LocalDate.of(2018, 12, 27);
String pattern = "yyyyMMddkkmmssxx";
DateTimeFormatter birthdateFormat = DateTimeFormatter.ofPattern(pattern);

// Throws a UnsupportedTemporalTypeException for ClockHourOfDay not supported
// Anyway I would have an unwanted string with non zero hour, minute, second, timezone
date.format(birthdateFormat);

// Parse correctly the date to 27-12-2018
date = LocalDate.parse("20181227000000+0000", birthdateFormat);

哪种模式可以同时满足解析和格式化要求?

我是否被迫使用2种不同的模式?

我问是因为模式是在属性文件中配置的.我只想在此属性文件中配置1个模式.我想外部化该模式,因为LDAP不是我的项目的一部分,它是共享资源,并且我不能保证格式不能更改.

解决方法:

我建议:

    LocalDate date = LocalDate.of(2018, Month.DECEMBER, 27);
    String pattern = "yyyyMMddHHmmssxx";
    DateTimeFormatter birthdateFormat = DateTimeFormatter.ofPattern(pattern);

    // Outputs 20181227000000+0000
    String formatted = date.atStartOfDay(ZoneOffset.UTC).format(birthdateFormat);
    System.out.println(formatted);

    // Parses to 2018-12-27T00:00Z
    OffsetDateTime odt = OffsetDateTime.parse("20181227000000+0000", birthdateFormat);
    System.out.println(odt);
    // Validate
    if (! odt.toLocalTime().equals(LocalTime.MIN)) {
        System.out.println("Unexpected time of day: " + odt);
    }
    if (! odt.getOffset().equals(ZoneOffset.UTC)) {
        System.out.println("Unexpected time zone offset: " + odt);
    }
    // Converts to 2018-12-27
    date = odt.toLocalDate();
    System.out.println(date);

LDAP字符串代表日期,时间和UTC偏移量.好的解决方案是尊重这一点,并在格式化(将一天中的时间设置为00:00并将偏移量设置为0)并解析回所有内容时生成所有这些内容(最多也可以验证它们,以防出现任何意外).当您知道如何时,LocalDate和OffsetDateTime之间的转换非常简单.

编辑3:允许配置模式

… the pattern is configured in a property file… I want to configure 1
pattern only in this property file.

… I have no guarantee that the format cannot change.

要考虑到模式有一天可能不包含一天中的时间和/或没有UTC偏移的可能性,请在上面的代码中使用以下格式化程序:

    DateTimeFormatter birthdateFormat = new DateTimeFormatterBuilder()
            .appendPattern(pattern)
            .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
            .toFormatter()
            .withZone(ZoneOffset.UTC);

这定义了默认时间(午夜)和默认偏移量(0).只要在LDAP的字符串中定义了时间和偏移量,就不会使用默认值.

如果您觉得它太复杂了,那么使用两种配置的格式(一种用于格式化,另一种用于解析)可能是最适合您的解决方案(最讨厌的解决方案).

编辑:避免类型转换

我认为以上是不错的解决方案.但是,如果您坚持要避免使用atStartOfDay从LocalDate到ZonedDateTime的转换,以及使用toLocalDate从OffsetDateTime的转换,则可以通过以下技巧实现:

    DateTimeFormatter birthdateFormat = new DateTimeFormatterBuilder()
            .appendValue(ChronoField.YEAR, 4, 4, SignStyle.NEVER)
            .appendValue(ChronoField.MONTH_OF_YEAR, 2, 2, SignStyle.NEVER)
            .appendValue(ChronoField.DAY_OF_MONTH, 2, 2, SignStyle.NEVER)
            .appendLiteral("000000+0000")
            .toFormatter();

    // Outputs 20181227000000+0000
    String formatted = date.format(birthdateFormat);
    System.out.println(formatted);

    // Parses into 2018-12-27
    date = LocalDate.parse("20181227000000+0000", birthdateFormat);
    System.out.println(date);

我正在指定每个字段的确切宽度,以便格式化程序可以在解析时知道在字符串中将其分隔的位置.

编辑2:这是解析错误吗?

我会立即期望yyyyMMdd’000000 0000’同时用于格式化和解析.您可以尝试向Oracle提交错误,然后看看他们怎么说,尽管我不会太乐观.

标签:java-8,datetime-format,datetime,java,date
来源: https://codeday.me/bug/20191024/1923756.html