English | 日本語
Calendar dates that never depend on the device's time zone — "today", "yesterday" and "the start of the day" come out the same on every phone, because the time zone is always an argument.
- A date without a time zone:
LocalDateis a day on the calendar. It adds and counts days, knows its weekday, and forms ranges you can iterate - The time zone is always explicit: every conversion between an instant and a date or wall-clock time takes a
TimeZone. Nothing reads the device's clock, time zone, calendar or locale, and a test enforces it - One text form:
"YYYY-MM-DD"forCodable, dictionary keys and parsing. Anything else is refused instead of guessed - Daylight saving handled honestly: a skipped wall-clock time converts to
nil, a repeated one to its earlier instant, and a day whose midnight is skipped starts at the transition - Input from outside never crashes: text, JSON and instants (
Date.distantPast, NaN, milliseconds read as seconds) convert tonilwhen they are not a date. Only arithmetic you control, such as stepping past year 9999, traps - Spans and windows that cannot be reversed:
DateSpan(days, first through last) andTimeWindow(instants from a start up to an end) arenilwhen reversed, including when decoded. ATimeWindowholds its start but not its end, so back-to-back windows never overlap; a zero-length window is a fixed time, and two at the same instant overlap - Months and weeks:
daysInMonth,monthSpanandstartOfWeek(firstWeekday:)build calendar grids without aCalendar - Foundation only: no dependencies,
Sendablethroughout
import CalendarDate
let tokyo = TimeZone(identifier: "Asia/Tokyo")!
let now = Date() // your clock, passed in
let today = LocalDate.today(now: now, in: tokyo) // 2026-09-25, wherever the phone is
let lastRead = LocalDate("2026-09-24")!
let streakAlive = lastRead >= today.adding(days: -1)
let trip = DateSpan(first: LocalDate("2026-09-23")!, last: today)!
trip.nights // 2
for day in trip.days { print(day, day.weekday) }
let meeting = today.instant(at: LocalTime("15:30")!, in: tokyo) // Date?
let week = today.startOfWeek(firstWeekday: .monday)...today // this week so far
let fromServer = Date(timeIntervalSince1970: serverSeconds)
let day: LocalDate? = LocalDate(fromServer, in: tokyo) // nil if it is not a dateA date that is not known yet is LocalDate? and nil — not an empty string, which neither parses
nor decodes.
The API reference and an article on how time zones and daylight saving are handled live on GitHub Pages.
Add the package to your Package.swift:
dependencies: [
.package(url: "https://github.com/no-problem-dev/swift-calendar-date.git", .upToNextMinor(from: "0.1.0"))
]Then add the product to your target:
.target(
name: "YourTarget",
dependencies: [
.product(name: "CalendarDate", package: "swift-calendar-date")
]
)MIT License - See LICENSE for details.