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.