Search code examples
javautcepochmillisecondslocaldate

How to convert Java LocalDate to milliseconds


I am trying to convert a date formatted in yyyy-mm-dd to LocalDate to milliseconds with this code.

LocalDate.parse("2022-08-01", DateTimeFormatter.ofPattern("yyyy-MM-dd"))
            .atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli()

It returns 1659283200000 which is not correct and behind 1 day. Is there better solution for this? The expected value is 1659312000000 in UTC.


Solution

  • Your code is correct, if you want a count of milliseconds since the epoch reference of first moment of 1970 as seen with an offset of zero hours-minutes-seconds from UTC, 1970-01-01T00:00Z.

    By the way, no need to specify a formatting pattern. Your input text complies with the ISO 8601 standard used by default in the java.time classes for parsing/generating text.

    long millis =
        LocalDate
        .parse(
            "2022-08-01"
        )
        .atStartOfDay(
            ZoneId.systemDefault()
        )
        .toInstant()
        .toEpochMilli()
    ;
    

    Time Zone

    I imagine the problem with getting an unexpected result is the time zone. Your results will vary by time zone.

    Understand that the day starts earlier in the east. At any moment, it can be “tomorrow” in Tokyo Japan 🇯🇵 while still “yesterday” in Edmonton Alberta Canada 🇨🇦 .

    enter image description here

    (photo: NASA)

    👉 To avoid surprises, specify your desired/expected time zone explicitly rather than relying on the JVM’s current default time zone.

    Example

    Same date used in examples below, for two time zones and one offset.

    String input = "2022-08-01" ;
    

    Tokyo

    ZoneId zoneIdTokyo = ZoneId.of( "Asia/Tokyo" ) ;
    
    long millisTokyo =
        LocalDate
        .parse(
           input
        )
        .atStartOfDay(
            zoneIdTokyo
        )
        .toInstant()
        .toEpochMilli()
    ;
    

    Edmonton

    ZoneId zoneIdEdmonton = ZoneId.of( "America/Edmonton" ) ;
    
    long millisEdmonton =
        LocalDate
        .parse(
           input
        )
        .atStartOfDay(
            zoneIdEdmonton
        )
        .toInstant()
        .toEpochMilli()
    ;
    

    UTC (offset of zero)

    ZoneOffset offsetUtc = ZoneOffset.UTC ;
    
    long millisUtc =
        LocalDate
        .parse(
           input
        )
        .atStartOfDay(
            offsetUtc
        )
        .toInstant()
        .toEpochMilli()
    ;
    

    Dump to console.

    System.out.println( "Tokyo: " + millisTokyo ) ;
    System.out.println( "Edmonton: " + millisEdmonton ) ;
    System.out.println( "UTC: " + millisUtc ) ;
    

    See this code run live at Ideone.com.

    Tokyo: 1659279600000

    Edmonton: 1659333600000

    UTC: 1659312000000

    Interval

    You said:

    The input date String should represent the current date and past 30 days which should be based on user's timezone. Example is 2022-07-08 to 2022-08-08

    I suggest adding the ThreeTen-Extra library to your project. Doing so gives you access to the Interval class to represent a span of time as a pair of Instant objects.

    Interval intervalOfPrior30DaysInUtc ( LocalDate end ) {
        return 
            Interval
            .of(
                end.minusDays( 30 ).atStartOfDay( ZoneOffset.UTC ).toInstant() ,
                end.atStartOfDay( ZoneOffset.UTC ).toInstant() 
            )
        ;
    }