DateFormatter's New Year's Bug - YYYY vs yyyy
Why your iOS date formatting breaks around New Year's and how to fix it
Every year around New Year's Eve, a wave of date-related bugs show up in iOS apps. The reason is a single character mistake in DateFormatter: using YYYY instead of yyyy.
The Bug
DateFormatter has two different year specifiers:
yyyy- Calendar year (what you almost always want)YYYY- ISO week-based year (rarely what you want)
The ISO week-based year can differ from the calendar year during the first and last few days of the year. When a week spans two calendar years, the ISO week-based year uses the year that contains the majority of that week.
When This Bites You
On December 30th, 2024 (a Monday), the ISO week-based year is already 2025 because that Monday starts ISO week 1 of 2025. But the calendar year is still 2024. This year it showed up Monday 29th December 2025, the first day of the week it becomes 2026.
import Foundation
let formatter = DateFormatter()
let dec30_2024 = Calendar.current.date(from: DateComponents(year: 2024, month: 12, day: 30))!
// Wrong - uses ISO week-based year
formatter.dateFormat = "YYYY-MM-dd"
print(formatter.string(from: dec30_2024))
// Output: 2025-12-30
// Correct - uses calendar year
formatter.dateFormat = "yyyy-MM-dd"
print(formatter.string(from: dec30_2024))
// Output: 2024-12-30
Here's a table showing how dates around the 2019/2020 boundary behave differently:
| Date | Weekday | yyyy (Calendar) | YYYY (ISO Week) | ISO Week |
|---|---|---|---|---|
| 2019-12-29 | Sunday | 2019-12-29 | 2019-12-29 | 52 |
| 2019-12-30 | Monday | 2019-12-30 | 2020-12-30 | 01 |
| 2019-12-31 | Tuesday | 2019-12-31 | 2020-12-31 | 01 |
| 2020-01-01 | Wednesday | 2020-01-01 | 2020-01-01 | 01 |
Notice how December 30th and 31st, 2019 show as "2020" when using YYYY because they fall in ISO week 1 of 2020.
Real-World Bug Example
Imagine you're generating log filenames in your app:
let formatter = DateFormatter()
let dec31_2019 = Calendar.current.date(from: DateComponents(year: 2019, month: 12, day: 31))!
// Bug: This creates a file with the wrong year!
formatter.dateFormat = "''YYYY-MM-dd'.txt'"
print(formatter.string(from: dec31_2019))
// Output: 2020-12-31.txt
// Correct: Uses the actual calendar year
formatter.dateFormat = "''yyyy-MM-dd'.txt'"
print(formatter.string(from: dec31_2019))
// Output: 2019-12-31.txt
Your 31st December 2019 log file ends up named 2020-12-31.txt.
When to Use Each
Use yyyy for:
- User-facing dates
- Filenames and paths
- Database records
- API date strings
- Anything where you want the actual calendar year
Use YYYY only when:
- Displaying ISO 8601 week dates (e.g., "2020-W01-3")
- Specifically working with ISO week numbering
- Always paired with the week number format specifier (
ww)
The Rule
If you're not explicitly working with ISO week numbers, you want lowercase yyyy. The uppercase YYYY variant exists for a specific use case (week-based date systems), and using it for general date formatting is almost always a bug.
This is one of those bugs that's invisible 360+ days of the year, then strikes during New Year's when everyone's on vacation.