tai

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 8, 2021 License: MIT Imports: 6 Imported by: 7

README

tai

Package tai provides support for International Atomic Time.

Usage

TAI values may be created directly,

t := tai.TAI{Sec: 123456789, Asec: 300 * tai.Millisecond}

To break a TAI value into is constituent Calendar parts:

g := t.AsGregorian()
// g.Year
// g.Month
// g.Day
// g.Hour
// g.Minute
// g.Sec

Gregorian is the calendar system used by most of the world.

Formatting

fmt.Println(tai.Now().Format(tai.RFC3339Micro)
// 2021-09-03T22:03:56.991894Z

Stdlib compatibility

Convert to and from stdlib time values

tai.FromTime(t.AsTime())

Compatible with the same UNIX notation as stdlib,

tai.Unix(secs, nsecs).Unix() // back to sec/nsec

More

See pkg.go.dev.

FAQ

  1. Why would I want to use this?

If you deal with the TAI timekeeping system, there are not (as of late 2021) any alternative packages for Go. TAI time is continuous and never repeats, properties that the Universal Time system used by most computers do not have.

  1. Why not stdlib time?

An alternative implementation of this package would utilize the stdlib time package and skew the values by the relevant number of leapseconds. However, stdlib time has a more finite range compared to the 292 billion years of pkg tai.

Additionally, support for non-UTC timezones is needless complexity, as TAI time only exists in the UTC timezone. As well, the nanosecond resolution of stdlib time is restrictive for some applications interested in continuous time systems, for which the attosecond resolution of this package is a tremendous improvement.

  1. Is the package threadsafe?

Yes. The leapsecond table is protected by a RWMutex. This limits ultimate concurrent performance when converting to/from stdlib Time values. Nothing else in the package is sensitive to concurrent execution.

  1. Why use global state for the leapsecond table?

The only significant benefit to a non-global leapsecond table is the ability to use sharding to improve performance. The performance of this package is sufficient that additional speed would likely not be a significant gain to any program. A non-global leapsecond table must be duplicated N times over which creates an enhanced opportunity for inconsistent state and incorrect programs.

  1. Will there be more features to mimic a larger portion of the time package?

The time package is privileged with runtime hooks for some of its features, e.g. timers. The stdlib has a greater variety of serialization options for e.g. non-allocating approaches. If desired, please implement them with a pull request.

The various methods for accessing parts of a time's representation such as the weekday may be added at a later date. It is unclear how valuable these are when AsGreg exists.

  1. How correct and bug free is this package?

There is a fairly complete set of unit tests that lead to the author's confidence that the package works properly. If you find a bug, please make an issue so it can be fixed. The "Hammer" test ensures that all dates from the Julian epoch (-4716) to the year 10,000 are understood correctly. This is over 5.7 million test cases. An additional million fuzz test cases are run, as well as specific tests for interesting or key moments.

  1. Why have a format syntax that is incompatible with Stdlib time?

Stdlib time is the ugly duckling in this respect. pkg tai is more in keeping with other similar tools.

  1. How stable is this package? Why isn't it 1.0?

The core mechanics or algorithms of this package are stable. The Tai/Gregorian interfaces will not see a reduction in the scope of public methods. Some more internal functions for Calendric calculations may be made private in the future, which would present a breaking change. V1.0 is deferred for this reason.

Documentation

Overview

Package tai provides functionality for International Atomic Time (TAI).

Index

Constants

View Source
const (
	January = iota + 1
	February
	March
	April
	May
	June
	July
	August
	September
	October
	November
	December
)
View Source
const (
	Monday = iota
	Tuesday
	Wednesday
	Thursday
	Friday
	Saturday
	Sunday
)
View Source
const (
	RFC3339      = "%Y-%m-%dT%H:%M:%S%Z"
	RFC3339Micro = "%Y-%m-%dT%H:%M:%S.%f%Z"
	// Second is the base unit for TAI and UNIX time since epoch
	Second = 1

	// Minute is the number of seconds per minute
	Minute = 60 * Second

	// Hour is the number of seconds per hour
	Hour = 60 * Minute

	// Day is the number of seconds per day
	Day = 24 * Hour

	// Year is the exact number of seconds per year in the TAI system
	Year = 31564800 * Second

	// Attosecond is the base unit for TAI fractional time
	Attosecond = 1

	// Femto, Pico, Nano, Micro, and Millisecond are whole number multiples of
	// Attoseconds
	Femtosecond = 1e3 * Attosecond
	Picosecond  = 1e6 * Attosecond
	Nanosecond  = 1e9 * Attosecond
	Microsecond = 1e12 * Attosecond
	Millisecond = 1e15 * Attosecond
)

Variables

View Source
var (
	// LastKnownBulletinCUpdate is the last known issue of Bulletin C by the
	// IERS that pkg tai was updated for
	LastKnownBulletinCUpdate = 62
	// LastKnownBulletinCTime is the date on which the last known Bulletin C
	// was released
	LastKnownBulletinCTimestamp = Gregorian{Year: 2021, Month: July, Day: 5}

	// PkgUpToDateUntil is the moment in time at which the last known bulletin C
	// update is made invalid
	PkgUpToDateUntil = Gregorian{Year: 2022, Month: January, Day: 5}
)

Functions

func CivilFromDays

func CivilFromDays(days int) (y, m, d int)

CivilFromDays converts the number of days in the internal representation to a day in the civil (Gregorian) calendar

func DaysFromCivil

func DaysFromCivil(y, m, d int) int

DaysFromCivil returns the number of days in the Gregorian calendar since Jan 1, 1958 from a year, month, and day

func DaysFromSecsEpoch

func DaysFromSecsEpoch(secs int64) int

DaysFromSecsEpoch returns the number of days in the internal representation since the epoch in seconds

func DaysInMonth

func DaysInMonth(m, y int) int

DaysInMonth returns the number of days in the given month and year

func IsLeapYear

func IsLeapYear(year int) bool

IsLeapYear returns true if year is a leap year, false if year is not a leap year.

func NextWeekday

func NextWeekday(wd int) int

NextWeekday returns the next weekday number after wd

func PrevWeekday

func PrevWeekday(wd int) int

PrevWeekday returns the weekday number proceeding wd

func RegisterLeapSecond

func RegisterLeapSecond(unixUTC int64, cumulativeSkew int64) error

RegisterLeapSecond inserts a new leap second into the leap second table

if the time t is already known to be a leap and the skew matches, the function silently does nothing.

if the time t is already known and the skew does not match, an error is returned

t need not be the most recent leap second

skew need not be 1 and need not be positive

inserting a leap prior to the first leap second (Jan 1, 1970) will produce an error, since there were no leap seconds prior to that time.

RegisterLeapSecond is not thread safe; two calls of the function may not be executed concurrently.

func RemoveLeapSecond

func RemoveLeapSecond(unixUTC int64)

RemoveLeapSecond removes a leap second from the table.

if unixUTC is not a leap, it does nothing

if removal of a leap would result in fewer entries in the table than are known to have been published by IERS when pkg tai was last updated, this function panics.

func SecsEpochFromDays

func SecsEpochFromDays(days int) int64

func WeekdayDifference

func WeekdayDifference(d1, d2 int) int

WeekdayDifference computes the number of days between weekday d1, d2.

d1,d2 are in the range [0,6]

func WeekdayFromDays

func WeekdayFromDays(days int) int

WeekFromDays returns the weekday number in the common programming, ISO-incompatible notation where 0 == sunday, 6 == sat; not ISO (0 == monday)

Types

type Gregorian

type Gregorian struct {
	Asec  int64
	Year  int
	Month int
	Day   int
	Hour  int
	Min   int
	Sec   int
}

Gregorian represents a moment in the Proleptic Gregorian Calendar and the TAI time system

func (Gregorian) After

func (g Gregorian) After(o Gregorian) bool

After returns true if g is after o

func (Gregorian) Before

func (g Gregorian) Before(o Gregorian) bool

Before returns true if g is before o

func (Gregorian) Eq

func (g Gregorian) Eq(o Gregorian) bool

Eq returns true if g and o represent the same instant in time

type TAI

type TAI struct {
	// Sec is the number of whole seconds since TAI Epoch
	Sec int64
	// Asec is the number of attoseconds representing fractional time
	// Behavior is undefined if Asec > 1e18
	Asec int64
}

TAI represents an international atomic time (TAI) moment

The zero value of TAI represents the atomic time Epoch of Jan 1, 1958 at 00:00:00

func Date

func Date(y, m, d int) TAI

Date returns the TAI value that corresponds to y,m,d in the Proleptic Gregorian Calendar

if y/m/d are outside the expected range (m in [1,12], days ~= in [1,30] depending on m) the behavior is undefined and the result will likely be quietly incorrect

func FromGregorian

func FromGregorian(g Gregorian) TAI

FromGreg returns the TAI value corresponding to a moment in the Proleptic Gregorian Calendar

FromGreg can be replaced by a pair of calls to Date(...).AddHMS and insertion of an Asec value

func FromTime

func FromTime(t time.Time) TAI

FromTime converts time t to TAI time, including handling of leap seconds

func Now

func Now() TAI

Now returns the current TAI moment, up to the level of maintenance in the leapsecond table. Consult the func tai.Unix documentation for further information.

func Unix

func Unix(seconds, nsec int64) TAI

Unix returns the TAI time corresponding the the given UNIX time in the UTC time zone

As UNIX times are in the UTC time system which contains leap seconds, the offset between UTC and TAI is not constant.

All known leap seconds to pkg tai known when Unix is called are consulted in making the conversion. If the leap second table is not maintained, this function will develope skew.

see func RegisterLeapSecond

Unix has nsec resolution for equivalence to the stdlib Time package, but TAI times have one billion times the precision.

func (TAI) AddHMS

func (t TAI) AddHMS(h, m, s int) TAI

AddHMS returns t offset by the given hours, minutes, and seconds

func (TAI) After

func (t TAI) After(o TAI) bool

After returns true if t is after o

func (TAI) AsGregorian

func (t TAI) AsGregorian() Gregorian

AsGreg converts a TAI timestamp to a time in the Gregorian Calendar

func (TAI) AsTime

func (t TAI) AsTime() time.Time

AsTime returns t as a Time object

func (TAI) Before

func (t TAI) Before(o TAI) bool

Before returns true if t is before o

func (TAI) Eq

func (t TAI) Eq(o TAI) bool

Eq returns true if t and o represent the same instant in time

func (TAI) Format

func (t TAI) Format(fmtspec string) string

Format converts t into a textual representation similar to strftime and similar functions. The valid specifiers are:

- %a weekday as abbreviated name, e.g. Mon

- %A Unabbreviated weekday, e.g. Monday

- %w Weekday as a single digit number. 0==Sunday

- %d Day of month as a two digit number, e.g. 12.

- %b Month as abbreviated name, e.g. Sept

- %B Unabbreviated Month, e.g. September

- %m Month as a two digit number, e.g. 03

- %y Year without century or millenium; two digits, e.g. 2012==12

- %Y Year with century/millenium, e.g. 2021

- %H 24-hour clock Hour as a two digit number, e.g. 22

- %I 12-hour clock Hour as a two digit number, e.g. 12

- %p AM or PM

- %M Minute as a two digit number, e.g. 03

- %S Second as a two digit number, e.g. 59

- %f Microsecond as a six digit decimal number

- %z The letter "Z" (timezone, but TAI only exists in the UTC timezone)

- %j Ordinal day of year, e.g. 364

- %U Week number of the year, with Sunday as the first day of the week

Format panics if an unknown specifier is used.

func (TAI) Unix

func (t TAI) Unix() (secs, nsecs int64)

Unix returns the UNIX representation of t with nanosecond resolution

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL