The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Time::UTC - manipulation of UTC in terms of TAI

SYNOPSIS

        use Time::UTC qw(
                utc_start_segment
                foreach_utc_segment_when_complete
                utc_start_tai_instant utc_start_utc_day
                utc_segment_of_tai_instant utc_segment_of_utc_day
        );

        $seg = utc_start_segment;
        foreach_utc_segment_when_complete { ... $_[0] ... };

        $instant = utc_start_tai_instant;
        $day = utc_start_utc_day;

        $seg = utc_segment_of_tai_instant($instant);
        $seg = utc_segment_of_utc_day($day);

        use Time::UTC qw(
                utc_day_leap_seconds utc_day_seconds
                utc_check_instant
        );

        $secs = utc_day_leap_seconds($day);
        $secs = utc_day_seconds($day);
        utc_check_instant($day, $secs);

        use Time::UTC qw(tai_to_utc utc_to_tai);

        ($day, $secs) = tai_to_utc($instant);
        $instant = utc_to_tai($day, $secs);

        use Time::UTC qw(
                utc_secs_to_hms utc_hms_to_secs
                utc_day_to_ymd utc_ymd_to_day
                utc_instant_to_ymdhms utc_ymdhms_to_instant
        );

        ($hr, $mi, $sc) = utc_secs_to_hms($secs);
        $secs = utc_hms_to_secs($hr, $mi, $sc);

        ($yr, $mo, $dy) = utc_day_to_ymd($day);
        $day = utc_ymd_to_day($yr, $mo, $dy);

        ($yr, $mo, $dy, $hr, $mi, $sc) =
                utc_instant_to_ymdhms($day, $secs);
        ($day, $secs) = utc_ymdhms_to_instant(
                                $yr, $mo, $dy, $hr, $mi, $sc);

        use Time::UTC qw(
                utc_day_to_mjdn utc_mjdn_to_day
                utc_day_to_cjdn utc_cjdn_to_day
        );

        $mjdn = utc_day_to_mjdn($day);
        $day = utc_mjdn_to_day($mjdn);

        $cjdn = utc_day_to_cjdn($day);
        $day = utc_cjdn_to_day($cjdn);

DESCRIPTION

This module encapsulates knowledge about the structure of the UTC time scale, including the leap seconds of the current incarnation. This information is useful in manipulating times stored in a UTC-based format, or in converting between UTC and TAI (the underlying atomic time scale). It automatically downloads new UTC data as required to keep up to date. This is a low-level module, intended for use by other modules that need to know about UTC. This module aims to be comprehensive and rigorous.

HISTORY OF UTC

Until the middle of the twentieth century, the passage of time was measured primarily against the astronomical motions of the Earth and other bodies. These motions are very regular, and indeed were the most temporally regular phenomena available to pre-industrial society. After the invention of the caesium-based atomic clock, a gradual transition from astronomic to atomic timekeeping began. The hyperfine transition of caesium is more regular than the Earth's motion, and so makes a better time standard. Unfortunately, this means that during the transition phase there are two disagreeing time standards in use, and we must jump through hoops to accommodate both. UTC is one of these hoops.

Solar timekeeping

Each revolution of the Earth relative to the Sun (i.e., each day) has traditionally been divided into units of hours, minutes, and seconds. These are defined such that there are exactly 86400 seconds in a day. Since these units are measuring the rotation of the Earth, rather than the passage of time per se, it makes more sense to view these as measures of angle than of time. Thus, the hour refers to a rotation of exactly 15 degrees, regardless of how much time that rotation takes, and so on.

Because the Earth's rotation is non-uniform, each day is a slightly different length, and so the duration of the second, as defined above, also varies over time. This is not good in a time standard. In order to make the time as stable as possible, the non-uniformities of the Earth's rotation need to be accounted for. The use of mean solar time rather than apparent solar time smooths out variation in the apparent daily motion of the Sun over the course of the year that are due to the non-circularity of the Earth's orbit. The mean solar time at Greenwich is known as Universal Time, and specifically as UT1. UT2, UT1R, and UT2R are smoothed versions of Universal Time, removing periodic seasonal and tidal variations.

But however smoothed these scales get, they remain fundamentally measures of angle rather than time. They are not uniform over time.

Atomic timekeeping

It has been long recognised that the Earth's rotation is non-uniform, and so that the scales based on the Earth's rotation are not stable measures of time. Scientists have therefore defined units of time that are unrelated to the Earth's current motions. Confusingly, the unit so defined is called the "second", and is arranged to have a duration similar to that of the traditional angle-based second, despite being fundamentally different in intent.

The second in this sense was originally defined as 1/86400 of the mean duration of a solar day. In 1956 the second was redefined in terms of the length of the tropical year 1900 (the "ephemeris second"), in recognition of the non-uniformity of the Earth's rotation. This definition was superseded in 1967 by a definition based on the hyperfine transition of caesium, following a decade of experience with early caesium clocks. That definition was refined in 1997, and further refinements may happen in the future.

The important aspects of atomic timekeeping, for our purposes, are that it is more stable than the Earth's spin; it is independent of the Earth's current spin; and it confusingly uses much of the same terminology as measurement of the Earth's spin.

TAI

Time started to be measured using atomic clocks in 1955, and the first formal atomic time scale started at the beginning of 1958. In 1961 an international effort constructed a new time scale, synchronised with the first one, which eventually (in 1971) came to be known as International Atomic Time or TAI. TAI is strictly a measure of time as determined by atomic clocks, and is entirely independent of the Earth's daily revolutions. However, it uses the terminology and superficial appearance of the time scales that went before it, which is to say the angle scales. Thus a point on the TAI scale is conventionally referred to by specifying a date and a time of day, the latter composed of hours, minutes, and seconds.

Like the pure measures of rotation, TAI has exactly 86400 seconds per day. Completely unlike those measures, TAI's seconds are, as far as possible, of identical duration, the duration with which the second was defined in 1967. TAI, through its predecessor atomic time scale, was initially synchronised with Universal Time, so that TAI and UT2 describe the same instant as 1958-01-01T00:00:00.0 (at least, according to the United States Naval Observatory's determination of UT2). TAI now runs independently of UT, and at the time of writing (early 2005) TAI is about 32.5 seconds ahead of UT1.

UTC

Over the long term, the world is switching from basing civil time on UT1 (i.e., the revolution of the Earth) to basing civil time on TAI (i.e., atomic clocks). In the short term, however, a clean switch is not such a popular idea. There is a demand for a hybrid system which is based on atomic clocks but which also maintains synchronisation with the Earth's spin. UTC is that system.

UTC is defined in terms of TAI, and is in that sense an atomic time standard. However, the relation between UTC and TAI is determined only a few months in advance. The relation changes over time, so that UTC remains an approximation of UT1.

This concept behind UTC originates with the WWV radio time signal station in the USA. Up until 1956 it had, like all time signal stations at the time, transmitted the closest achievable approximation of UT1. In 1956, with atomic clocks now available, the National Bureau of Standards started to base WWV's signals on atomic frequency standards. Rather than continuously adjust the frequency to track UT1, as had been previously done, they set the frequency once to match the rate of UT1 and then let it diverge by accurately maintaining the same frequency. When the divergence grew too large, the time signals were stepped by 20 ms at a time to keep the magnitude of the difference within chosen limits.

This new system, deliberately accepting a visible difference between signalled time and astronomical time, was initially controversial, but soon caught on. Other time signal stations operated by other bodies, such as the National Physical Laboratory in the UK, started to use the same type of scheme. This raised the problem of keeping the time signals synchronised, so international agreement became necessary.

In 1960, with the frequency of the caesium hyperfine transition now established (though it did not become the SI standard until 1967), a frequency offset for time signals was internationally agreed, chosen to match the then-current rate of UT2. It was decided that the International Time Bureau (BIH, Bureau International de l'Heure) would henceforth determine what frequency offset to use, changing it if necessary at each year end, and also coordinate the necessary time steps to closely approximate UT2. Thus was international synchronisation of time signals achieved.

From the beginning of 1961 this system was formalised as Coordinated Universal Time (UTC). Time steps, both forward and backward, were always introduced at midnight, achieved by making a UTC day have a length other than 86400 UTC seconds. The time steps of 20 ms having been found to be inconveniently frequent, it was decided to use steps of 50 ms instead. This was soon increased to 100 ms. This arrangement lasted until the end of 1971.

The frequency offsets, which when correctly chosen avoided the need for many time steps, were found to be inconvenient. Radio time signals commonly provided per-second pulses that were phase-locked to the carrier signal, and maintaining that relation meant that the frequency offset to make atomic time match UT2 had to be applied to the carrier frequency also. This made the carrier unreliable as a frequency standard, which was a secondary use made of it.

To maintain the utility of time signals as frequency standards, from the beginning of 1972 the frequency offset was permanently set to zero. Henceforth the UTC second is identical in duration to the TAI second. The size of the time steps was increased again, to one second, to make the steps less frequent and to avoid phase shifts in per-second pulse signals. An irregular time step was used to bring UTC to an integral number of seconds offset from TAI, where it henceforth remains.

Because of the zero frequency offset, the new form of UTC has only had backward jumps (by having an 86401 s UTC day). Forward jumps are also theoretically possible, but unlikely to ever occur.

Notice that the new form of UTC is more similar to TAI than the old form was. This appears to be part of the gradual switch from solar time to atomic time. It has been proposed (controversially) that in the near future the system of irregularities in UTC will terminate, resulting in a purely atomic time scale.

STRUCTURE OF UTC

UTC is a time scale derived from TAI. UTC divides time up into days, and each day into seconds. Most UTC days are exactly 86400 UTC seconds long, but they can be up to a second shorter or longer. The UTC second is in general slightly different from the TAI second; it stays stable most of the time, occasionally undergoing an instantaneous change. Since 1972 the UTC second has been equal to the TAI second, and it will remain so in the future.

The details of the day lengths, and until 1972 the length of the UTC second, are published by the International Earth Rotation and Reference Systems Service (IERS). They are announced only a few months in advance, so it is not possible to convert between TAI and UTC for times more than a few months ahead.

UTC is not defined for dates prior to 1961.

FUNCTIONS

Because UTC is defined purely in terms of TAI, these interfaces make frequent use of the TAI epoch, 1958-01-01T00:00:00.0. Instants on the TAI scale are identified by a scalar number of TAI seconds since the TAI epoch; this is a perfectly linear scale with no discontinuities. The TAI seconds count can be trivially split into the conventional units of days, hours, minutes, and seconds for display (TAI days contain exactly 86400 TAI seconds each).

Because UTC days have differing lengths, instants on the UTC scale are identified by the combination of an integral number of days since the TAI epoch and a number of UTC seconds since midnight within the day. In some interfaces the day number is used alone. The conversion of the number of seconds within a day into hours, minutes, and seconds for display is idiosyncratic; the function utc_secs_to_hms handles this.

All numbers in this API are Math::BigRat objects. All numeric function arguments must be Math::BigRats, and all numeric values returned are likewise Math::BigRats.

Description of UTC

This module contains a machine-manipulable description of the relation between UTC and TAI. Most users of this module do not need to examine this directly, and will be better served by the higher-level functions described later. However, users with unusual requirements have access to the description if necessary. The functions in this section deal with this.

The internal description is composed of Time::UTC::Segment objects. Each segment object describes a period of time over which the relation between UTC and TAI is stable. See Time::UTC::Segment for details of how to use these objects. More segments can appear during the course of a program's execution: updated UTC data is automatically downloaded as required.

utc_start_segment

Returns the first segment of the UTC description. The later segments can be accessed from the first one. This function is intended for programs that will walk through the entire description.

foreach_utc_segment_when_complete(WHAT)
foreach_utc_segment_when_complete BLOCK

WHAT must be a reference to a function which takes one argument; it may be specified as a bare BLOCK in the function call. The function is called for each segment of the UTC description in turn, passing the segment as an argument to the function. This call takes place, for each segment, when it is complete, as described in Time::UTC::Segment. The function is immediately called for already-complete segments.

To do this for only one segment, see the when_complete method on Time::UTC::Segment.

utc_start_tai_instant

Identifies the instant at which the UTC service started. This instant was the start of the first UTC day.

utc_start_utc_day

Identifies the first day of UTC service.

utc_segment_of_tai_instant(INSTANT)

Returns the segment of the UTC description that pertains to the specified TAI instant. dies if the specified instant precedes the start of UTC or if the relevant segment hasn't been defined yet.

utc_segment_of_utc_day(DAY)

Returns the segment of the UTC description that pertains to the specified day number. dies if the specified day precedes the start of UTC or if the relevant segment hasn't been defined yet.

Shape of UTC

utc_day_leap_seconds(DAY)

Returns the number of extra UTC seconds inserted at the end of the day specified by number. The number is returned as a Math::BigRat and may be negative. dies if the specified day precedes the start of UTC or if UTC for the day has not yet been defined.

utc_day_seconds(DAY)

Returns the length, in UTC seconds, of the day specified by number. The number is returned as a Math::BigRat. dies if the specified day precedes the start of UTC or if UTC for the day has not yet been defined.

utc_check_instant(DAY, SECS)

Checks that a day/seconds combination is valid. dies if UTC is not defined for the specified day or if the number of seconds is out of range for that day.

Conversion between UTC and TAI

tai_to_utc(INSTANT)

Translates a TAI instant into UTC. The function returns a list of two values: the integral number of days since the TAI epoch and the number of UTC seconds within the day. dies if the specified instant precedes the start of UTC or if UTC is not yet defined for the instant.

utc_to_tai(DAY, SECS)

Translates a UTC instant into TAI. dies if the specified instant precedes the start of UTC or if UTC is not yet defined for the instant, or if the number of seconds is out of range for the day.

Display formatting

utc_secs_to_hms(SECS)

When a UTC day is longer than 86400 seconds, it is divided into hours and minutes in an idiosyncratic manner. Rather than times more than 86400 seconds after midnight being displayed as 24 hours and a fraction of a second, they are displayed as 23 hours, 59 minutes, and more than 60 seconds. This means that each UTC day contains the usual 1440 minutes; where leap seconds occur, the last minute of the day has a non-standard length. This arrangement is essential to make timezones work with UTC.

This function takes a number of seconds since midnight and returns a list of hours, minutes, and seconds values, in the UTC manner. It dies if given a negative number of seconds. It places no upper limit on the number of seconds, because the length of UTC days varies.

utc_hms_to_secs(HR, MI, SC)

This performs the reverse of the translation that utc_secs_to_hms does. It takes numbers of hours, minutes, and seconds, and returns the number of seconds since midnight. It dies if the numbers provided are invalid. It does not impose an upper limit on the time that may be specified, because the length of UTC days varies.

utc_day_to_ymd(DAY)

Although UTC is compatible with any means of labelling days, and the scalar day numbering used in this API can be readily converted into whatever form is required, it is conventional to label UTC days using the Gregorian calendar. Even when using some other calendar, the Gregorian calendar may be a convenient intermediate form, because of its prevalence.

This function takes a number of days since the TAI epoch and returns a list of a year, month, and day, in the Gregorian calendar. It places no bounds on the permissible day numbers; it is not limited to days for which UTC is defined. All year numbers generated are in the Common Era, and may be zero or negative if a sufficiently negative day number is supplied.

utc_ymd_to_day(YR, MO, DY)

This performs the reverse of the translation that utc_day_to_ymd does. It takes year, month, and day numbers, and returns the number of days since the TAI epoch. It dies if the numbers provided are invalid. It does not impose any limit on the range of years.

utc_instant_to_ymdhms(DAY, SECS)
utc_ymdhms_to_instant(YR, MO, DY, HR, MI, SC)

As a convenience, these two functions package together the corresponding pairs of display formatting functions described above. They do nothing extra that the underlying functions do not; they do not check that the instant specified is actually a valid UTC time.

Calendar conversion

utc_day_to_mjdn(DAY)

This function takes a number of days since the TAI epoch and returns the corresponding Modified Julian Day Number (a number of days since 1858-11-17 UT). MJDN is a standard numbering for days in Universal Time. There is no bound on the permissible day numbers; the function is not limited to days for which UTC is defined.

utc_mjdn_to_day(MJDN)

This performs the reverse of the translation that utc_day_to_mjdn does. It takes a Modified Julian Day Number and returns the number of days since the TAI epoch. It does not impose any limit on the range.

utc_day_to_cjdn(DAY)

This function takes a number of days since the TAI epoch and returns the corresponding Chronological Julian Day Number (a number of days since -4713-11-24). CJDN is a standard day numbering that is useful as an interchange format between implementations of different calendars. There is no bound on the permissible day numbers; the function is not limited to days for which UTC is defined.

utc_cjdn_to_day(CJDN)

This performs the reverse of the translation that utc_day_to_cjdn does. It takes a Chronological Julian Day Number and returns the number of days since the TAI epoch. It does not impose any limit on the range.

SEE ALSO

Date::ISO8601, Date::JD, DateTime, Time::UTC::Now, Time::UTC::Segment, Time::TAI

AUTHOR

Andrew Main (Zefram) <zefram@fysh.org>

COPYRIGHT

Copyright (C) 2005, 2006, 2007, 2009, 2010 Andrew Main (Zefram) <zefram@fysh.org>

LICENSE

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.