1

Android Studio 4.0, Java 6. My GMT is GMT + 03:00

 defaultConfig {
        minSdkVersion 17
        targetSdkVersion 28


 const val LONDON_TIME_ZONE_ID = "Europe/London"
const val DEFAULT_DATE_JSON_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.000'Z'"

    fun fromDateToLondonDate(localDate : Date) : Date? {
        val dateLondonAsString = fromDate2LondonDateAsString(localDate)
        val timeZoneLondon : TimeZone = TimeZone.getTimeZone(LONDON_TIME_ZONE_ID)
        val dateLondon = getDateFromString(dateLondonAsString, timeZoneLondon)
        if (BuildConfig.DEBUG)
            Log.d("", "fromDateToLondonDate:" +
                    "\nlocalDate = $localDate"+
                    "\ndateLondonAsString = $dateLondonAsString"+
                    "\ndateLondon = $dateLondon")
        return dateLondon
    }

    fun fromDate2LondonDateAsString(date : Date) : String? {
        val timeZoneLondon : TimeZone = TimeZone.getTimeZone(LONDON_TIME_ZONE_ID)
        val formatter: DateFormat = SimpleDateFormat(DEFAULT_DATE_JSON_FORMAT)
        formatter.setTimeZone(timeZoneLondon)
        val dateAsString : String = formatter.format(date)
        return dateAsString
    }

    fun getDateFromString(str: String?, tz: TimeZone?): Date? {
        return try {
            val sdf = SimpleDateFormat(DEFAULT_DATE_JSON_FORMAT);
            sdf.setTimeZone(tz)
            val date = sdf.parse(str)
            return date
        } catch (e: ParseException) {
            //e.printStackTrace();
            null
        }
    }

and here result:

fromDateToLondonDate:
 localDate = Tue Jun 16 13:14:59 GMT+03:00 2020
 dateLondonAsString = 2020-06-16T11:14:59.000Z
 dateLondon = Tue Jun 16 13:14:59 GMT+03:00 2020

as you can see the local date is Tue Jun 16 13:14:59 GMT+03:00 2020 and success convert to London date as String -> dateLondonAsString = 2020-06-16T11:14:59.000Z

Nice. But I need to convert String to London Date. And I use method getDateFromString and result is not correct:

dateLondon = Tue Jun 16 13:14:59 GMT+03:00 2020

The correct London Date must be : Tue Jun 16 11:14:59 GMT+03:00 2020

Why getDateFromString not correct convert from string to date?

Alexei
  • 14,350
  • 37
  • 121
  • 240
  • 2
    I recommend not to use `java.util.Date`, it's outdated for good reasons. One of them is troublesome handling of offsets and zones. Can you use `java.time`? – deHaar Jun 16 '20 at 10:27
  • @deHaar It's old Android project. And I can't use java.time. I can use only java.util.Date – Alexei Jun 16 '20 at 10:35
  • 1
    Ok, but there's a [backport](https://github.com/JakeWharton/ThreeTenABP) that you can import in order to have this functionality... See [this question (*How to use ThreeTenABP in Android*)](https://stackoverflow.com/questions/38922754/how-to-use-threetenabp-in-android-project), too. – deHaar Jun 16 '20 at 10:50
  • This is Kotlin, not Java. – MC Emperor Jun 16 '20 at 12:22

1 Answers1

3

java.time and ThreeTenABP

To get a date-time object with a specific time zone turn to java.time, the modern Java date and time API. I am writing Java code, it’s what I can, and I trust you to translate:

    ZoneId london = ZoneId.of("Europe/London");

    Date date = getOldfashionedDateFromSomewhere();
    System.out.println("Original Date: " + date);

    ZonedDateTime dateTimeLondon = date.toInstant().atZone(london);
    System.out.println("Date-time in London: " + dateTimeLondon);

Example output:

Original Date: Tue Jun 16 13:14:59 GMT+03:00 2020
Date-time in London: 2020-06-16T11:14:59+01:00[Europe/London]

A Date hasn’t got, as in cannot have a time zone. Yes, I know, when you print it, thereby implicitly calling its toString method, it prints a time zone. What happens is that it grabs the default time zone of your JVM and uses it for rendering the string. So as long as your JVM’s time zone setting is GMT+03:00, all of your Date objects will always print this time zone.

So if (in some another project) I need date with time zone the better way is use java.time.ZonedDateTime (java 8) ?

It's at least the way I would recommend. As I said, using Date is not a way. Some ways are:

  1. The poor and old-fashioned way: using GregorianCalendar.
  2. The better way: using DateTime from Joda-Time. If accepting an external dependency, I'd prefer ThreeTenABP, though.
  3. The good way: ZonedDateTime from java.time.
  4. The advanced way: using Time4J. I haven't got experience, so I'd rather not recommend for or against. If your project has requirements that go beyond what java.time offers, I'd certainly research this option.

What went wrong in your code

First, you should probably not set your time zone to GMT+03:00. While your time zone uses this GMT offset now, that doesn’t mean that it always has nor that it always will. For correct results for historic and future dates use a real time zone ID such as Africa/Nairobi or Europe/Moscow, that is, in the region/city format. Just as you used Europe/London for British time (fortunately not GMT+00:00).

I have already mentioned the second point: You cannot use a Date for a date and time with a time zone because a Date hasn’t got a time zone. A Date is a point in time, no more, no less.

Next, 2020-06-16T11:14:59.000Z is wrong for the time in London. 11:14:59 is correct for the time of day. Z means UTC or offset 0 from UTC and is wrong since Great Britain uses summer time (DST) and hence is at offset +01:00 in June (as the output from my code above also says). The time at offset Z would have been 10:14:59. In other words, your time is 1 hour off. This definition of Z is part of the ISO 8601 standard. Your JSON format is ISO 8601 format. I include a link at the bottom. Since Z is an offset, you should always format and parse it as such and never hardcode it as a literal in your format pattern string.

Your conversion back from string to Date exhibits the same error, so it balances out with the error in converting to a String, and you succeed in getting an equivalent Date object back.

It's old Android project. And I can't use java.time. I can use only java.util.Date

You can certainly use java.time in old Android projects for old Android versions too.

  • In Java 8 and later and on newer Android devices (from API level 26) the modern API comes built-in.
  • In non-Android 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

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • So if (in some another project) I need date with time zone the better way is use java.time.ZonedDateTime (java 8) ? – Alexei Jun 16 '20 at 21:04