Camsoft
Camsoft

Reputation: 12015

Adjusting Swift Date from one timezone to another is not producing expected result

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.

Upvotes: -1

Views: 1980

Answers (3)

Gihan
Gihan

Reputation: 2536

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.

Upvotes: 0

Paulw11
Paulw11

Reputation: 115051

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.

Upvotes: 0

HangarRash
HangarRash

Reputation: 15042

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.

Upvotes: 1

Related Questions