DayType — A Swift API for dates without the time baggage

Hello everyone,

I'd like to share a package I've been working on called DayType.

The problem DayType solves

Swift's Date APIs are great, but they are also limited by their focus on representing points in time. This means that when you want to work with just a calendar date such as someone’s birthday, a deadline or a booking for an event - you're often having to sanitise time components, manage time zones and perform a variety of related functions in case something drifts across a midnight boundary.

DayType solves this by providing a Day type to represent a calendar date, and nothing else. No time components, timezone or calendars. No stripping time, start of day, midnight or other manipulations and validations needed.

Just dates.

For example:

let birthday = try Day(1985, 8, 29)
let today = Day.today
let daysUntil = birthday - today
let buyPresentDate = birthday - 2 

for day in today ... birthday {
    // iterate every day in a range
}

Plus:

  • Calendar grid generation for building calendar UIs.

  • Codable property wrappers covering a variety of common server date formats. (@Epoch.Seconds, @ISO8601.Default, @DayString.DMY, etc.)

  • Common protocol conformance to Comparable, Hashable and Strideable.

  • SwiftData support (with a documented workaround for a current SwiftData limitation).

  • A SwiftUI calendar picker capable of selecting both singular dates and date ranges.

Feedback and questions welcome!

6 Likes

thats cool, Also from a library design pov do you have plans to adopt the newer FormatStyle and ParseableFormatStyle protocols?

1 Like

Hi and thanks. Day already has a .format(…) function which is the same as Swift Date's format function, but without the time option. Parsing the other way looks like it might be useful to add. I haven't done it simply because I haven't needed it, but if people want it, I'll certainly add it in. If you take a look at the FUTURE.md file you'll see a whole bunch of things Claude is suggesting can be added at some stage as well.

ciao
Derek

What you think about adding time zone? There is no one „today” on the Earth.

1 Like

Hi, thanks for your question. I'm not sure if I understood it correctly, but the idea of DayType is to be independant of time zones and time components. Simply because there are many situations where such things are irrelevant to the task at hand. For example, if my birthday is the 2nd of June (it isn't :slight_smile: ) then it's the 2nd of June regardless of the time zone. Plus we don't need any finer level of detail such as the hour and minute I was born. This is the context in which DayType works.

As far as time zones go, DayType does have the ability to accept time zone arguments when converting dates to and from DayType. Does that conver the scenarios you have in mind?

ciao
Derek

1 Like

But if your friend from another continent has birthday „today” it could be too late to send greetings. Or too early. ;) Time does not exist between 23:59:59,99(9) today and 0:00:00,00 tomorrow. It's just observation.

It seems to me that the type you want in that scenario is Date. This Day seems to be intended for use cases where the time component of a date is simply not required at all. For example, a calendar where you want to mark the day on which someone’s birthday occurs. If you want to set a reminder to call them and wish them a happy birthday, you use Date, which can handle time and time zones (with the help of other Foundation types).

2 Likes

This looks like Foundation's DateComponents to me.

fwiw, a useful term i’m picked up for this concept is BusinessDate. makes it clear that the date is more index-like than instant-like.

2 Likes

Great work, thanks for doing it.

Below are my thoughts and feedback that may be useful.

In practice, I’ve run into multiple cases where Foundation.Date is the wrong abstraction. We ended up introducing similar concepts in our codebase, so it seems to be something that comes up across different projects.

From that experience, I’ve found it useful to think in terms of two distinct “day” abstractions:

1. Calendar day (timezone-independent)

  • Birthdays
  • Holidays
  • Soft deadlines (“due on April 10”)
  • Historical dates

These behave like coordinates in a calendar, not slices of time. They don’t represent a duration, just a day or point in a calendar system.

2. Calendar day in a specific timezone

  • Financial trading days (modeling this with Date or a naive DateInterval is error-prone)
  • Legal day counting within a country (a civil day in that jurisdiction’s timezone)
  • Recurring daily schedules (“every day at 09:00 local time”)
  • Queries like “all events on April 10 in Cupertino”
  • Deadlines like “within 7 calendar days”

In these cases, the day is not just a label, it’s a rule-defined unit tied to a timezone. Its boundaries are derived from calendar and timezone rules, not from a fixed duration or a single instant.

The key distinction is that in the second category, the timezone is part of the identity of the day.

I have briefly explored the library and have some thoughts:

  • Day should integrate well with existing Foundation formatting APIs, but formatting should be Calendar- and TimeZone-aware when needed
  • weekday is inherently calendar-dependent, so it may be clearer if it is computed using a function with explicit Calendar (and TimeZone where relevant)
  • The underlying representation (daysSince1970) appears to assume a Gregorian calendar, which may limit its suitability as a universal representation

In my experience, Day aligns closely with a DayComponents concept (year/month/day without intrinsic timezone). Operations on such values often require an explicit Calendar and TimeZone, which might be worth making more explicit in the API design.

A components-based representation (e.g. year/month/day) makes this explicit. In such a model, values like daysSince1970would naturally become derived and depend on the chosen Calendar.

We used a GregorianDay struct, which is a composition of DayComponents and Calendar.gregorian.

Also it is convenient to have a Format type with static consntas where broadly used formats are predefined and discoverable via dot syntax.

Hello @Dmitriy_Ignatyev . Thanks for taking such an in-depth look and offering your ideas. There's some interesting thoughts there. Yes the API is inherently Gregorian in its internal math. I have wondered whether it might be useful to enable other calendar systems but I haven't looked to see if there's any equivalent math to drive them. I assume there is, I just haven't gone looking for it as (to my knowledge) the Gregorian calendar is the most used calendar in apps and the one I've always had to deal with.

I may have not used to best terminology in referring to a day as a floating period of time, but I could think of anything better. Perhaps abstraction might be a better way to express it. Certainly there is room to explore the boundaries of how to interpret the internal number of days when converting to other formats (including DayComponents) but in the readme I outlined there are so many things I could add, but wanted to see what people wanted rather than trying to throw an immense array of functions at it. I'll certainly take a look at your suggestions and use cases and see if any seem like something to add.

ciao
Derek

What I mean is calendars like .indian, .japanese, .chinese... should be supported.
I don't suggests to implement them now, but I suggest other calendar systems can be supported at abstractions level if someone want to support them and contribute to the library.

That's why we used GregorianDay name for type in our codebase. Once app become international, we have to deal with other calendars.
The Day that have representation as year: Int64, month: UInt8, day: UInt8 is not tied to any calendar.