Date and time might be among your list of the hardest things in programming (It is for me). Today, I'm going to talk about a basic concept of a `Date` and its companion `DateComponents`.

## What is Date?

Date is a specific point in time, independent of any calendar or time zone. Date values represent a time interval relative to an absolute reference date. You might come across this concept if you have ever work with Unix timestamp, which describes a date in the form of a time interval that has elapsed since the Unix epoch[1] (00:00:00 UTC on 1 January 1970).

Representing a date by a time interval makes it an easy task for the `Date` structure to provide methods for comparing dates, calculating the time interval between two dates, and creating a new date from a time interval relative to another date.

The key takes away here is `Date` is a specific point in time, independent of any calendar or time zone.

## Date in our daily life

I point out that a `Date` is independent of a calendar or time zone because it is a crucial difference from date and time as we know and use it in our daily life.

If you ask the question, "what day is today?" in your office, you might get the same answer regardless of who you ask. But if you ask the same question on the internet, you might get a different one. That's because people in your office assume the same calendar and time zone as you, but the people on the internet think in their calendar and time zone. That's why you might get a different answer to the very same question.

Right now, in Thailand, it is 22:43 (GMT+7) October 26, 2563 BE (Check current local time in Bangkok here), which is unlikely to be the same date as you. Especially the calendar part.

``let date = Date()``

So the same `date` in code might not be the same date as we comprehend it. To create meaningful localized representations of dates and times, we need a calendar and timezone.

In iOS, we have many classes that do the hard lifting, such as `DateFormatter` that converts between dates and their textual representations.

``let date = Date()let dateFormatter = DateFormatter()dateFormatter.dateStyle = .mediumdateFormatter.timeStyle = .nonedateFormatter.string(from: date)``

I said time zone and calendar are crucial in creating meaningful representations of dates and times, but I didn't set any of that in the previous example. That's because `DateFormatter` will assume the current calendar and time zone setting from your device. So, please make sure you aware of these two hidden components when dealing with date and time. Failing to do so is a cause of many bugs.

## What is DateComponents?

`Date` is a good struct to represent a specific point in time, but it not so useful when we want to manipulate a date as we know and understand. Let's say I want to get a date of the same day but in the next month from now. It is quite hard and error-prone to do that on a `Date` value.

Since `Date` is all about time interval, you could do something like this.

``let date = Date()let nextWeek = date.addingTimeInterval(60 * 60 * 24 * 30) // <1>``

<1> 30 days equal 60 seconds x 60 minutes x 24 hours & 30 days.

But the above example didn't take time zone and calendar into account, so the result might not be the one we expected since not every month has 30 days, and in some areas, there is a daylight saving concept, which means some days might be more or less than 24 hours. That's why we have `DateComponents`.

`DateComponents` is a struct representing a date in terms of units (such as year, month, day, hour, and minute).

To use it, we first create a `DateComponents`. Then, set all the components that make up a date. In this case, we want a date to be October 21, 2020, without time components (which will default to 00:00:00).

``var comps = DateComponents() // <1>comps.day = 21comps.month = 10comps.year = 2020let date = Calendar.current.date(from: comps)! // <2>print(date)``

<1> Set all units that make up a date to `DateComponents`.
<2> Get a date created from the specified components.

This looks like a straightforward API, but there is a lot of pitfalls here and there.

### Quiz

To test out your understanding, let's see if you can answer this question. What is the return date in UTC of the above `DateComponents`?

If your answer is it depends, you are on the right path. If you run the above code, you might get a different answer. Mine is `2020-10-20 17:00:00 +0000`. You will get a different answer if you are not in a country with a timezone of GMT+7.

### Key

As you can see, just a unit of time is not sufficient to pinpoint a specific point in time. Because when we say January 1, 2020, we leave out two hidden components. That is a time zone and calendar. If you open a television on a new year's day, you would see that the firework doesn't happen at the same time. Because we interpret a new years' day in our own time zone.

Let's go back a little bit and see the definition of `DateComponents` from Apple documentation.

DateComponents
A date or time specified in terms of units (such as year, month, day, hour, and minute) to be evaluated in a calendar system and time zone.

You will see that time components need to be evaluated in a specified calendar and time zone to be able to get a specific date.

So, let's look back at our question. We leave out `calendar` and `timezone` as `nil` here. So, the system use calendar and timezone of the receiver instead `let date = Calendar.current.date(from: comps)!`. In this case, it is the current calendar and time zone, `Calendar.current`.

``var comps = DateComponents()comps.day = 21comps.month = 10comps.year = 2020``

Here is my current calendar.

``gregorian (current)  - identifier : Foundation.Calendar.Identifier.gregorian  - kind : "current"  ▿ locale : Optional<Locale>    ▿ some : en (current)      - identifier : "en"      - kind : "current"  ▿ timeZone : Asia/Bangkok (current)    - identifier : "Asia/Bangkok"    - kind : "current"    ▿ abbreviation : Optional<String>      - some : "GMT+7"    - secondsFromGMT : 25200    - isDaylightSavingTime : false  - firstWeekday : 1  - minimumDaysInFirstWeek : 1``

So our date is October 21, 2020, in Asia/Bangkok timezone (GMT+7). That's why when we print out this date in a console, which is a UTC format, we get `2020-10-20 17:00:00 +0000`.

### Examples

The following are examples of variation setting calendar/timezone for `DateComponents`.

Not set `calendar` and `timezone` in `DateComponents` would use a calendar and timezone from the receiver which is `Calendar.current` (GMT+7).

``var comps = DateComponents()comps.day = 21comps.month = 10comps.year = 2020let date = Calendar.current.date(from: comps)!// 2020-10-20 17:00:00 +0000``

Set a `calendar` to `DateComponents` would not effect a `timezone` eventhough you set a `timezone` to that `calendar`. The system still pickup a timezone from the receiver which is `Calendar.current` (GMT+7).

``var calendar = Calendar(identifier: .gregorian)let timezone = TimeZone(secondsFromGMT: 0)!calendar.timeZone = timezonevar comps = DateComponents()comps.calendar = calendarcomps.day = 21comps.month = 10comps.year = 2020let date = Calendar.current.date(from: comps)!// 2020-10-20 17:00:00 +0000``

If you want to specify a time zone for the `DateComponents`, explicitly set it in `timezone`. The result date would be October 21, 2020 at GMT+0 in this case (Ignore our `Calendar.current`).

``let timezone = TimeZone(secondsFromGMT: 0)!var comps = DateComponents()comps.timeZone = timezonecomps.day = 21comps.month = 10comps.year = 2020let date = Calendar.current.date(from: comps)!// 2020-10-21 00:00:00 +0000``

If you want to use the same time zone for all `DateComponents`, you can create a custom calendar for the conversion.

``var calendar = Calendar(identifier: .gregorian)let timezone = TimeZone(secondsFromGMT: 0)!calendar.timeZone = timezonevar comps = DateComponents()comps.day = 21comps.month = 10comps.year = 2020let date = calendar.date(from: comps)!// 2020-10-21 00:00:00 +0000``

Timezone (and other values) in `DateComponents` would have precedance over value in receiver calendar. In this case timezone from `DateComponents` will use in a conversion (GMT+7).

``var calendar = Calendar(identifier: .gregorian)let timezone = TimeZone(secondsFromGMT: 0)!calendar.timeZone = timezonevar comps = DateComponents()comps.timeZone = TimeZone.currentcomps.day = 21comps.month = 10comps.year = 2020let date = calendar.date(from: comps)!// 2020-10-20 17:00:00 +0000``

## Conclusion

It is a lot easier to manipulate date and time with `DateComponents` because it aligns with how we interpret date and time in the real world. That's why you will see a lot of `Calendar` methods involve `DateComponents`.

``func dateComponents(Set<Calendar.Component>, from: Date) -> DateComponentsfunc date(from: DateComponents) -> Date?func date(byAdding: Calendar.Component, value: Int, to: Date, wrappingComponents: Bool) -> Date?``

Knowing what `Date` really is and how to convert them between time zone and calendars is important when working with date and time.

1. An epoch, for the purposes of chronology and periodization, is an instant in time chosen as the origin of a particular calendar era. The "epoch" serves as a reference point from which time is measured. https://en.wikipedia.org/wiki/Epoch#Notable_epoch_dates_in_computing ↩︎

Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Previous
What is @escaping in Swift closures

Learn the meaning of @escaping, so you know what to do when you see it or when you need to write one.

Next
Getting the number of days between two dates in Swift

There are a few variations when dealing with counting days. You need to ask yourself some questions beforehand.

← Home