从Joda时间库迁移到Java时间(Java 8)

时间:2015-05-05 20:44:49

标签: java-8 jodatime

我正在尝试从Joda时间库迁移到Java时间(Java 8)。 我无法在ISODateTimeFormat.dateOptionalTimeParser()

中找到相当于java.time的内容

Joda ISO格式化程序有很好的解析器:

ISODateTimeFormat.dateTimeParser():generic - 根据解析的字符串选择解析器。 同理: ISODateTimeFormat.dateOptionalTimeParser()

我发现很难将Joda时间改为java.time。 有人可以指导我吗?

示例:

String dateTimeString = "2015-01-01T12:29:22+00:00"; 
String dateTimeString2 = "2015-01-01T12:29:22";

当我使用joda时间解析此字符串时

ISODateTimeFormat.dateTimeParser().withZone("EST")

可以解决这两个问题。这在java时间相当于这个?

使用java 8,带有ISO_Zoned_date_time的ZonedDateTime无法同时处理这两种情况。

2 个答案:

答案 0 :(得分:9)

您不能使用预定义的格式化程序,但您可以使用以下模式构建自己的格式化程序(并将其分配给静态常量):

static final DateTimeFormatter DATE_TIME_OPTIONAL_OFFSET =
    DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss[xxx]");

注意:如果您解析的输入只包含日期和时间但没有偏移(并且没有任何偏移/区域默认值),那么结果只能是LocalDateTime,而不是全球时间戳。

请注意方法withZone(...)的不同行为。

<强>约达时间

When parsing, this zone will be set on the parsed datetime.     
A null zone means of no-override. If both an override chronology
and an override zone are set, the override zone will take precedence
over the zone in the chronology.

Java-8(JSR-310)

When parsing, there are two distinct cases to consider.
If a zone has been parsed directly from the text, perhaps because 
DateTimeFormatterBuilder.appendZoneId() was used, then this override Zone
has no effect. If no zone has been parsed, then this override zone will
be included in the result of the parse where it can be used to build
instants and date-times.

旁注:Joda-Time-method withOffsetParsed()更接近Java-8行为。

更新:我现在已经完成了自己的测试。看看有时令人惊讶的结果。

System.out.println(System.getProperty("java.version")); // 1.8.0_31

// parsing s1 with offset = UTC
String s1 = "2015-01-01T12:29:22+00:00"; 

OffsetDateTime odt1 = DATE_TIME_OPTIONAL_OFFSET.parse(s1, OffsetDateTime::from);
System.out.println(odt1); // 2015-01-01T12:29:22Z --- OK

LocalDateTime ldt1 = DATE_TIME_OPTIONAL_OFFSET.parse(s1, LocalDateTime::from);
System.out.println(ldt1); // 2015-01-01T12:29:22 --- OK

ZonedDateTime zdt1 = DATE_TIME_OPTIONAL_OFFSET.withZone(ZoneId.of("America/New_York")).parse(s1, ZonedDateTime::from);
System.out.println(zdt1); // 2015-01-01T12:29:22-05:00[America/New_York] --- seems to be a bug compared with the spec above, the parsed offset was overridden!!!

// now parsing s2 without offset
String s2 = "2015-01-01T12:29:22";

OffsetDateTime odt2 = DATE_TIME_OPTIONAL_OFFSET.parse(s2, OffsetDateTime::from);
System.out.println(odt2); // 2015-01-01T12:29:22Z --- questionable, the offset Z is invented/guessed here

LocalDateTime ldt2 = DATE_TIME_OPTIONAL_OFFSET.parse(s2, LocalDateTime::from);
System.out.println(ldt2); // 2015-01-01T12:29:22 --- OK

DATE_TIME_OPTIONAL_OFFSET.withZone(ZoneId.of("America/New_York")).parse(s2, ZonedDateTime::from);
// throws an exception --- seems to be a bug compared with the spec above, the zone set was not accepted

<强>结论:

迁移时我会小心。细节决定成败。也许更新的Java版8u40同时纠正了显示的一些问题(至少withZone()的行为可能已经纠正 - 请参阅JDK-issue 8033662,但是对于8u31,后端修复似乎丢失了?!) 。你还应该注意你的&#34;时区&#34;标记为&#34; EST&#34;取而代之的是&#34; America / New_York&#34;在我的测试中因为&#34; EST&#34;不是公认的时区ID(它在美国是一个本地化的时区名称缩写)。

更新 - 最终解决方案

经过额外的测试后,这段代码似乎适用于Java 8u31(假设输入中缺少偏移量时默认为UTC):

static final DateTimeFormatter DATE_TIME_OPTIONAL_OFFSET =
    DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ss[xxx]");      
OffsetDateTime odt = 
  DATE_TIME_OPTIONAL_OFFSET.withZone(ZoneOffset.UTC).parse(input, OffsetDateTime::from);
ZonedDateTime zdt = odt.toZonedDateTime(); // containing a fixed offset

答案 1 :(得分:0)

我在尝试将ISODateTimeFormat.dateTimeParser()。parseDateTime(&#34; ...&#34;)转换为基于Java 8 java.time工具的等效项时遇到了类似的问题。 最后,我无法使用DateTimeFormatter重现Joda-Time的ISODateTimeFormat的行为,而是选择了基于正则表达式的方法:

private static final Pattern ISO_8601_PARSE = Pattern.compile(
        "(?<year>\\d{1,4})-(?<month>\\d{1,2})-(?<day>\\d{1,2})"
        + "(T((?<hour>\\d{1,2})(\\:(?<minute>\\d{1,2})(\\:(?<second>\\d{1,2})(\\.(?<millis>\\d{1,3}))?Z?)?)?)?)?");

public static Date parseIso8601Date(String date) throws IllegalArgumentException {
    Matcher matcher = ISO_8601_PARSE.matcher(date);
    if (matcher.matches()) {
        try {
            String day = matcher.group("day");
            String month = matcher.group("month");
            String year = matcher.group("year");
            String hour = matcher.group("hour");
            String minute = matcher.group("minute");
            String second = matcher.group("second");
            String millis = matcher.group("millis");
            return Date.from(ZonedDateTime.of(
                    Integer.valueOf(year),
                    Integer.valueOf(month),
                    Integer.valueOf(day),
                    hasText(hour) ? Integer.valueOf(hour) : 0,
                    hasText(minute) ? Integer.valueOf(minute) : 0,
                    hasText(second) ? Integer.valueOf(second) : 0,
                    (hasText(millis) ? Integer.valueOf(millis) : 0) * 1000000, // nanoOfSecond
                    ZoneOffset.UTC).toInstant());
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("Failed to parse [" + date + "]: " + e, e);
        }
    } else {
        throw new IllegalArgumentException("Failed to parse [" + date + "]; does not match pattern yyyy-MM-ddThh:mm:ss[.SSS]Z");
    }
}

这还不是100%等效(即它不支持&#34; +00:00&#34;样式时区偏移,而是采用UTC),但它的宽度非常接近解析字符串。