简体   繁体   中英

Java Converting 19-digit Unix Timestamp to a Readable Date

I am trying to convert 19 digit Unix timestamp such as 1558439504711000000 (one and a half quintillion ) into a readable date/time format. My timestamp ends with 6 zeros which suggests the time is in nano seconds.

I have come across some examples where people have used time zones which I don't need. Another example uses ofEpochSecond like so:

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

But I am not sure whether I need to use ofEpochSecond.

The code below gives my most recent approach of achieving this:

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

But the output I get is something like this:

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

Which does not show the year format in eg 2019 format.

tl;dr

Never use legacy class java.util.Date . Instead, use modern 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

…or…

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

Note the hour difference because the time zone is one hour ahead of UTC.

Avoid legacy date-time classes

The java.util.Date class is terrible . Along with its littermates such as Calendar & SimpleDateFormat , they amount to a awful mess. Avoid them. Sun, Oracle, and the JCP community gave up on them when they adopted JSR 310.

Instant

A java.util.Date object represents a moment in UTC , with a resolution of milliseconds . Its replacement is java.time.Instant , also a moment in UTC but with a resolution of nanoseconds . Internally, both track a count since the epoch reference of first moment of 1970 in UTC .

To avoid dealing with gigantic numbers, internally a Instant tracks a number of whole seconds since 1970 plus a fractional second kept as a number of nanoseconds. Two separate numbers. Those are what you need to feed Instant.ofEpochSecond .

Parse your input string as a long using the Long class. By the way, notice that your value is pushing towards to the limit of a 64-bit integer.

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

Use the TimeUnit enum to do the math of splitting out whole seconds.

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

Modulo by a billion, the remainder being the nanoseconds of the fractional second.

long nanosPortion = ( totalNanos % 1_000_000_000L ) ;

Instantiate an Instant .

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

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

Actually, nanoseconds count up to a billion, so nine (9) digits not six (6). The fractional second in your count from epoch is 711000000 , or 711,000,000 nanos. Your number of whole seconds is 1558439504 , or 1,558,439,504 (one and a half billion). As a decimal:

1,558,439,504.711000000 seconds since 1970-01-01T00:00Z

Time Zone

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

To represent a moment, a specific point on the timeline, you always need a time zone (or offset-from-UTC of hours-minutes-seconds).

To see that same moment through the wall-clock time used by the people of a particular region (a time zone), apply a ZoneId to get a ZonedDateTime .

Specify a proper time zone name in the format of Continent/Region , such as America/Montreal , Africa/Casablanca , or Pacific/Auckland . Never use the 2-4 letter abbreviation such as BST or EST or IST as they are not true time zones, not standardized, and not even unique(!).

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]

Notice the adjustment in the time-of-day, going from hour 11 to hour 12. This makes sense as Europe/London zone is an hour ahead of UTC on that date. Same moment, same point on the timeline, different wall-clock time.

Shortcut

As Ole VV noted in the comment, you could skip the math discussed above. Feed the entire number of nanoseconds as the second argument to ofEpochSecond . The class internally does the math to separate whole seconds from the fractional second.

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

See this code run live at IdeOne.com.

Generate text

Generate text representing the value of that ZonedDateTime in standard ISO 8601 format extended to append the name of the time zone in square brackets.

String output = zdt.toString() ;

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

Or let java.time automatically localize for you.

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

21/05/2019, 12:51

Or specify a custom format.

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

Tip: Be very careful about providing a date-time without specifying the zone explicitly. This creates ambiguity, where the user may assume a different zone/offset is in play.

I think there is nothing wrong with that, you are dealing with a timestamp that represent a date in the FUTURE (a really far away date in the future).

If you consider this:

String timeStamp = "1558439504";

this should give you: 05/21/2019 @ 11:51am (UTC)

Then there is I think an easy way to get the Date. Just create the Instant first based on that timestamp and then do:

Date myDate = Date.from(instant);

Try using this

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

Instead of multiplying by 1000 , divide by 1000000

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM