使用 Java 将 unix 纪元转换为人类可读时的日期不正确

Posted

技术标签:

【中文标题】使用 Java 将 unix 纪元转换为人类可读时的日期不正确【英文标题】:Incorrect date when converting unix epoch to human readable using Java 【发布时间】:2014-10-19 05:35:05 【问题描述】:

编辑:删除了“*1000”,但日期仍然不正确,但更新了下面的日志以显示我现在得到的内容。

下面是我的代码 sn-p 和我的日志,我认为我正确地实现了它,所以我不知道为什么它没有给我正确的转换:

NewFoodItem foodItem = data.get(position);
String date = new java.text.SimpleDateFormat("MM/dd/yyyy HH:mm:ss").format(new java.util.Date (foodItem.date));
String a =  Integer.toString(foodItem.date);
Log.d("returnedDate:", a);
Log.d("formattedDate:", date);

它不允许我发布图片,但日志如下所示:

D/returnedDate:  1409012824
D/formattedDate: 01/17/1970 02:23:32
D/returnedDate:  1409013004
D/formattedDate: 01/17/1970 02:23:33

【问题讨论】:

为什么要乘以date*1000 @arielnmz 因为returnedDate 排在第二位,我猜。 是的,秒,不是毫秒 您需要 *1000 才能从秒转换为毫秒。但是所有的算术运算都需要用 longs 来完成,否则你会溢出 int 结果并得到一个奇怪的格式化日期。 【参考方案1】:

Answer by Andrew T. 是正确的:整数溢出。但是示例代码现在已经过时了。

java.time

现代方法使用 java.time 类。

Instant 类代表一个时刻。与java.util.Date 一样,Instant 从 UTC 1970 年第一刻的epoch reference 开始计算,1970-01-01T00:00Z。但是Instant 使用了纳秒而不是毫秒的更精细的分辨率。

L 附加到数字文字的末尾以指示long 类型。在您喜欢对数字进行分组的任何地方使用下划线,不添加任何含义(编译器忽略)。

long input = 1_409_012_824L ;
Instant instant = Instant.ofEpochSecond( input ) ;

以标准ISO 8601 格式生成值的文本表示。

String output = instant.toString() ;

2014-08-26T00:27:04Z

为了更灵活地生成文本,请转换为OffsetDateTime

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ;

使用DateTimeFormatter 自动定位输出。

Locale locale = Locale.US ;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.MEDIUM ).withLocale( locale ) ;
String output2 = odt.format( f ) ;

2014 年 8 月 26 日上午 12:27:04

看到这个code run live at IdeOne.com。



关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310。

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

您可以直接与您的数据库交换 java.time 对象。使用符合JDBC 4.2 或更高版本的JDBC driver。不需要字符串,不需要java.sql.* 类。

从哪里获得 java.time 类?

Java SE 8Java SE 9Java SE 10Java SE 11 和更高版本 - 具有捆绑实现的标准 Java API 的一部分。 Java 9 添加了一些小功能和修复。 Java SE 6Java SE 7 大部分 java.time 功能在ThreeTen-Backport 中向后移植到 Java 6 和 7。 Android java.time 类的 android 捆绑包实现的更高版本。 对于早期的 Android (ThreeTenABP 项目适应 ThreeTen-Backport(如上所述)。见How to use ThreeTenABP…

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuarter 和more。

【讨论】:

【参考方案2】:

我只是用一些假设进行了测试,似乎问题与整数溢出有关。

我假设您将NewFoodItem.date 定义为int,而不是long。因此,当您乘以 date * 1000(两者都是 int)时,它会返回 int

int d = 1409012824; // foodItem.date
int d1000 = d * 1000; // returns 263550912 because of overflow, instead of 1409012824000

String date = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss")
    .format(new Date(d1000)); // returns 01/04/1970 08:42:30

当我尝试将其中一个更改为 long 时,它的行为符合预期

// case 1
long d = 1409012824;
long d1000 = d * 1000; // now returns 1409012824000 correctly

String date = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss")
    .format(new Date(d1000)); // returns 08/26/2014 08:27:04

// case 2
int d = 1409012824;
long d1000 = d * 1000L; // note the "L" suffix to indicate the number as long 
long d1000f = d * 1000; // FAIL, still returns 263550912 because of integer overflow

String date = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss")
    .format(new Date(d1000)); // returns 08/26/2014 08:27:04
String date = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss")
    .format(new Date(d1000f)); // returns 01/04/1970 08:42:30

通常在 Java 中使用 Date 时,我们将它们定义为 long,因为它们通常以毫秒为单位。为便于维护,最好将NewFoodItem.date的类型改为long;如果它也以毫秒为单位,那就更好了。

【讨论】:

完美就是问题所在。我知道我应该使用 long 但我必须转换为整数,因为我使用的是 SQLite 并且我没有意识到在转换回人类可读时 int 不起作用。

以上是关于使用 Java 将 unix 纪元转换为人类可读时的日期不正确的主要内容,如果未能解决你的问题,请参考以下文章

在 Mac OSX 上将 unix 纪元时间转换为人类可读的日期 - BSD

如何使用 sed 将纪元转换为人类可读的日期时间

如何将纳秒的纪元时间转换为人类可读的?

将纪元转换为人类可读的日期在 python 中不起作用

bash将csv尾部的纪元时间转换为人类可读

如何在 Teradata 中将纪元时间转换为人类可读