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

时间:2019-05-23 14:07:34

标签: java unix-timestamp datetime-conversion

我试图将19位Unix时间戳(例如1558439504711000000(一个quintillion一半)转换为可读的日期/时间格式。我的时间戳以6个零结尾,表示时间以纳秒为单位。

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

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

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

下面的代码提供了我最新的实现方法:

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格式。

3 个答案:

答案 0 :(得分:2)

tl; dr

请勿使用旧版类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早一小时。

避免使用旧的日期时间类

java.util.Date类很糟糕。连同其CalendarSimpleDateFormat之类的同窝仔,真是一团糟。避免他们。 Sun,Oracle和JCP社区在采用JSR 310时放弃了它们。

Instant

java.util.Date对象代表UTC中的时刻,分辨率为milliseconds。替换为java.time.Instant,也是UTC的片刻,但分辨率为nanoseconds。在内部,两者都跟踪自epoch referencefirst moment of 1970 in UTC开始的计数。

为避免处理巨大的数字,内部Instant跟踪自1970年以来的整秒数,加上小数秒保持为纳秒。两个单独的数字。这些就是您需要喂Instant.ofEpochSecond的地方。

使用Long类将输入字符串解析为long。顺便说一下,请注意,您的值正在逼近the limit整数的64-bit

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 ) ;
  

我的时间戳以6个零结尾,这表明时间以纳秒为单位。

实际上,纳秒的计数高达十亿,因此九(9)个数字而不是六(6)。从纪元开始计数的秒数是711000000,即711,000,000纳米。您的总秒数为1558439504,即1,558,439,504(十亿分之一)。用小数点表示:

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

时区

  

我遇到了一些例子,其中人们使用了我不需要的时区。

要表示时刻,是时间轴上的特定点,您总是需要time zone (或offset-from-UTC小时-分钟,秒)。

要查看特定区域(时区)的人们在墙上时钟所用的同一时刻,请应用ZoneId以获得ZonedDateTime

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

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 [欧洲/伦敦]

请注意在一天中的调整时间,从11点到12点。这很有意义,因为Europe/London区域比该日期的UTC早一个小时。同一时刻,时间轴上的同一点,不同的时钟时间。

快捷方式

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

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 [欧洲/伦敦]

或者让 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

提示:在提供日期时间时要非常小心,不要显式指定区域。这会产生歧义,用户可能会假设正在玩一个不同的区域/偏移。

答案 1 :(得分:0)

我认为这没什么不对,您正在处理一个表示FUTURE(将来很远的日期)中的日期的时间戳。

如果您考虑这一点:

String timeStamp = "1558439504";

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

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

Date myDate = Date.from(instant);

答案 2 :(得分:0)

尝试使用此

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

除以 1000000

,而不是乘以 1000