# Difference between revisions of "Time"

(→Time) |
m (Fix a few minor errors) |
||

(11 intermediate revisions by 4 users not shown) | |||

Line 1: | Line 1: | ||

− | + | The canonical time library for Haskell is the [http://hackage.haskell.org/package/time time] package by Ashley Yakeley. There are several similar but different concepts associated with time and the time package keeps these different concepts separated by different data types with appropriate transformations between them to help keep your logic sound. However, if you are trying to use the wrong data type for the wrong task, you may find yourself frustrated with the time library. | |

− | + | To help avoid frustration, this guide will describe the various data types available in the time library and what temporal concepts they represent. This will help make sure you are using the right data type for the right task. The first few data types in this guide are not widely used in practice, but it is important to understand their associated concepts before moving onto the more complex, widely used, data types. | |

+ | |||

+ | = A time cheatsheet = | ||

+ | |||

+ | [[File:Time-diagram.png]] | ||

+ | |||

+ | Updates? Ask EvanR on #haskell. | ||

− | + | = Data Types for Measuring Time = | |

== AbsoluteTime == | == AbsoluteTime == | ||

Line 23: | Line 29: | ||

The problem with <tt>AbsoluteTime</tt> is that Earth's rotation rate is not constant. Over the long term, solar noon would drift over time. <tt>UniversalTime</tt> measures time by counting the number of "solar" rotations, including fractions of a rotation, of the earth since the MJD (Modifed Julian Date) epoch. | The problem with <tt>AbsoluteTime</tt> is that Earth's rotation rate is not constant. Over the long term, solar noon would drift over time. <tt>UniversalTime</tt> measures time by counting the number of "solar" rotations, including fractions of a rotation, of the earth since the MJD (Modifed Julian Date) epoch. | ||

− | The variations in the Earth's rotation means that a difference in <tt> | + | The variations in the Earth's rotation means that a difference in <tt>UniversalTime</tt> does not correspond to any particular amount of time. Your computer's hardware cannot track <tt>UniversalTime</tt> since it can only measure absolute durations. Converting between <tt>UniversalTime</tt> and <tt>AbsoluteTime</tt> would require a database build upon astronomical observations. No such conversion utilities are provided. |

<tt>UniversalTime</tt> is almost never used in practice. | <tt>UniversalTime</tt> is almost never used in practice. | ||

Line 29: | Line 35: | ||

== UTCTime == | == UTCTime == | ||

− | <tt>UTCTime</tt> measures "Coordinated Universal Time". One second of <tt>UTCTime</tt> is a constant amount of time equal to one second of <tt>AbsoluteTime</tt>. However, in order to keep "coordinated" with Universal Time, the number of seconds per day varies. Most days have 86400 seconds; however, on occasion a day will have a "leap second" | + | <tt>UTCTime</tt> measures "Coordinated Universal Time". One second of <tt>UTCTime</tt> is a constant amount of time equal to one second of <tt>AbsoluteTime</tt>. However, in order to keep "coordinated" with Universal Time, the number of seconds per day varies. Most days have 86400 seconds; however, on occasion a day will have a "leap second", meaning the day will consist of 86399 or 86401 seconds. <tt>UTCTime</tt> counts the number of days (each day consisting of between 86399-86401 seconds) since the MJD epoch plus an absolute time difference up to the number of seconds that occur in that day. |

It is unpredictable which days in the future will have a leap second. A leap second table is needed to convert between <tt>AbsoluteTime</tt> and <tt>UTCTime</tt>. This conversion can only be done reliably within recent history. | It is unpredictable which days in the future will have a leap second. A leap second table is needed to convert between <tt>AbsoluteTime</tt> and <tt>UTCTime</tt>. This conversion can only be done reliably within recent history. | ||

Line 43: | Line 49: | ||

<tt>NominalDiffTime</tt> is a member of the <tt>Num</tt> and <tt>Fractional</tt>, so can be generated by <tt>fromIntegral</tt> and <tt>realToFrac</tt>. <tt>NominalDiffTime</tt> is also a member of <tt>RealFrac</tt> and <tt>Real</tt> so <tt>round</tt> and <tt>realToFrac</tt> can operate on it. | <tt>NominalDiffTime</tt> is a member of the <tt>Num</tt> and <tt>Fractional</tt>, so can be generated by <tt>fromIntegral</tt> and <tt>realToFrac</tt>. <tt>NominalDiffTime</tt> is also a member of <tt>RealFrac</tt> and <tt>Real</tt> so <tt>round</tt> and <tt>realToFrac</tt> can operate on it. | ||

− | <tt>NominialDiffTime</tt> is regularly used. In fact, <tt>POSIXTime</tt> the <tt>NominalDiffTime</tt> since the UNIX epoch of Jan 1st, 1970, UTC. | + | <tt>NominialDiffTime</tt> is regularly used. In fact, <tt>POSIXTime</tt> is the <tt>NominalDiffTime</tt> since the UNIX epoch of Jan 1st, 1970, UTC. |

+ | |||

+ | = Data Types for Denoting Time = | ||

+ | |||

+ | So far our data have denoted occurrences of events by counting the number of days, or the number of seconds since some fixed reference epoch. However, we usually tell time by using years, months, day of month, hours, minutes and seconds. The following data types will assist with this more usual approach to telling time. | ||

== Day == | == Day == | ||

− | + | The <tt>Day</tt> data type is an abstract way of referring to a calendar date. Due to time zone issues, the <tt>Day</tt> type does not necessarily refer to any specific 1-day long absolute time period. | |

− | |||

− | The <tt>Day</tt> data type is an abstract way of referring to a | ||

The standard calender used to reference a date is the Gregorian calendar. The <tt>toGregorian</tt> and <tt>fromGregorian</tt> functions will construct and deconstruct a <tt>Day</tt> from the usual year-month-day format. There are other calendars, such as the Julian calendar, which are only used in unusual situations. | The standard calender used to reference a date is the Gregorian calendar. The <tt>toGregorian</tt> and <tt>fromGregorian</tt> functions will construct and deconstruct a <tt>Day</tt> from the usual year-month-day format. There are other calendars, such as the Julian calendar, which are only used in unusual situations. | ||

Line 55: | Line 63: | ||

== TimeOfDay == | == TimeOfDay == | ||

− | <tt>TimeOfDay</tt> represents "wall-clock" time. It consists of an hour of the day, a minute of the hour, and a second of the minute, including fractions of a second up to a | + | <tt>TimeOfDay</tt> represents "wall-clock" time. It consists of an hour of the day, a minute of the hour, and a second of the minute, including fractions of a second up to a resolution of 10^12. The seconds could go up to 61 in order to accommodate leap seconds. |

== LocalTime == | == LocalTime == | ||

Line 77: | Line 85: | ||

There are, of course, many different <tt>ZonedTime</tt> values corresponding to the same absolute, <tt>UTCTime</tt>. Given a <tt>TimeZone</tt> one can create a <tt>ZonedTime</tt> with that <tt>TimeZone</tt> by using the <tt>utcToZonedTime</tt> function. | There are, of course, many different <tt>ZonedTime</tt> values corresponding to the same absolute, <tt>UTCTime</tt>. Given a <tt>TimeZone</tt> one can create a <tt>ZonedTime</tt> with that <tt>TimeZone</tt> by using the <tt>utcToZonedTime</tt> function. | ||

− | <tt> | + | <tt>ZonedTime</tt> is a common stop when converting between common year-month-day-hour-minute-second human time format and absolute <tt>UTCTime</tt> time. |

== TimeZoneSeries == | == TimeZoneSeries == | ||

− | Almost nothing in the time library deals with regional time zones such as "Eastern Time" or "Pacific time" For that we need to reach out to the timezone-series and timezone-olson packages. The difficulty with regional time zones is, like leap seconds, that they are unpredictable and subject to be changed. | + | Almost nothing in the time library deals with regional time zones such as "Eastern Time" or "Pacific time". For that we need to reach out to the timezone-series and timezone-olson packages. The difficulty with regional time zones is, like leap seconds, that they are unpredictable and subject to be changed. |

A <tt>TimeZoneSeries</tt> is a series of <tt>TimeZone</tt>s, that is offsets from UTC, along with the absolute times where those <tt>TimeZone</tt>s are in effect. This is a suitable structure for defining a regional time zone. The functions in the timezone-olson package will allow you to create a <tt>TimeZoneSeries</tt> from an Olson time zone database file. | A <tt>TimeZoneSeries</tt> is a series of <tt>TimeZone</tt>s, that is offsets from UTC, along with the absolute times where those <tt>TimeZone</tt>s are in effect. This is a suitable structure for defining a regional time zone. The functions in the timezone-olson package will allow you to create a <tt>TimeZoneSeries</tt> from an Olson time zone database file. | ||

Line 87: | Line 95: | ||

== getTimeZone == | == getTimeZone == | ||

− | There | + | There is one set of functions in the time package that does deal with regional time zones. The <tt>getTimeZone</tt> function will compute the timezone for a given <tt>UTCTime</tt>, but only the local region as set by the <tt>TZ</tt> environment variable. The <tt>getCurrentTimeZone</tt>, <tt>getZonedTime</tt>, and <tt>utcToLocalZonedTime</tt> are functions derived from <tt>getTimeZone</tt>. |

− | There is no functionality in the time package that will convert from a local regional time to <tt>UTCTime</tt>, owing to the fact that the same local regional time may refer to multiple different <tt>UTCTime</tt> due to daylight savings times. <tt>localTimeToUTC'</tt> will do this conversion given a <tt>TimeZoneSeries</tt>. | + | There is no functionality in the time package that will convert from a local regional time to <tt>UTCTime</tt>, owing to the fact that the same local regional time may refer to multiple different <tt>UTCTime</tt> due to daylight savings times. <tt>localTimeToUTC'</tt> from the <tt>timezone-series</tt> packages will do this conversion given a <tt>TimeZoneSeries</tt>. |

## Latest revision as of 06:28, 8 October 2022

The canonical time library for Haskell is the time package by Ashley Yakeley. There are several similar but different concepts associated with time and the time package keeps these different concepts separated by different data types with appropriate transformations between them to help keep your logic sound. However, if you are trying to use the wrong data type for the wrong task, you may find yourself frustrated with the time library.

To help avoid frustration, this guide will describe the various data types available in the time library and what temporal concepts they represent. This will help make sure you are using the right data type for the right task. The first few data types in this guide are not widely used in practice, but it is important to understand their associated concepts before moving onto the more complex, widely used, data types.

## Contents

# A time cheatsheet

Updates? Ask EvanR on #haskell.

# Data Types for Measuring Time

## AbsoluteTime

`AbsoluteTime` is a data type is type for denoting when an event occurs. AbsoluteTime is a uniform time scale that simply counts the number of seconds since the TAI epoch. There are no leap seconds or timezones. You can compare two `AbsoluteTime`s directly to determine which one came first. You can reference time as far into the future or past as you like to within a resolution of 10^{-12} seconds.

Although `AbsoluteTime` is appropriate for many tasks, it is rarely used in applications and you will likely never use this data type. Unless you are a particularly pioneering person, you will probably want to use `UTCTime` which is more widely used in practice.

## DiffTime

`DiffTime` is used to represent an absolute duration of time measured in seconds. Subtracting two `AbsoluteTime`s with `diffAbsoluteTime` will produce a `DiffTime` and you can add a `DiffTime` to an `AbsoluteTime` to produce an offset `AbsoluteTime`.

`DiffTime` is a member of the `Num` and `Fractional`, so can be generated by `fromIntegral` and `realToFrac`. `DiffTime` is also a member of `RealFrac` and `Real` so `round` and `realToFrac` can operate on it.

`DiffTime` is appropriate in situations where precision time differences are required; however `NominalDiffTime` is more often used in practice.

## UniversalTime

The problem with `AbsoluteTime` is that Earth's rotation rate is not constant. Over the long term, solar noon would drift over time. `UniversalTime` measures time by counting the number of "solar" rotations, including fractions of a rotation, of the earth since the MJD (Modifed Julian Date) epoch.

The variations in the Earth's rotation means that a difference in `UniversalTime` does not correspond to any particular amount of time. Your computer's hardware cannot track `UniversalTime` since it can only measure absolute durations. Converting between `UniversalTime` and `AbsoluteTime` would require a database build upon astronomical observations. No such conversion utilities are provided.

`UniversalTime` is almost never used in practice.

## UTCTime

`UTCTime` measures "Coordinated Universal Time". One second of `UTCTime` is a constant amount of time equal to one second of `AbsoluteTime`. However, in order to keep "coordinated" with Universal Time, the number of seconds per day varies. Most days have 86400 seconds; however, on occasion a day will have a "leap second", meaning the day will consist of 86399 or 86401 seconds. `UTCTime` counts the number of days (each day consisting of between 86399-86401 seconds) since the MJD epoch plus an absolute time difference up to the number of seconds that occur in that day.

It is unpredictable which days in the future will have a leap second. A leap second table is needed to convert between `AbsoluteTime` and `UTCTime`. This conversion can only be done reliably within recent history.

`UTCTime` is the most popular way of referencing events in absolute time. Events occurring hundreds of years ago can be referenced with moderate precision because we may know approximately when during a particular day they occurred, even though we do not know the `AbsoluteTime` that they occurred (because we do not know the detailed historical changes in the rotation rate of the Earth). Events occurring in the recent past can be converted to `AbsoluteTime` with a leap second table. Future events are usually set relative to the day in which they are to occur, so the fact that the absolute time that they will occur at (that is exact the number seconds into the future that they will occur) is unknown is often not a problem.

The major disadvantage with `UTCTime` is that one cannot compute the absolute difference between two `UTCTime`s without first converting them to `AbsoluteTime`, and that requires an accurate leap second tables. To circumvent this issue, one uses `NominalDiffTime`.

## NominalDiffTime

The `diffUTCTime` function computes a `NominalDiffTime` between two `UTCTimes` which is the number of seconds that would have occurred between two `UTCTime`s if no leap seconds had occurred between the two events. Therefore the `diffUTCTime` is not suitable for measuring the absolute time between two events to a high degree of precession. However a `NominalDiffTime` is often suitable for advancing an event by a week, a day, or an hour, since one would normally want said event to occur at the same time of day, or same minute of the hour, regardless of whether or not a leap second happens to be inserted in that duration.

`NominalDiffTime` is a member of the `Num` and `Fractional`, so can be generated by `fromIntegral` and `realToFrac`. `NominalDiffTime` is also a member of `RealFrac` and `Real` so `round` and `realToFrac` can operate on it.

`NominialDiffTime` is regularly used. In fact, `POSIXTime` is the `NominalDiffTime` since the UNIX epoch of Jan 1st, 1970, UTC.

# Data Types for Denoting Time

So far our data have denoted occurrences of events by counting the number of days, or the number of seconds since some fixed reference epoch. However, we usually tell time by using years, months, day of month, hours, minutes and seconds. The following data types will assist with this more usual approach to telling time.

## Day

The `Day` data type is an abstract way of referring to a calendar date. Due to time zone issues, the `Day` type does not necessarily refer to any specific 1-day long absolute time period.

The standard calender used to reference a date is the Gregorian calendar. The `toGregorian` and `fromGregorian` functions will construct and deconstruct a `Day` from the usual year-month-day format. There are other calendars, such as the Julian calendar, which are only used in unusual situations.

## TimeOfDay

`TimeOfDay` represents "wall-clock" time. It consists of an hour of the day, a minute of the hour, and a second of the minute, including fractions of a second up to a resolution of 10^12. The seconds could go up to 61 in order to accommodate leap seconds.

## LocalTime

A `Day` with a `TimeOfDay` forms a `LocalTime`. This is contains the data that we normally denote time with: a year, month, day, hour, minute, and second. However, it does not denote an absolute time because it contains no time zone information.

`LocalTime` is used represent date and time for printing and parsing; however it should be avoided for computation. The biggest problem with `LocalTime` is that, due to daylight savings time, regularly the same time of day is repeated twice a day. This means you cannot even use `LocalTime`s to reliably determine the relative order of events.

## TimeZone

Somewhat confusingly named, a `TimeZone` represents an offset from UTC measured in minutes. It also contains ancillary data such as the timezone name and whether or not it is intended to be used for summer time only. Eastern Standard Time (EST) or Pacific Daylight Time (PDT) are examples of timezone that could be represented by a `TimeZone` value.

It does not represent the time zone of a region, such as Eastern Time, because regions often vary their offsets from UTC depending on the time of year.

The `utc` value is the value of the `TimeZone` with no offset from UTC.

## ZonedTime

A `ZonedTime` is a `LocalTime` together with a `TimeZone`. This does refer to a specific event in absolute time and can be converted to a `UTCTime` with the `zoneTimeToUTC` function, and could be further converted into a `AbsoluteTime` if one has a leap second table.

There are, of course, many different `ZonedTime` values corresponding to the same absolute, `UTCTime`. Given a `TimeZone` one can create a `ZonedTime` with that `TimeZone` by using the `utcToZonedTime` function.

`ZonedTime` is a common stop when converting between common year-month-day-hour-minute-second human time format and absolute `UTCTime` time.

## TimeZoneSeries

Almost nothing in the time library deals with regional time zones such as "Eastern Time" or "Pacific time". For that we need to reach out to the timezone-series and timezone-olson packages. The difficulty with regional time zones is, like leap seconds, that they are unpredictable and subject to be changed.

A `TimeZoneSeries` is a series of `TimeZone`s, that is offsets from UTC, along with the absolute times where those `TimeZone`s are in effect. This is a suitable structure for defining a regional time zone. The functions in the timezone-olson package will allow you to create a `TimeZoneSeries` from an Olson time zone database file.

## getTimeZone

There is one set of functions in the time package that does deal with regional time zones. The `getTimeZone` function will compute the timezone for a given `UTCTime`, but only the local region as set by the `TZ` environment variable. The `getCurrentTimeZone`, `getZonedTime`, and `utcToLocalZonedTime` are functions derived from `getTimeZone`.

There is no functionality in the time package that will convert from a local regional time to `UTCTime`, owing to the fact that the same local regional time may refer to multiple different `UTCTime` due to daylight savings times. `localTimeToUTC'` from the `timezone-series` packages will do this conversion given a `TimeZoneSeries`.