migrate from Joda time library to Java time(Java 8)

筅森魡賤 提交于 2019-12-21 17:31:34

问题


I am trying to migrate from Joda time library to Java time (Java 8). I am unable to find equivalent of ISODateTimeFormat.dateOptionalTimeParser() in java.time

Joda ISO formatter has nice parsers:

ISODateTimeFormat.dateTimeParser() : generic - selects parser based on string parsed. Similarly: ISODateTimeFormat.dateOptionalTimeParser().

I am finding it difficult to change Joda time to java.time. Can some one guide me?

example:

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

When I parse this string using joda time then

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

can handle both without as problem. Which is equivalent of this in java time?

Using java 8, ZonedDateTime with ISO_Zoned_date_time is not able to handle both.


回答1:


You cannot use a predefined formatter but you can construct your own one (and assign it to a static constant) using following pattern:

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

Attention: If you parse an input containing only date and time but without offset (and without any offset/zone-default) then the result can only be a LocalDateTime, not a global timestamp.

Please also note the different behaviour of method withZone(...).

Joda-Time

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.

Side remark: The Joda-Time-method withOffsetParsed() is closer to Java-8-behaviour.

Update: I have now done my own tests. See the sometimes surprising results.

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

Conclusion:

I would be careful when migrating. The devil is in the details. Maybe a newer Java-version 8u40 has meanwhile corrected some of the problems shown (at least the behaviour of withZone() is probably corrected - see JDK-issue 8033662, but for 8u31 the backport fix appears to be missing?!). You should also note that your "timezone" labelled "EST" was replaced by "America/New_York" in my tests because "EST" is not a recognized timezone id (it is rather a localized timezone name abbreviation in US).

Update - final solution

After extra testing this code seems to work in Java 8u31 (assuming UTC as default in case of missing offset in input):

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



回答2:


I struggled with a similar problem trying to convert ISODateTimeFormat.dateTimeParser().parseDateTime("...") to an equivalent based on the Java 8 java.time facilities. In the end I failed to reproduce the behavior of Joda-Time's ISODateTimeFormat using a DateTimeFormatter and instead opted for a regexp based approach:

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");
    }
}

This is not 100% equivalent yet (i.e. it doesn't support "+00:00" style timezone offsets and instead assumes UTC), but it is quite close in how lenient it is when parsing the string.



来源:https://stackoverflow.com/questions/30062933/migrate-from-joda-time-library-to-java-timejava-8

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!