Java Converting 19-digit Unix Timestamp to a Readable Date

后端 未结 3 2097
旧时难觅i
旧时难觅i 2021-01-26 11:26

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 z

3条回答
  •  无人及你
    2021-01-26 12:19

    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 V.V. 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.

提交回复
热议问题