Time is a tricky thing: why Swift developers go crazy
\n\nHave you ever sent a push notification that arrived to a user at 3 AM? Or calculated a deadline for an international team, only to have it shift by a whole day? Welcome to the world of time zones — that area of programming where a single missing 'Z' in a string can break your entire app's logic.
\n\nIn Swift, working with time zones is not just about calling Date(). It's a whole ecosystem of classes: Date, TimeZone, DateFormatter, Calendar, and DateComponents. If you don't figure them out from the start, your project will turn into a nightmare of bugs that only reproduce for users in certain regions.
In this article, we'll break down working with time zones in Swift: from simple formatting to complex scenarios with daylight saving time transitions. You'll learn how not to mess up with UTC, why Date is just a timestamp, and how to make your app work correctly anywhere in the world.
Date in Swift: the main pitfall for beginners
\n\nThe first thing to remember: Date in Swift is not a date or a time. It's simply the number of seconds that have passed since January 1, 2001, 00:00:00 UTC (Reference Date). By itself, Date knows nothing about time zones or calendars. It's an absolute timestamp.
\n\nHere's a classic beginner mistake:
\n\nlet now = Date()\nprint(now) // "2025-03-15 14:30:00 +0000" — this is UTC, not your local time!\n\n\nWhen you print Date to the console, Xcode shows it in UTC. But the user sees this time in their own time zone. The problem is that many beginners think Date already 'knows' their local time. No. Date is just a number. Everything else is interpretation.
How to correctly get the current time in the desired zone
\n\nTo work with 'human' time, you need to use DateFormatter and TimeZone. Here's a minimal working example:
let dateFormatter = DateFormatter()\ndateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"\ndateFormatter.timeZone = TimeZone(identifier: "Europe/Moscow")\n\nlet now = Date()\nlet moscowTime = dateFormatter.string(from: now)\nprint("Current time in Moscow: \\(moscowTime)")\n\n\nRemember the rule: store dates in UTC, display them in the local zone. If you save a string like '2025-03-15 14:30:00' to a database without specifying a time zone, you're creating a time bomb. Always add the offset or use ISO 8601 with the 'Z' or '+03:00' suffix.
\n\nTimeZone: your main helper and enemy
\n\nThe TimeZone class in Swift is an interface to the IANA time zone database (Olson database). It stores information about daylight saving time transitions, historical changes, and geographic zones.
The most popular ways to create a TimeZone:
\n\n- \n
- By identifier (recommended):
TimeZone(identifier: "America/New_York")\n - By abbreviation (unreliable):
TimeZone(abbreviation: "EST")— can confuse summer/winter time \n - By offset from GMT:
TimeZone(secondsFromGMT: 10800)— ignores DST \n - Current system zone:
TimeZone.current\n
Here's an example of why abbreviations are evil:
\n\nlet est = TimeZone(abbreviation: "EST") // Eastern Standard Time (UTC-5)\nlet edt = TimeZone(abbreviation: "EDT") // Eastern Daylight Time (UTC-4)\n\n// But if you use EST in July, you'll get the wrong time!\n// Always use "America/New_York" — it will account for DST automatically\n\n\nAlways use IANA identifiers — they account for history and daylight saving time transitions. This is critically important for applications,