Java将日期转换为纪元值会产生错误的输出

Posted

技术标签:

【中文标题】Java将日期转换为纪元值会产生错误的输出【英文标题】:Java Converting date to epoch value produces false output 【发布时间】:2014-10-06 06:42:36 【问题描述】:

我正在尝试将以下日期时间字符串对象转换为 unix 纪元时间戳值。但是,当我运行该程序时,我注意到它生成了一个 1404461110000 的纪元值,当我在我的 ubuntu unix 机器上检查时,它是 8 月 7 日星期三 18:06:40 但实际上我正在通过 2014 年 7 月 4 日 04 年 7 月 4 日: 05:10。我的 ubuntu 机器上有一个美国/多伦多时区,但我认为这不重要吗?

Java 代码:

            long epoch = 0;
            String str = "2014-07-04 04:05:10";   // UTC

            DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            Date datenew = df.parse(str);
            epoch = datenew.getTime();

            System.out.println(epoch); // prints 1404461110000

Ubuntu Linux 14.04

date -d@1404461110000

显示= 8 月 7 日星期三 18:06:40 EDT 46475

【问题讨论】:

您没有考虑时区,Date 将在打印文本时应用... 【参考方案1】:

问题是您在 Java 中调用 getTime() 时没有处理 unix timestamp。 Unix 时间戳表示自纪元以来的 ,而您从 Java 获得的值是自 epoch(1970 年 1 月 1 日)以来的 毫秒,因此存在差异。

必须是:

epoch = datenew.getTime() / 1000;

这应该让你至少更接近 10000 年。如果在那之后您仍然看到差异,则它与时区有关,可以通过在 DateFormat 实例上指定时区来适应。

【讨论】:

或者更近 44461 年 ;-) @ScaryWombat 是否考虑了时区的值? ;-) 根据 OP displays= Wed Aug 7 18:06:40 EDT 46475 我从来没有意识到这一点,非常感谢,我现在离 44461 更近了 :-)【参考方案2】:

java.time

旧的日期时间 API(java.util 日期时间类型及其格式类型,SimpleDateFormat 等)已过时且容易出错。建议完全停止使用,改用java.time,modern date-time API*

使用现代 API java.time 的解决方案:

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.concurrent.TimeUnit;

public class Main 
    public static void main(String[] args) 
        String str = "2014-07-04 04:05:10"; // UTC

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("u-M-d H:m:s", Locale.ENGLISH);
        LocalDateTime ldt = LocalDateTime.parse(str, dtf);
        ZonedDateTime zdtUtc = ldt.atZone(ZoneId.of("Etc/UTC"));
        Instant instant = zdtUtc.toInstant();
        long epochMillis = instant.toEpochMilli();
        long epochSeconds = TimeUnit.SECONDS.convert(epochMillis, TimeUnit.MILLISECONDS);
        System.out.println(epochSeconds);

        // Corresponding date-time in America/Toronto
        ZonedDateTime zdtToronto = instant.atZone(ZoneId.of("America/Toronto"));
        System.out.println(zdtToronto);
    

输出:

1404446710
2014-07-04T00:05:10-04:00[America/Toronto]

Trail: Date Time了解更多关于java.timemodern date-time API*的信息。

一些重要说明:

    Unix time 指定自纪元以来的秒数。 如果有可用的 OOTB(即开即用)API,例如,我们应该避免自己执行计算。 TimeUnit#convert

* 出于任何原因,如果您必须坚持使用 Java 6 或 Java 7,您可以使用 ThreeTen-Backport 将大部分 java.time 功能反向移植到 Java 6 和 7 . 如果您正在为一个 android 项目工作并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaring 和 How to use ThreeTenABP in Android Project。

【讨论】:

【参考方案3】:

您需要告诉 Java 时间戳是 UTC,而不仅仅是添加注释。

long epoch = 0;
String str = "2014-07-04 04:05:10";   // UTC

DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
df.setTimeZone(TimeZone.getTimeZone("UTC")); // assume UTC
Date datenew = df.parse(str);
epoch = datenew.getTime();

System.out.println(epoch);

【讨论】:

是的,那会让Wed Aug 7 18:06:40 EDT 46475变成2014不会吗?【参考方案4】:

乔达时间

仅供参考,这是相同类型的代码,但在 Joda-Time 2.4 中完成。 Java 8 中的新 java.time package(受 Joda-Time 启发)可以以类似的方式使用。

java.util.Date 和 .Calendar 类是出了名的麻烦。避开他们。使用上面提到的两个库中的任何一个。

示例代码

您的输入字符串接近标准ISO 8601 格式,但不完全是,缺少中间的T 和与UTC 的偏移量。 Joda-Time 有一个用于在 ISO 8601 中解析/生成的内置格式化程序。因此,与其定义我们自己的格式化程序,让我们稍微调整一下输入字符串,插入 T

String inputRaw = "2014-07-04 04:05:10";   // UTC
String input = inputRaw.replace( " ", "T" ); // Convert to standard ISO 8601 format.

然后我们将告诉 DateTime 构造函数将字符串解析为 UTC(零偏移)。与 java.util.Date 不同,DateTime 知道自己分配的时区。

DateTime dateTimeUtc = new DateTime( input, DateTimeZone.UTC );

轻松适应多伦多时间、娱乐或调试/健全性检查。

DateTime dateTimeToronto = dateTimeUtc.withZone( DateTimeZone.forID( "America/Toronto" ) );

与 java.util.Date 一样,Joda-Time 在内部以milliseconds 的数量跟踪时间,自 UTC 的 Unix 纪元(1970 年开始)以来。这意味着使用 64 位 long 而不是通常的 32 位 int。 (顺便说一句,java.time 跟踪 nanoseconds。)所以如果我们需要 seconds-since-unix-epoch,除以一千。

long millisecondsSinceEpoch = dateTimeUtc.getMillis();  // Use a "long", not "int".
long secondsSinceEpoch = ( millisecondsSinceEpoch / 1000L );

转储到控制台。

System.out.println( "input: " + input );
System.out.println( "dateTimeUtc: " + dateTimeUtc );
System.out.println( "dateTimeToronto: " + dateTimeToronto );
System.out.println( "millisecondsSinceEpoch: " + millisecondsSinceEpoch );
System.out.println( "secondsSinceEpoch: " + secondsSinceEpoch );

运行时。

input: 2014-07-04T04:05:10
dateTimeUtc: 2014-07-04T04:05:10.000Z
dateTimeToronto: 2014-07-04T00:05:10.000-04:00
millisecondsSinceEpoch: 1404446710000
secondsSinceEpoch: 1404446710

【讨论】:

以上是关于Java将日期转换为纪元值会产生错误的输出的主要内容,如果未能解决你的问题,请参考以下文章

如何将日期时间(在特定时区)转换为纪元秒

如何将(round)纪元时间转换为最接近的小时和日期wrt IST(在Java中)

将 JSON 纪元时间转换为本地人类可读日期

如何在python中将日期转换为纪元时间[重复]

如何使用 Java 将 unix 纪元的列转换为 Apache spark DataFrame 中的日期?

如何将 df 中的列转换为具有日期时间格式的纪元,反之亦然?