HORIZON HASKELLDocslts/ghc-10.0.xfc4e5982026-10-11Search names, modules, packages, or :: a typeCtrl K

GHC 10.0.0.20260917 · lts/ghc-10.0.x · fc4e598 · 2026-10-11

Modulethyme-0.4.1Haskell2010

Data.Thyme.Clock.TAI

International Atomic Time (TAI) and conversion to/from UTC, accounting for leap seconds.

  • 3 types
  • 11 values
  • Packagethyme-0.4.1
  • Exports14
  • LanguageHaskell2010
  • LicenceBSD-3-Clause
  • SourceTAI.hs
newtypenewtype AbsoluteTime
#

Temps Atomique International (TAI). Note that for most applications UTCTime is perfectly sufficient, and much more convenient to use.

Internally this is the number of seconds since taiEpoch. TAI days are exactly 86400 SI seconds long.

Instances24Bounded, Enum, Eq, Data, Ord, Show, …
datadata TAIUTCMap
#

A table of TAIUTCRows for converting between TAI and UTC.

The two Maps are keyed on the corresponding instants in UTC and TAI from which the TAIUTCRow becomes applicable. The UTCTime key of the first Map is always at midnight.

No table is provided here because leap seconds are unpredictable, and any program shipped with such a table could become out-of-date in as little as 6 months. See parseTAIUTCDAT for details.

Instances6Eq, Data, Ord, Show, Generic, Rep
datadata TAIUTCRow
#

Each line of tai-utc.dat (see parseTAIUTCDAT) specifies the difference between TAI and UTC for a particular period. For example:

1968 FEB  1 =JD 2439887.5  TAI-UTC=   4.2131700 S + (MJD - 39126.) X 0.002592 S

says that from 1968-02-01 00:00:00 (Julian Date 2439887.5; or Modified Julian Date 39887.0), the difference between TAI and UTC is 4.2131700s (the additive part) plus a scaled component that increases for each day beyond MJD 39126 (the base) by 0.002592s (the coefficient). In general, the latter half of each line is of the form:

TAI-UTC= additive S + (MJD - base) X coefficient S

TAIUTCRow a b c is a normalised version of the above, with the base multiplied by 86400s, and the coefficient divided by the same. This allows us to use the internal representation of UTCTime—seconds since the MJD epoch—as the MJD term without further rescaling.

Note that between 1961-01-01 and 1972-01-01, each UTC second was actually slightly longer than one TAI (or SI) second. For the first year this was at the rate of exactly 1.000000015 TAI (or SI) seconds per UTC second, but was subject to irregular updates. Since leap seconds came into effect on 1972-01-01, the additive part has always been an intergral number of seconds, and the coefficient has always been zero.

To convert between TAI and UTC, we refer to the definition:

TAI - UTC = a + (MJD - b) * c

Using UTC for MJD (with b and c scaled as described above):

TAI = UTC + a + (UTC - b) * c
TAI - a + b * c = UTC + UTC * c
(TAI - a + b * c) / (1 + c) = UTC

This is implemented by absoluteTime and absoluteTime'.

Further reading:

Constructors

  • TAIUTCRow !DiffTime !UTCTime !Rational

    Each row comprises of an additive component, the base of the scaled component, and the coefficient of the scaled component.

Instances6Eq, Data, Ord, Show, Generic, Rep

Convert between UTCTime and AbsoluteTime using a TAIUTCMap.

Since UTCTime cannot represent a time-of-day of 86400s or more, any conversion from AbsoluteTime that happens to be during a leap second will overflow into the next day.

See parseTAIUTCDAT for how to obtain the tum :: TAIUTCMap below.

> let jul1 = utcTime # UTCView (gregorian # YearMonthDay 2015 7 1) zeroV
> jul1 & absoluteTime tum %~ (.-^ fromSeconds 1.1)
2015-06-30 23:59:59.9 UTC

Convert between UTCView and TAI AbsoluteTime using a TAIUTCMap.

Unlike absoluteTime, UTCView can represent a time-of-day greater than 86400s, and this gives the correct results during a leap second.

See parseTAIUTCDAT for how to obtain the tum :: TAIUTCMap below.

> let jul1 = UTCView (gregorian # YearMonthDay 2015 7 1) zeroV
> jul1 & absoluteTime' tum %~ (.-^ fromSeconds 0.1)
UTCView {utcvDay = 2015-06-30, utcvDayTime = 86400.9s}

However keep in mind that currently there is no standard way to get the TAI on most platforms. Simply converting the result of getCurrentTime (which calls gettimeofday(2)) to AbsoluteTime during a leap second will still give non-monotonic times.

Compatibility

4 declarations