Home > Software design >  Adjusting Swift Date from one timezone to another is not producing expected result
Adjusting Swift Date from one timezone to another is not producing expected result

Time:12-08

I'm really struggling with dates and timezones.

My understanding is Date is independent of timezone and is just a point in time.

I'm writing a unit test and I want to create a Date object that would represent the time now if the user was in a different timezone than the default London zone I'm running the test from and then convert it to a different timezone.

I start off by creating a Date object using DateComponents with the projected time, in this example, I want Australia/Sydney time which is 11hrs ahead of GMT (my location).

Next, I want to derive a new Date that is adjusted back to GMT i.e. essentially subtracting 11hrs.

The derived date I want is 2022-12-07 13:30:00 0000. I'm using dateComponents(in:from:) method on Calendar to specify the timezone, according to Apple's documentation:

Returns all the date components of a date, as if in a given time zone (instead of the Calendar time zone).

I then set the timeZone component which should then adjust the time from Australia/Sydney to Europe/London but while it's adjusting the time it's not correctly adjusting the date component which should be shifted one day back.

Here is my code example:

let components = DateComponents(
    year: 2022,
    month: 12,
    day: 8,
    hour: 02,
    minute: 30,
    second: 0
)

let originalDate = Calendar.current.date(from: components)! 
// originalDate printed is 2022-12-08 02:30:00  0000

var components = Calendar.current.dateComponents(in: TimeZone(identifier: "Australia/Sydney")!, from: originalDate)
components.timeZone = TimeZone(identifier: "Europe/London")!

let localTime = components.date! 
// localTime printed is 2022-12-08 13:30:00  0000 but I expected 2022-12-07 13:30:00  0000 as Sydney is  11hrs ahead of GMT 0.

CodePudding user response:

If your goal is to create a Date that is on 2022-12-08 at 02:30 local time in Sydney then the simplest way is:

let components = DateComponents(
    timeZone: TimeZone(identifier: "Australia/Sydney")!,
    year: 2022,
    month: 12,
    day: 8,
    hour: 02,
    minute: 30,
    second: 0
)

let originalDate = Calendar.current.date(from: components)!

That's it. No other conversion is needed. originalDate will show as 2022-12-07 15:30:00 0000.

CodePudding user response:

The reason you get the result you do is that DateComponents are just a set of components. You can create them from a Date and create a Date from them but they aren't a Date.

You are creating DateComponents from a Date using the Australia/Sydney so you get "13:30" in the hour and minute components and "Australia/Sydney" in the time zone component.

Then you change the timezone component to "Europe/London". This doesn't change any other component, so you now have a set of components that represent 13:30 in London.

Another way of thinking about it, is if you set the hours component to 2 instead of changing the Timezone; You are just going to get 02:30 in Australia/Sydney. It isn't going to change the time zone to UTC

If you want to create a Date at a specific time in a particular Timezone, specify that Timezone in your original date components.

CodePudding user response:

let components = DateComponents(
    year: 2022,
    month: 12,
    day: 8,
    hour: 02,
    minute: 30,
    second: 0
)

let originalDate = Calendar.current.date(from: components)!
// originalDate printed is 2022-12-08 02:30:00  0000

var components1 = Calendar.current.dateComponents(in: TimeZone(identifier: "Australia/Sydney")!, from: originalDate)
let afterTimeZoneSetDate = Calendar.current.date(from: components1)!
// this prints - "Dec 8, 2022 at 2:30 AM"

components1.timeZone = TimeZone(identifier: "Europe/London")!

let localTime = components1.date
// localTime printed is 2022-12-08 13:30:00 and this is correct.

If you look at the code above, because your original components does not include a timeZone, it does not alter the time after you set "Australia/Sydney", rather it assign a timeZone now. Beyond this since you are changing a timeZone, it will adapt the time accordingly.

  • Related