-1

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.
Camsoft
  • 11,718
  • 19
  • 83
  • 120

3 Answers3

1

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.

HangarRash
  • 7,314
  • 5
  • 5
  • 32
  • Thank you but I need to construct a date object before adjusting to local time. The date would be hardcoded in the production application, only the unit test will create a mock date in order to simulate a device date that is from a timezone other than Europe/London. The code I’m testing will adjust the Date() to GMT. – Camsoft Dec 07 '22 at 20:30
  • `Date` doesn't have a timezone. It's not clear why you think anything needs to be adjusted. Perhaps you should redo your question with the actual problem you are having instead of making up an example that doesn't seem to apply. It seems like you are making an issue where one doesn't actually exist. – HangarRash Dec 07 '22 at 20:43
  • I never said Date has a timezone in fact I clearly pointed that out in my original question. – Camsoft Dec 08 '22 at 21:33
  • Sorry if I misstated the timezone statement. But the rest of my comment still applies. It’s unclear what you are really trying to accomplish or what actual problem you are trying to solve. – HangarRash Dec 08 '22 at 21:35
  • What I’m asking is to adjust a fixed date and time that represents a date in a timezone to another ignoring the current devices calendar time zone. The reason for mentioning timezone is that you need to know what zone the time and date was representing in order to calculate any offset between that zone and the reference zone to adjust the time to. I want this to be completely independent of the system zone. – Camsoft Dec 08 '22 at 21:36
  • Please update your question with actual code demonstrating your original issue, not your attempted solution. Your description in your comment doesn’t really clarify your issue at all. As you state, Date doesn’t have a timezone so it isn’t clear why you think you need to adjust the non-existent timezone. – HangarRash Dec 08 '22 at 21:41
0

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.

Paulw11
  • 108,386
  • 14
  • 159
  • 186
0
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.

Gihan
  • 2,476
  • 2
  • 27
  • 33