0

I have a date, assumed to be in GMT, which I want to convert to local time zone using the ISO_OFFSET_DATE_TIME formatting.

Basically, I want to go from:

2018-03-13 03:00:00.0

to:

2018-03-13T00:00:00-09:00

Obviously this would change, depending on your local time zone.

Any ideas on how I could do this?

robelgado
  • 103
  • 2
  • 11

2 Answers2

2

You can leverage ZonedDateTime for this. You just need to read in the date as UTC and convert it as needed. You might get something like this:

String readPattern = "yyyy-MM-dd HH:mm:ss.S";
DateTimeFormatter readDateTimeFormatter = DateTimeFormatter.ofPattern(readPattern).withZone(ZoneOffset.UTC);
LocalDateTime utcLocalDateTime = LocalDateTime.parse("2018-03-13 03:00:00.0", readDateTimeFormatter);
ZonedDateTime localZonedDateTime = utcLocalDateTime.atOffset(ZoneOffset.UTC).atZoneSameInstant(ZoneId.systemDefault());
String writePattern = "yyyy-MM-dd HH:mm:ssXXX";
DateTimeFormatter writeDateTimeFormatter = DateTimeFormatter.ofPattern(writePattern);
System.out.println(writeDateTimeFormatter.format(localZonedDateTime));

For more info, see:

Lee Marlow
  • 133
  • 3
  • This Answer is not bad but has some issues. It could use a bit more discussion/explanation. And, I suggest breaking it up into two phases, parsing the input, then applying the `ZoneOffset`. – Basil Bourque Oct 16 '18 at 00:28
  • Since you are parsing as a `LocalDateTime` (no zone or offset), there is no need to call `withZone` on your `DateTimeFormatter`. Does not add value to this example. – Basil Bourque Oct 16 '18 at 00:29
  • `ZonedDateTime` is not appropriate here. When using a mere offset-from-UTC rather than a time zone, use the `OffsetDateTime` class, as the name suggests. – Basil Bourque Oct 16 '18 at 00:31
  • 1
    Your last two lines are confusing and tangled. You should be working with time zones at that point, not an offset. Call `ZoneId.systemDefault`. All of your code could be replaced by one line: `LocalDateTime.parse( "2018-01-23 01:23:45".replace( " " , "T" ) ).atOffset( ZoneOffset.UTC ).atZoneSameInstant( ZoneId.systemDefault() )` – Basil Bourque Oct 16 '18 at 00:34
  • Thanks for the answer. Your basic idea is right, and @BasilBourque has already offered some good suggestions for improvement. Allow me to add that `SSS` is requiring three decimals on the second, and the example string in the question has only one. So use only one `S`. – Ole V.V. Oct 16 '18 at 03:33
  • @BasilBourque How would I get this into OffsetDateTime, as you suggested? Your one liner is suitable for ZonedDateTime – robelgado Oct 16 '18 at 12:15
  • Greatly appreciate all your help! Simply convert to OffsetDateTime by calling .toOffsetDateTime() – robelgado Oct 16 '18 at 14:20
  • @user1655963 Better to stay with a `ZonedDateTime` if you know the intended time zone. A zone is always preferable over a mere offset. If your concern is generating a string in your desired format with an offset indicator, stick with `ZonedDateTime` and use `DateTimeFormatter` to generate text in any format you wish. – Basil Bourque Oct 16 '18 at 15:48
  • @BasilBourque the same is essentially accomplished by first converting to a ZonedDateTime and then creating a OffsetDateTime using .toOffsetDateTime() How would you have gone about using the DateTimeFormatter to remove the unwanted text from the ZonedDateTime? – robelgado Oct 17 '18 at 12:21
1

Parse the date-time string into LocalDateTime:

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("u-M-d H:m:s.S", Locale.ENGLISH);
LocalDateTime ldt = LocalDateTime.parse("2018-03-13 03:00:00.0", dtf);

Combine this with UTC offset to create an OffsetDateTime:

OffsetDateTime odtUtc = ldt.atOffset(ZoneOffset.UTC);

Create its copy with offset set as -09:00 while keeping the instant same:

OffsetDateTime odtUtcMinus9 = odtUtc.withOffsetSameInstant(ZoneOffset.of("+09:00"));

Demo:

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("u-M-d H:m:s.S", Locale.ENGLISH);
        LocalDateTime ldt = LocalDateTime.parse("2018-03-13 03:00:00.0", dtf);
        System.out.println(ldt); // 2018-03-13T03:00

        OffsetDateTime odtUtc = ldt.atOffset(ZoneOffset.UTC);
        System.out.println(odtUtc); // 2018-03-13T03:00Z

        OffsetDateTime odtUtcMinus9 = odtUtc.withOffsetSameInstant(ZoneOffset.of("+09:00"));
        System.out.println(odtUtcMinus9); // 2018-03-13T12:00+09:00
    }
}

Note that the timezone offset is a fixed thing i.e. it is independent of the DST. If you are looking for an automatic adjustment of timezone offset as per the DST, use ZonedDateTime. The methods are very much similar to what we have used in the last demo.

Demo:

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class Main {
    public static void main(String[] args) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("u-M-d H:m:s.S", Locale.ENGLISH);
        LocalDateTime ldt = LocalDateTime.parse("2018-03-13 03:00:00.0", dtf);
        System.out.println(ldt); // 2018-03-13T03:00

        ZonedDateTime zdtUtc = ldt.atZone(ZoneId.of("Etc/UTC"));
        System.out.println(zdtUtc); // 2018-03-13T03:00Z[Etc/UTC]

        ZonedDateTime zdtAmericaAdak = zdtUtc.withZoneSameInstant(ZoneId.of("America/Adak"));
        System.out.println(zdtAmericaAdak); // 2018-03-12T18:00-09:00[America/Adak]

        // A custom format
        DateTimeFormatter dtfOutput = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss.SSSXXX", Locale.ENGLISH);
        String formatted = dtfOutput.format(zdtAmericaAdak);
        System.out.println(formatted); // 2018-03-12 18:00:00.000-09:00
    }
}   

Learn more about java.time, the modern date-time API* from Trail: Date Time.


* For any reason, if you have to stick to Java 6 or Java 7, you can use ThreeTen-Backport which backports most of the java.time functionality to Java 6 & 7. If you are working for an Android project and your Android API level is still not compliant with Java-8, check Java 8+ APIs available through desugaring and How to use ThreeTenABP in Android Project.

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110