在Java中将纪元转换为ZonedDateTime

时间:2017-12-26 07:17:46

标签: java epoch datetime-conversion zoneddatetime

如何在java中将1413225446.92000之类的纪元转换为ZonedDateTime

给出的代码需要 long 值,因此这将为上面给出的值抛出NumberFormatException

ZonedDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(dateInMillis)), ZoneId.of(TIME_ZONE_PST));

3 个答案:

答案 0 :(得分:1)

将数字拆分为一对64位长整数:

  • 自UTC 1970年第一时刻的纪元参考日期以来的整秒数
  • 小数秒的数个纳秒

将这些数字传递给工厂方法Instant.ofEpochSecond​(long epochSecond, long nanoAdjustment)

手持Instant后,继续指定时区以获取ZonedDateTime

ZoneId z = ZoneId.of( "America/Los_Angeles" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;

答案 1 :(得分:1)

时间戳的特殊情况下(即在纪元之前),在此处扩展Basil和Ole的答案。那有可能吗?这是Jon Skeet在“关于java.util.Date的全部”中写的内容:

  

Date类使用“自Unix时代以来的毫秒数”,即   由getTime()返回的值,并由Date(long)设置   构造函数或setTime()方法。当月亮走过之前   Unix时期,该值为负:实际上是-14159020000。

Ole的答案之间唯一真正的区别(除了一些额外的断言)是,在这里,如果日期字符串以负号开头,我们不会反转nanos上的符号。原因是,当将nanos传递到Instant constructor时,这是 adjustment ,因此,如果我们将nanos发送为负数,则实际上会将秒数调整回来,因此整个ZonedDateTime值都偏离了纳秒级。

这来自JavaDoc,请注意有趣的行为:

  

此方法允许传递任意数量的纳秒。   工厂将更改秒和纳秒的值   为了确保存储的纳秒在0到   999,999,999。例如,以下结果将导致   同一时刻:

     

EpochSecond(3,1);
    Instant.ofEpochSecond(4,-999_999_999);
    Instant.ofEpochSecond(2,1000_000_001);

因此,第二个参数nanos,我们没有设置值,而是调整。因此,与正向时间戳记(时期之后)相同,我们要发送实际的nanos。

以Ole的代码为基础并添加上述更改:

    String strDateZoned = "Jul 20 1969 21:56:20.599 CDT"; // yes, should use America/Chicago here as Ole points out
    DateTimeFormatter dtfFormatter  = DateTimeFormatter.ofPattern("MMM dd yyyy HH:mm:ss.SSS zzz");
    ZonedDateTime     originalZoned  = ZonedDateTime.parse(strDateZoned, dtfFormatter);

    long epochSeconds = originalZoned.toInstant().getEpochSecond();
    int nanoSeconds = originalZoned.toInstant().getNano();
    String dateInMillis = epochSeconds + "." + nanoSeconds;
    String[] secondsAndFraction = dateInMillis.split("\\.");
    int nanos = 0;
    if (secondsAndFraction.length > 1) { // there’s a fractional part
        // extend fractional part to 9 digits to obtain nanoseconds
        String nanosecondsString
                = (secondsAndFraction[1] + "000000000").substring(0, 9);
        nanos = Integer.parseInt(nanosecondsString);
    }

    ZonedDateTime zdt = Instant
            .ofEpochSecond(Long.parseLong(secondsAndFraction[0]), nanos)
            .atZone(ZoneId.of("America/Chicago"));

    String formattedZdt = dtfFormatter.format(zdt);

    System.out.println("zoneDateTime expected    = " + strDateZoned);
    System.out.println("zoneDateTime from millis = " + formattedZdt);

    assertEquals("date in millis is wrong",  "-14159020.599000000", dateInMillis);
    assertEquals("date doesn't match expected",strDateZoned, dtfFormatter.format(zdt));

代码输出:

zoneDateTime expected    = Jul 20 1969 21:56:20.599 CDT
zoneDateTime from millis = Jul 20 1969 21:56:20.599 CDT

如果在秒部分为负的情况下反转nanos上的符号,则可以看到格式化的ZonedDateTime中的差异:

org.junit.ComparisonFailure: date doesn't match expected 
Expected :Jul 20 1969 21:56:20.599 CDT
Actual   :Jul 20 1969 21:56:19.401 CDT

P.S。乔恩·斯凯特(Jon Skeet)所说的“宽大处理”在“关于日期的所有内容”一文中还有一些想法,在其他地方我也看到了“规范化”,这可能是由于POSIX的影响:

  

宽容没有明显的理由:“在所有情况下,   用于这些目的的方法不必落在指定的范围内;   例如,日期可以指定为1月32日并被解释为   意思是2月1日。”多久有用一次?

答案 2 :(得分:0)

Basil Bourque’s answer是个好人。从小数部分取纳纳秒到纳秒的整数可能会导致一两个陷阱。我建议:

    String dateInMillis = "1413225446.92000";
    String[] secondsAndFraction = dateInMillis.split("\\.");
    int nanos = 0;
    if (secondsAndFraction.length > 1) { // there’s a fractional part
        // extend fractional part to 9 digits to obtain nanoseconds
        String nanosecondsString
                = (secondsAndFraction[1] + "000000000").substring(0, 9);
        nanos = Integer.parseInt(nanosecondsString);
        // if the double number was negative, the nanos must be too
        if (dateInMillis.startsWith("-")) {
            nanos = -nanos;
        } 
    }
    ZonedDateTime zdt = Instant
            .ofEpochSecond(Long.parseLong(secondsAndFraction[0]), nanos)
            .atZone(ZoneId.of("Asia/Manila"));
    System.out.println(zdt);

打印

2014-10-14T02:37:26.920+08:00[Asia/Manila]

纳秒时我们不需要64位,所以我只使用int

如果有一天您的纪元时间以科学记数法出现(1.41322544692E9),则上述情况将不起作用。

如果碰巧不是亚洲/马尼拉,请用地区/城市格式替换您想要的时区,例如America / Vancouver,America / Los_Angeles或Pacific / Pitcairn。避免像PST这样的三个字母缩写,它们含糊不清,通常不是真正的时区。