Search code examples
javaandroiddatetimekotlinandroid-jodatime

How to convert GMT +09:00 to local time?


When I print date that I get from server, it shows Mon Jun 24 16:15:31 GMT+09:00 2019

val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
val date: Date? = formatter.parse(checkedDate) // date from server
val transformedDate = ("${String.format("%02d", date!!.month + 1)}.${String.format("%02d", date!!.date)}.${date.year + 1900}")
val title: String? = ("$transformedDate")

val longGmtTime = date.time
val mZone = TimeZone.getDefault()
val offset = mZone.getOffset(longGmtTime)
val longLocalTime = longGmtTime + offset - (9 * HOUR)

val localDate = Date() // local date
localDate.time = longLocalTime
val localFormatTime = formatter.format(localDate)
val transformedLocalDate = ("${String.format("%02d", localDate!!.month + 1)}.${String.format("%02d", localDate!!.date)}.${localDate.year + 1900}")

And it gives me server time: 2019-06-24 16:15:31 -> 06.24.2019, local time(Asia/Seoul)-> 2019-06-25 01:15:30 ->06.25.2019 for the result.

The server time and local time must be the same. But the local time shows somewhere else.

What's the problem?


Solution

  • What's the problem?

    The gross problem list includes:

    • You are using the poorly designed and long outdated Java date and time classes Date, TimeZone and SimpleDateFormat.
    • You are using the deprecated methods getMonth, getDate and getYear of the Date class. These methods work unreliably across time zone, which is the main reason why they were deprecated.
    • You are doing the time zone conversion manually using addition, subtraction and multiplication. Date and time math is error-prone, and you should always leave it to proven library methods.
    • The millisecond count you get from Date.getTime is since the epoch of 1970-01-01T00:00:00 UTC. This is a unique moment in time and independent of time zone, so adding to and subtracting from the millisecond count for time zone conversion makes no sense.
    • I can reproduce your result when I set my JVM’s default time zone to Asia/Seoul and assume that HOUR is 0 (or some value in the range from 0 through 111). I assume that you had wanted HOUR to denote the number of milliseconds in an hour, 3 600 000 (at least usually, exceptions exist).
    • You were formatting your date by concatenating the results of calls to Strirg.format. It’s better to leave formatting to a specialized date formatter.

    The fix: java.time

        ZoneId serverTimeZone = ZoneId.of("Asia/Seoul");
        DateTimeFormatter serverFormatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
        ZoneId clientTimeZone = ZoneId.systemDefault();
    
        String checkedDate = "2019-06-24 16:15:31";
        ZonedDateTime serverDateTime = LocalDateTime.parse(checkedDate, serverFormatter)
                .atZone(serverTimeZone);
        ZonedDateTime clientDateTime = serverDateTime.withZoneSameInstant(clientTimeZone);
        System.out.println("clientDateTime: " + clientDateTime);
    

    Sorry that I can write and run only Java code, I trust you to translate. With my JVM’s time zone still set to Asia/Seoul I get:

    clientDateTime: 2019-06-24T16:15:31+09:00[Asia/Seoul]

    The server time and the client time are the same, as you requested. If instead I keep my own time zone, I get:

    clientDateTime: 2019-06-24T09:15:31+02:00[Europe/Copenhagen]

    So there is a conversion taking place.

    To format the date:

        DateTimeFormatter displayFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)
                .withLocale(Locale.forLanguageTag("ko-KR"));
        String transformedLocalDate = clientDateTime.format(displayFormatter);
        System.out.println("transformedLocalDate: " + transformedLocalDate);
    

    transformedLocalDate: 2019. 6. 24.

    Or if you insist on month.date.year:

        DateTimeFormatter displayFormatter = DateTimeFormatter.ofPattern("MM.dd.u");
    

    transformedLocalDate: 06.24.2019

    A further recommendation would be to have your server deliver a date-time string in UTC in ISO 8601 format. That would go like 2019-06-24T07:15:31Z for the moment used in the examples.

    Question: Can I use java.time with minSdk API level 23 on Android?

    Yes, java.time works nicely on older and newer Android devices. It just requires at least Java 6.

    • In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
    • In Java 6 and 7 get the ThreeTen Backport, the backport of the modern classes (ThreeTen for JSR 310; see the links at the bottom).
    • On (older) Android use the Android edition of ThreeTen Backport. It’s called ThreeTenABP. And make sure you import the date and time classes from org.threeten.bp with subpackages.

    Links