Java Convert UTC to PDT/PST with Java 8 time library

18,310

Solution 1

My program uses LocalDateTime and the value is always in UTC.

A LocalDateTime has no time zone at all, so it is not in UTC.

For a moment in UTC, use the Instant class. This represents a moment on the timeline in up to nanosecond resolution.

Instant now = Instant.now();

To adjust into a time zone, apply a ZoneId to get a ZonedDateTime.

Never use the 3-4 letter abbreviations like PST & PDT so commonly seen in the mainstream media. They are not real time zones, not standardized, and are not even unique(!). Use proper time zone names in continent/region format.

ZoneId zoneId = ZoneId.of( "America/Los_Angeles" );
ZonedDateTime zdt = instant.atZone( zoneId );

It sounds like your data sink has the poor design of taking an input of a string that represents a date-time value assumed to be in America/Los_Angeles time zone but lacking any indicator (no offset-from-UTC, no time zone).

To get such a string, lacking any offset or zone, use the predefined DateTimeFormatter named ISO_LOCAL_DATE_TIME. You will get a string in standard ISO 8601 format like this: 2011-12-03T10:15:30.

String output = zdt.format( DateTimeFormatter.ISO_LOCAL_DATE_TIME );

Your data sink omits the T from the middle, so replace with SPACE.

output = output.replace( `T` , " " );

If your data sink expects only whole seconds, you can truncate any fractional second from your date-time value.

zdt = zdt.truncatedTo( ChronoUnit.SECONDS );

Going the other direction, from string to object, define a formatter, parse as a LocalDateTime and apply the assumed time zone.

String input = "2011-12-03 10:15:30";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss" );
LocalDateTime ldt = LocalDateTime.parse( input , formatter );
ZoneId zoneId = ZoneId.of( "America/Los_Angeles" );
ZonedDateTime zdt = ldt.atZone( zoneId );

Solution 2

How about this?

ZoneId zid = ZoneId.of(ZoneId.SHORT_IDS.get("PST"));
return LocalDate.now(zid)

The SHORT_IDS is a documented way of getting the zoneId based on recognized TZ abbreviations. Documentation, FYR

Share:
18,310
Minjun Yu
Author by

Minjun Yu

Work solves all problems.

Updated on June 11, 2022

Comments

  • Minjun Yu
    Minjun Yu almost 2 years

    I would like to use built-in Java 8 time library to convert from UTC to PST/PDT.

    I am writing a program that talks to an API that returns a list of objects according to a time frame. E.g. objects created/modified from a certain date time.

    My program uses LocalDateTime and the value is always in UTC.
    The API accepts PST/PDT.
    "The time zone represented in all API responses is PST/PDT. Similarly, CompanyName asks that you make all time zone convertions and submit any dateTime requests in PST/PDT." -- quote from API documentation

    I think what they mean by PST/PDT is that the time value should reflect whether it is in day-light-saving or not.
    E.g. the time value changes twice a year.

    If that is the case, is there a practical/conventional way to convert from UTC to PST/PDT without manually figuring out if it is in day-light-saving time by looking at the exact date?

    UPDATE

    The API returns the objects in JSON format.
    My app send a request to their API with a query parameter api.com/objects?modified-date=${yyyy-MM-dd hh:mm:ss}

    I am currently using the following method to do the conversion:

    public static String toSsTimeStr(LocalDateTime utcTime){
        String pattern  = "yyyy-MM-dd HH:mm:ss";
    
        ZonedDateTime zdt = ZonedDateTime.ofInstant(utcTime, ZoneOffset.UTC, ZoneId.of("America/Los_Angeles"));
    
        String timeStr = zdt.format(DateTimeFormatter.ofPattern(pattern));
        return timeStr;
    }  
    

    Will this help adjust the day-light-saving time, in other words, return the correct value according to the current day-light-saving-time status for me?

    Thank you.

  • Minjun Yu
    Minjun Yu almost 8 years
    I am currently using the proper time zone names America/Los_Angeles" to convert the LocalDateTime object in UTC. If I remember right, today is in daylight saving time so it is 7 hours behind. When the daylight-saving-time is over, will the result still be correctly which is 8 hours behind?
  • Andreas
    Andreas almost 8 years
    instant.atZone(zoneId) seems more convenient than ZonedDateTime.ofInstant(instant, zoneId). --- Typo: ZoneId.of
  • Basil Bourque
    Basil Bourque almost 8 years
    @Andreas Yes, thanks, that is better, clearer. I never noticed that method before.
  • Andreas
    Andreas almost 8 years
    Note latest comment by OP: It is returned as JSON the format is the standard yyyy-MM-dd hh:mm:ss. So, not ISO.
  • Basil Bourque
    Basil Bourque almost 8 years
    @Minjun.Y The ZonedDateTime handles the Daylight Saving Time (DST) adjustments for you, that's it's job. LocalDateTime does not! Use LocalDateTime only when you have no time zone in mind at all, like "Christmas starts at midnight on the 25th of December this year".
  • Minjun Yu
    Minjun Yu almost 8 years
    @BasilBourque Thanks for the Clarification, that helps. I also updated my question with some clarification.
  • Andreas
    Andreas almost 8 years
    It would be better to create an appropriate formatter, rather than using ISO + replace, e.g. DateTimeFormatter jsonDateTimeFormat = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss", Locale.US)
  • Basil Bourque
    Basil Bourque almost 8 years
    @Andreas That's a matter of taste in this particular case. The SPACE instead of T actually is compliant with ISO 8601 as a variation though not canonical. So I don't consider the String::replace to be crude mangling. Using the named formatter constant plus a call to replace seems to be more self-documenting as to my intention of using the variation of ISO 8601.
  • Andreas
    Andreas almost 8 years
    But if you have to do it in many places, it's better for the formatter to produce the correct result, rather than having to remember to add replace() every time. It's also better for performance. All assuming you use a global, shared formatter instance. Of course, in this particular case, since OP needs timezone handling too, a static helper method is likely the best option, so there may only be one line with replace() anyway.
  • Basil Bourque
    Basil Bourque almost 8 years
    @Andreas Agreed. Really, java.time should have more formatters pre-defined, such as this space-instead-of-T variation and the “basic” variations where most delimiters are removed.
  • expert
    expert over 7 years
    Code at the end of the answer is incorrect. Parsing throws an exception.
  • Basil Bourque
    Basil Bourque over 7 years
    @expert Fixed. Forgot to pass formatter as the second argument to parse method. LocalDateTime.parse( input , formatter ); instead of LocalDateTime.parse( input ); If the formatter is not explicitly passed, the default ISO 8601 parser is used by default. The ISO 8601 format requires a T in the middle rather than a SPACE (unless both sending and receiving parties agree to a SPACE).