-
Notifications
You must be signed in to change notification settings - Fork 3
TanStack Time
Dealing with Dates and Times on the web is…challenging. By default, browser Date objects default to browser local timezone, even when created with offsets defined. Any developer who has had to build an application displaying dates and times according to where an event occurred, rather than local, knows just how challenging this can be.
But, since the death of IE, the W3C and TC39 work group have been busy trying to make Dates first class citizens of the web world. The Intl API has already introduced date formatting bits allowing for ‘timezone casting’, and the upcoming Temporal API will make date manipulation much more straightforward and standardized.
With all these advancements, things still move somewhat slowly. And, even then, there are still many “ins and outs” of formatting your dates/times and manipulating your data. Online Calendars, date pickers, time pickers, date and time pickers, are everywhere, with different feature sets, varying api’s, and different levels of overall support.
With TanStack Time, we look to create a library of utilities for dealing with dates and times in a manner that is easy to use, takes the guesswork and complexity away, and allows developers to focus more on their date/time specific application logic, rather than on the “date” logic.
TanStack Time would replace libraries like Moment and Luxon and DayJS, using standards based core logic, and provide a consistent api for creating, manipulating, and displaying dates. It should allow for timezone specific manipulation and display, creating and managing groups, find intersections between groups, and more.
What day is the beginning of a week? There are 7+ Billion people in the world. Half of them see Sunday as the beginning of their week, while the other half recognizes Monday as the beginning of their week. A full third of the globe does not use Daylight Savings Time. There are more than 24 time zones in the world, and those that recognize DST change their date offset twice a year. The date ‘12/31/2024’ in the US is ‘31/12/2024’ in Great Britain, ‘2024. 12.31.’ in Korea, and ‘٣١/١٢/٢٠٢٤’ in Arabic. And that is using the Gregorian calendar. Completely different in the Hebrew, Islamic or Assamese. Only 18 countries in the world use a 12 hour clock.
When developing calendars and date/time based applications the locale, calendar and timezone are critical points for properly handling dates and time.
- Dates, given to the TanStack Time API, should be in standard Internet Date Time Format RFC 3339 (sec 5.6) (string) format, epoch time (numeric), or already be a Date object.
- Format of dates/times are configurable to base standards set forth by the Intl.DateTimeFormat API. Formats are not designated by string tokens, but rather by parts, and displayed according to locale, calendar and timezone. This ensures consistent display of dates and times when switching between any of these three localization points.
- Until Temporal is ratified and implemented by all major browsers, TanStack Time will use the polyfill maintained by the TC39 champions of the Temporal API
- Date objects are mutable, therefore we must take care not to directly revise any Date passed into the API
- Public API methods should return either a Date object or a string. Temporal objects should never be returned by the public API methods
While most major databases and public API’s have the ability to return dates in specific formats, we must recognize that not all applications are properly storing date/time data in this fashion. As such, TanStack public APIs should provide an optional param allowing for an optional parser of strings into true Date objects. We should also provide a public API ‘output()’ method that would provide all date parts in specific formats, allowing a developer to transform a Date or RFC 3339 date/time string into a specific string format for those use cases. This can be easily done, leveraging the Intl.DateTimeFormat.prototype.formatToParts and Intl.DateTimeFormat.prototype.formatRangeToParts.
Dates as Date objects are extremely difficult to use in memoization methods, as you are most likely presented with a fresh object (new Date(value)) every time. While there are ways to hack around this, it is far easier to use simple values (strings or numbers). Remote API’s will always accept/return date time representations in either string or numeric format as well.
This is a short list of methods that the API should have
- parse() - Default parser used in the API for creating a Date object from an RFC 3339 date time string or other Date
- Formatters - creates format methods based on locale and timezone (can have multiple default, memoized formatters, as well as a method to build custom formatters)
- Math - add or subtract by specific parts of date time (i.e. Add 5 hours)
- Comparison - compare two values by specific parts (i.e. date 2 is greater than equal to date 1’s hour)
- Group - group sets of values by specific parts (i.e. group all provided dates by day)
- Range - create group ranges by specific parts (i.e. a Date for every 15 minutes for 8 hours)
- sort(dates) - sort a collection of dates
- Intersection - Does a date exist within a range? Given a start, end and range, do the start and/or end exist in the range?
- Start - get the start of date by part (i.e. date as beginning of day)
- End - get the end of date by part (i.e. date as end of specific hour)
- merge(a, b) - given date A and time B, apply time B to date A
- diff(a, b) - give the duration of difference between two by specific parts (i.e. how many minutes apart are these two dates?)
All public API methods should parse and validate date values. We accept RFC 3339 Internet Date Time strings, an epoch time (numeric), or Date objects. Each public API method should contain an ‘opts’ argument, with the ‘parser’ defaulted to our parser. This allows the developer to override with a custom parser, should they choose to do so. Custom format parsers are outside of the scope of TanStack Time.
The Intl.DateTimeFormat constructor is used to provide an object containing several methods for formatting date/time output. TanStack Time can provide memoized formatter methods based on calendar, locale and timezone of many basically recognized formats, as well as provide the ability to create a memoized custom format. One, this keeps the end developer from having to instantiate a new Intl.DateTimeFormat object every time they need to output a date. Two, by providing a prebuilt ‘formatThis()’ method the developer ensures that every time they use it the formatting is consistent within their application. If a value, passed to a formatter, is null, undefined, or an empty string, then we should return an empty string (formatting would imply a string). If the value, passed to a formatter, is an invalid date, then an error should be thrown.
Date math is difficult. The Temporal API makes it easier, but it can be much more complex than advertised.
One of the most difficult issues, when dealing with date time values, is accounting for timezones and DST offsets and, occasionally, the calendar to use. For this reason the majority of TanStack Time’s math API should utilize the Temporal.ZonedDateTime objects, defaulting the timezone and calendar to the browser’s current settings. We should allow the ability to override this, both in individual public API methods as well as at a broader level (“use these for every method call”).
While many Temporal math methods can take simple arguments to define duration adjustments, we will want to see impact in (rare) instances where duration is memoized. In those scenarios we should define Temporal.Duration instances, and output that duration in a ISO 8601 duration format (string). And, while possibly rare, our math API should also accept duration in this format as well.