Dates and Times

Overview

Cocoa's time libraries have some serious problems. To begin with, several components have names that don't accurately describe what they do. NSDate holds timestamps, not dates. NSDateComponents holds date information, but also time information. It's unnecessarily cumbersome to get local date information, requiring multiple statements and the creation of multiple intermediary objects. It has no proper semantic representation of time intervals; the NSTimeInterval holds everything in seconds, which makes it impossible to represent a time like "one month" and have it increment dates with the proper local rules. To solve these problems, Tailor has its own date and time library.

Timestamps

The core type in Tailor's time library is a Timestamp. This type represents a moment in time. It provides two equivalent representations of the moment in time. The absolute representation, called epochSeconds, is a floating-point number giving the number of seconds since 1970-01-01 00:00:00 UTC. The local representation gives the year, month, day, hour, minute, second, and nanosecond, in a given time zone and calendar system. Every timestamp has both representations, which means every time zone must have a calendar and time zone. These details default to the system calendar and time zone. You can create a time zone by providing either the absolute representation or the local representation.

The Timestamp type also provides quick methods for changing components of the time. The inTimeZone and inCalendar methods convert the timestamp into a different time zone or calendar system. The absolute representation will remain the same, but the local representation will be recalculated with the new time zone and calendar.

There is also a change method which produces a new timestamp with some of the parts of the local representation changed. For instance, if you have a timestamp for 2015-08-21 11:48:00 UTC, and you call timestamp.change(month: 7, day: 19), it will produce a timestamp for 2015-07-19 11:48:00 UTC. Any values you don't specify in the call to change will be left as they are.

Time Interval

Tailor also provides a TimeInterval type for describing a range of time. It has fields for all of the local time components: years, months, days, hours, minutes, seconds, nanoseconds. You can add time intervals to timestamps to get new timestamps; it will change the local representation by the amounts in the time interval and recompute the absolute timestamp. You can also have values in time intervals that exceed the normal bounds for that local value, so a time interval of 48 hours is equivalent to a time interval of two days.

There are shorthands for getting time intervals for simple values: the Int type has methods called years, months, days, and so on that create a time interval with that number of years, months, or days. You can also add intervals together, so you can have expressions like 2.hours + 30.minutes that will create a time interval of two hours and thirty minutes. There are also shorthands on time intervals for adding a time interval to the current time: 2.hours.fromNow gets a timestamp two hours in the future, whereas 2.hours.ago gets a timestamp two hours in the past.

Dates and Times

If you want to model a field that only has some of the local time components, you can use the Date and Time types. The Date type has a year, month, day, and calendar. It also has helper methods beginningOfDay and endOfDay for getting the first and last timestamp in that date. There is also a shorthand Date.today() for getting the current date. The Time type has an hour, minute, second, nanosecond, and time zone.

Time Formatting

You can format time with a TimeFormat. A time format specifies a way of turning a timestamp into a string, or getting a timestamp from a string. It's composed of a sequence of TimeFormatComponents, which represent a single part of the time format, like a year, an hour, a time zone, or a string literal. The time format components also have formatting options, like whether to pad with zeros or to use an abbreviated name. You should check out the TimeFormatComponent documentation to see all of the available options. You can create a TimeFormat by providing the time format components in a list, or strings for the string literal components. For instance, to format a timestamp in ISO 8601 format, you would create a time format like this: TimeFormat(.Year, "-", .Month, "-", .Day, " ", .Hour, ":", .Minute, ":", .Second, " ", .TimeZone). There are also some built-in time formats that you can access as static fields on the TimeFormat type, which you can see in the documentation. Those built-in formats also provide interesting examples of the different options for creating time formats.

To format a timestamp with a timestamp, you can either call timeFormat.formatTime(timestamp), or timestamp.format(timeFormat). If you want to create a timestamp from a formatted string, you can call timeFormat.parseTime(string). If the string is not properly formatted for the given format, this will return nil.