简体   繁体   English

java 8 日期/时间 API:使用通用日期/时间格式解析以提取自纪元以来的毫秒数

[英]java 8 date/time API: parsing with general date/time format to extract milliseconds since epoch

Given any date/time format (passed by the user), I need to parse the date with it and return milliseconds since java epoch, something that could be done with the following code using old date API:给定任何日期/时间格式(由用户传递),我需要用它解析日期并返回自 java 纪元以来的毫秒数,这可以使用旧日期 API 使用以下代码完成:

//dateFormatter is SimpleDateFormat
Date d = dateFormatter.parse(value);
return d.getTime();

The only requirement for the format that it will contain the date part, for example all of the following are possible formats:对包含日期部分的格式的唯一要求,例如以下所有可能的格式:

"dd/MM/yyyy"
"dd/MM/yyyy HH:mm X" //with timezone

The missing part are completed with start of the day/local time zone.缺少的部分以当天/当地时区的开始完成。 So I came up with the following, which seems to be much more verbose and not sure if there is a more efficient way to do it:所以我想出了以下内容,这似乎更加冗长,并且不确定是否有更有效的方法来做到这一点:

        //dateFormatter is DateTimeFormatter
        TemporalAccessor ta = dateFormatter.parse(value);
        
        LocalDate ld = LocalDate.from(ta); //assume must have date part

        LocalTime lt = ta.query(TemporalQueries.localTime());
        if (lt == null) {
            lt = LocalTime.MIN;
        }
        
        ZoneId zoneId = ta.query(TemporalQueries.zone());
        if (zoneId == null) {
            zoneId = ZoneId.systemDefault();
        }
        
        Instant d = LocalDateTime.of(ld, lt).atZone(zoneId).toInstant();
        return d.toEpochMilli();

Use DateTimeFormatterBuilder.parseDefaulting to set default values for when that field is not found in the format string:当在格式字符串中找不到该字段时,使用DateTimeFormatterBuilder.parseDefaulting设置默认值:

DateTimeFormatter formatter = new DateTimeFormatterBuilder()
        .appendPattern(patternFromTheUser)
        // make everything non-required default to 0
        .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
        .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
        .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
        .parseDefaulting(ChronoField.OFFSET_SECONDS, 0)
        .toFormatter();
System.out.println(ZonedDateTime.parse("01/01/2021 20:00", formatter)
        .toInstant()
        .toEpochMilli());

As also shown in answer by Sweeper , you can specify default values in the formatter.正如Sweeper 的回答中所示,您可以在格式化程序中指定默认值。

Zone ID is handled differently, where it is not just a default, it is also a kind of override, and has to be defined using withZone(ZoneId zone) after building the formatter.区域 ID 的处理方式不同,它不仅是默认值,也是一种覆盖,必须在构建格式化程序使用withZone(ZoneId zone)定义。

If the format and value specifies a Zone ID, then it is used, all good.如果格式和值指定了区域 ID,则使用它,一切都很好。 If it doesn't specify Zone ID or Zone Offset, the given "default" is used.如果它没有指定区域 ID 或区域偏移,则使用给定的“默认值”。 However, if only Zone Offset is specified, it is used for parsing, but then the "default" Zone ID overrides the result, adjusting the time and Zone Offset accordingly.但是,如果只指定了 Zone Offset,它会用于解析,但是“默认”Zone ID会覆盖结果,相应地调整时间和 Zone Offset。

Since you want epoch milliseconds, that doesn't actually affect this code, I just wanted to mention it for other users who might not do the final conversion to UTC.由于您需要纪元毫秒,这实际上不会影响此代码,我只是想为可能不会最终转换为 UTC 的其他用户提及它。

The code should be:代码应该是:

public static long parseToEpochMillis(String format, String dateText) {
    return new DateTimeFormatterBuilder()
            .appendPattern(format)
            .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
            .toFormatter(Locale.US) // So e.g. MMM parses "Feb", not some other language
            .withZone(ZoneId.systemDefault())
            .parse(dateText, ZonedDateTime::from)
            .toInstant()
            .toEpochMilli();
}

Test测试

public static void main(String[] args) {
    test("M/d/u", "7/4/2015");
    test("M/d/u H:mm:ss VV", "7/4/2015 1:23:45 America/Denver");
    test("M/d/u H:mm:ssXXXXX", "7/4/2015 1:23:45-08:00");
    test("M/d/u H:mm:ssX", "7/4/2015 1:23:45Z");
}
static void test(String format, String dateText) {
    DateTimeFormatter formatter = new DateTimeFormatterBuilder()
            .appendPattern(format)
            .parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
            .toFormatter(Locale.US)
            .withZone(ZoneId.systemDefault());
    ZonedDateTime dateTime = ZonedDateTime.parse(dateText, formatter);
    Instant instant = dateTime.toInstant();
    long epochMilli = instant.toEpochMilli();
    System.out.printf("%-45s -> %s -> %d%n", dateTime, instant, epochMilli);
}

Output Output

2015-07-04T00:00-04:00[America/New_York]      -> 2015-07-04T04:00:00Z -> 1435982400000
2015-07-04T01:23:45-06:00[America/Denver]     -> 2015-07-04T07:23:45Z -> 1435994625000
2015-07-04T05:23:45-04:00[America/New_York]   -> 2015-07-04T09:23:45Z -> 1436001825000
2015-07-03T21:23:45-04:00[America/New_York]   -> 2015-07-04T01:23:45Z -> 1435973025000

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM