Java 将 19 位 Unix 时间戳转换为可读日期

Java Converting 19-digit Unix Timestamp to a Readable Date

我正在尝试将 19 位 Unix 时间戳(例如 1558439504711000000(一个半 quintillion)转换为可读的 date/time 格式。我的时间戳以 6 个零结尾,这表明时间以纳秒为单位。

我遇到过一些示例,其中人们使用了我不需要的时区。另一个例子像这样使用 ofEpochSecond:

Instant instant = Instant.ofEpochSecond(seconds, nanos);

但是我不确定是否需要使用ofEpochSecond。

下面的代码给出了我最近的实现方法:

String timeStamp = "1558439504711000000";
long unixNanoSeconds = Long.parseLong(timeStamp);
Date date = new java.util.Date(timeStamp*1000L); 
// My preferred date format
SimpleDateFormat sdf = new java.text.SimpleDateFormat("dd-MM-yyyy HH:mm:ss");                    
String formattedDate = sdf.format(date);
System.out.println("The timestamp in your preferred format is: " +  formattedDate); 

但是我得到的输出是这样的:

// The timestamp in your preferred format is: 11-12-49386951 11:43:20

其中不显示年份格式,例如2019 格式。

我认为这没有任何问题,您正在处理代表未来日期(未来非常遥远的日期)的时间戳。

如果你考虑这个:

String timeStamp = "1558439504";

这应该给你:05/21/2019 @ 11:51am (UTC)

然后我想有一个简单的方法来获取日期。只需先根据该时间戳创建 Instant,然后执行:

Date myDate = Date.from(instant);

试试这个

Date date = new java.util.Date(timeStamp/1000000); 

不是乘以 1000,而是除以 1000000

tl;博士

切勿使用旧版 class java.util.Date。相反,使用现代 java.time.Instant.

Instant                                  // The modern way to represent a moment in UTC with a resolution of nanoseconds. Supplants the terrible `java.util.Date` class.
.ofEpochSecond(                          // Parse a count since epoch reference of 1970-01-01T00:00:00Z.
    0L ,                                 // Passing zero for the count of whole seconds, to let the class determine this number from the 2nd argument.
    Long.parse( "1558439504711000000" )  // Count of nanoseconds since the epoch reference of 1970-01-01T00:00:00Z. 
)                                        // Returns a `Instant` object.
.atZone(                                 // Adjust from UTC to the wall-clock time used by the people of a specific region (a time zone).
    ZoneId.of( "Europe/London" ) 
)                                        // Returns a `ZonedDateTime` object. Same moment as the `Instant`, same point on the timeline, different wall-clock time.
.format(                                 // Generate text to communicate the value of the moment as seen through this time zone.
    DateTimeFormatter.ofPattern(         // Define how to format our generated text.
        "dd-MM-uuuu HH:mm:ss" ,          // Specify your desired formatting pattern.
        Locale.UK                        // Pass a `Locale` to be used in localizing, to (a) determine human language used in translating name of day-of-week and such, and (b) determine cultural norms to decide issues of capitalization, abbreviation, etc. Not really needed for this particular formatting pattern, but a good habit to specify `Locale`.
    )                                    // Returns a `DateTimeFormatter` object.
)                                        // Returns a `String` object containing our text.

21-05-2019 12:51:44

……或者……

Instant
.ofEpochSecond (
    TimeUnit.NANOSECONDS.toSeconds( 
       Long.parse( "1558439504711000000" ) 
    ) ,
    ( 1_558_439_504_711_000_000L % 1_000_000_000L )
)
.toString()

2019-05-21T11:51:44.711Z

请注意时差,因为时区比 UTC 早一小时。

避免遗留 date-time classes

java.util.Dateclass很糟糕。连同它的同胞,如 CalendarSimpleDateFormat,它们简直就是一团糟。避开他们。 Sun、Oracle 和 JCP 社区在采用 JSR 310 时放弃了它们。

Instant

一个 java.util.Date 对象表示 UTC 中的一个时刻,分辨率为 毫秒 。它的替换是 java.time.Instant,也是 UTC 中的一个时刻,但分辨率为 纳秒 。在内部,两者都在 UTC.

中跟踪自 1970 年第一个时刻的 纪元参考 以来的计数

为了避免处理巨大的数字,内部 Instant 跟踪自 1970 年以来的整秒数 加上 小数秒保持为纳秒数。两个不同的号码。这些是你需要喂养的 Instant.ofEpochSecond.

使用 Long class. By the way, notice that your value is pushing towards to the limit of a 64-bit 整数将您的输入字符串解析为 long

long totalNanos = Long.parse( "1558439504711000000" ) ;

使用 TimeUnit enum 计算整秒。

long secondsPortion = TimeUnit.NANOSECONDS.toSeconds( totalNanos ) ;

Modulo 减十亿,余数为小数秒的纳秒数。

long nanosPortion = ( totalNanos % 1_000_000_000L ) ;

实例化一个Instant

Instant instant = Instant.ofEpochSecond( secondsPortion , nanosPortion ) ;

My timestamp ends with 6 zeros which suggests the time is in nano seconds.

实际上,纳秒数可达十亿,因此九 (9) 位数字不是六 (6) 位。从纪元算起的小数秒是 711000000,即 711,000,000 纳米。您的整数秒数是 1558439504,即 1,558,439,504(1.5 亿)。作为小数:

1,558,439,504.711000000 秒,自 1970-01-01T00:00Z

时区

I have come across some examples where people have used time zones which I don't need.

要表示时刻,时间轴上的特定点,总是需要time zone (or offset-from-UTC of hours-minutes-seconds)。

要查看特定地区(时区)人们使用的 wall-clock 时间的同一时刻,请应用 ZoneId to get a ZonedDateTime

Continent/Region的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用 BSTESTIST 等 2-4 个字母的缩写,因为它们 不是 真实时区,未标准化,也不是甚至是独一无二的(!)。

ZoneId z = ZoneId.of( "Europe/London" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;  // Same moment, same point on the timeline, different wall-clock time.

2019-05-21T12:51:44.711+01:00[Europe/London]

请注意 time-of-day 中的调整,从 11 点到 12 点。这是有道理的,因为 Europe/London 时区比该日期的 UTC 早一个小时。同一时刻,时间轴上的同一点,不同的 wall-clock 时间。

快捷方式

作为 Ole V.V。在评论中指出,您可以跳过上面讨论的数学。将整个纳秒数作为 ofEpochSecond 的第二个参数。 class 在内部进行数学运算以将整秒与小数秒分开。

Instant instant = Instant.ofEpochSecond( 0L , 1_558_439_504_711_000_000L ) ;

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

生成文本

以标准 ISO 8601 格式生成表示 ZonedDateTime 值的文本,扩展为在方括号中附加时区名称。

String output = zdt.toString() ;

2019-05-21T12:51:44.711+01:00[Europe/London]

或者让java.time自动为您本地化。

Locale locale = Locale.UK;
DateTimeFormatter f = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.SHORT ).withLocale( locale );
String output = zdt.format( f );

21/05/2019, 12:51

或指定自定义格式。

Locale locale = Locale.UK;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd-MM-uuuu HH:mm:ss" , locale ) ;
String output = zdt.format( f );

21-05-2019 12:51:44

提示:在提供 date-time 而不明确指定区域时要非常小心。这会产生歧义,用户可能会假设使用不同的 zone/offset。