Pure Go TAI64/TAI64N (http://cr.yp.to/libtai/tai64.html) implementation.
-cmd/tai64nlocal and cmd/leapsecsdb are similar to DJB's ones.
-Look at cmd/* and docstrings for example usage.
+cmd/tai64nlocal is similar to DJB's one.
+++ /dev/null
-// go.cypherpunks.su/tai64n -- Pure Go TAI64/TAI64N implementation
-// Copyright (C) 2020-2024 Sergey Matveev <stargrave@stargrave.org>
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3 of the License.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-package main
-
-import (
- "bufio"
- "flag"
- "fmt"
- "log"
- "os"
- "time"
-
- "go.cypherpunks.su/tai64n/v3"
-)
-
-func main() {
- log.SetFlags(0)
- flag.Usage = func() {
- fmt.Fprintf(
- flag.CommandLine.Output(),
- `Convert YYYY-MM-DD dates to TAI64 timestamps.
-$ leapsecsdb > leapsecs.dat <<EOF
-1972-07-01
-1973-01-01
-EOF
-`,
- )
- flag.PrintDefaults()
- }
- flag.Parse()
-
- scanner := bufio.NewScanner(os.Stdin)
- var err error
- var t time.Time
- tai := new(tai64n.TAI64)
- for {
- if !scanner.Scan() {
- if err = scanner.Err(); err != nil {
- log.Fatalln(err)
- }
- break
- }
- t, err = time.Parse("2006-01-02", scanner.Text())
- if err != nil {
- log.Fatalln(err)
- }
- tai.FromTime(t)
- if _, err = os.Stdout.Write(tai[:]); err != nil {
- log.Fatalln(err)
- }
- }
-}
// go.cypherpunks.su/tai64n -- Pure Go TAI64/TAI64N implementation
-// Copyright (C) 2020-2024 Sergey Matveev <stargrave@stargrave.org>
+// Copyright (C) 2020-2025 Sergey Matveev <stargrave@stargrave.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
"strings"
"time"
- "go.cypherpunks.su/tai64n/v3"
+ "go.cypherpunks.su/tai64n/v4"
)
func main() {
if err != nil {
log.Fatalln(err)
}
- tai64n.LeapsecsDBLoad(buf)
+ tai64n.Leapsecs.Load(buf)
}
scanner := bufio.NewScanner(os.Stdin)
log.Fatalln(err)
}
if *leapsecs {
- t = tai64n.LeapsecsSub(t)
+ t, _ = tai64n.Leapsecs.Sub(t)
}
os.Stdout.WriteString(t.Format(tai64n.LocalFmt) + s[sep:] + "\n")
}
+++ /dev/null
-// go.cypherpunks.su/tai64n -- Pure Go TAI64/TAI64N implementation
-// Copyright (C) 2020-2024 Sergey Matveev <stargrave@stargrave.org>
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3 of the License.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-package tai64n
-
-import "time"
-
-func init() {
- LeapsecsDB = []int64{
- time.Date(2017, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(2015, 7, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(2012, 7, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(2009, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(2006, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1999, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1997, 7, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1996, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1994, 7, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1993, 7, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1992, 7, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1991, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1990, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1988, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1985, 7, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1983, 7, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1982, 7, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1981, 7, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1980, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1979, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1978, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1977, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1976, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1975, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1974, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1973, 1, 1, 0, 0, 0, 0, time.UTC).Unix(),
- time.Date(1972, 7, 1, 0, 0, 0, 0, time.UTC).Unix(),
- }
-}
-module go.cypherpunks.su/tai64n/v3
+module go.cypherpunks.su/tai64n/v4
go 1.20
// go.cypherpunks.su/tai64n -- Pure Go TAI64/TAI64N implementation
-// Copyright (C) 2020-2024 Sergey Matveev <stargrave@stargrave.org>
+// Copyright (C) 2020-2025 Sergey Matveev <stargrave@stargrave.org>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
package tai64n
import (
- "sort"
"time"
)
const Leapsecs1972 = 10
+type LeapsecsList []int64
+
// Database of Unix timestamps of the time when leap second occurred.
// Library contains and initializes it with leap seconds up to 2016-12-31.
-var LeapsecsDB []int64
+var Leapsecs LeapsecsList
-// TAI<->UTC difference for the given Unix timestamp.
-func LeapsecsDiff(t int64) int {
- for i, leap := range LeapsecsDB {
- if t > leap {
- return len(LeapsecsDB) - i
+// Add leap seconds to the time (convert TAI to UTC).
+func (leapsecs LeapsecsList) Add(tai time.Time) (utc time.Time) {
+ var i int
+ v := tai.Unix()
+ for ; i < len(leapsecs); i++ {
+ if v < leapsecs[i] {
+ break
}
}
- return 0
-}
-
-// Add currently known (LeapsecsDB) leap seconds, not including initial
-// 1972-01-01 10-seconds offset.
-func LeapsecsAdd(t time.Time) time.Time {
- return t.Add(time.Second * time.Duration(LeapsecsDiff(t.Unix())))
+ diff := Leapsecs1972 + int64(i)
+ if v+diff == leapsecs[i] {
+ diff++
+ }
+ utc = tai.Add(time.Second * time.Duration(diff))
+ return
}
-// Opposite of LeapsecsAdd().
-func LeapsecsSub(t time.Time) time.Time {
- return t.Add(-time.Second * time.Duration(LeapsecsDiff(t.Unix())))
+// Subtract leap seconds from the time (convert UTC to TAI).
+func (leapsecs LeapsecsList) Sub(utc time.Time) (tai time.Time, isLeap bool) {
+ diff := int64(Leapsecs1972)
+ v := utc.Unix()
+ for _, leapsec := range leapsecs {
+ if v < leapsec {
+ break
+ }
+ diff++
+ if v == leapsec {
+ isLeap = true
+ break
+ }
+ }
+ tai = utc.Add(-time.Second * time.Duration(diff))
+ return
}
-type Int64s []int64
-
-func (a Int64s) Len() int { return len(a) }
-func (a Int64s) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
-func (a Int64s) Less(i, j int) bool { return a[i] > a[j] }
-
-// Load "leapsecs.dat"-like database: concatenated TAI64 leap seconds.
-// Function panics if encoding is invalid.
-func LeapsecsDBLoad(buf []byte) {
+// Load "leapsecs.dat"-like database (concatenated TAI64 leap seconds).
+func (leapsecs *LeapsecsList) Load(buf []byte) {
db := make([]int64, 0, len(buf)/TAI64Size)
+ var tai TAI64
for i := 0; i < len(buf); i += TAI64Size {
- db = append(db, (ToTime(buf[i:i+TAI64Size]).Unix()/86400)*86400)
+ tai = [TAI64Size]byte(buf[i : i+TAI64Size])
+ db = append(db, tai.Time().Unix())
+ }
+ *leapsecs = db
+}
+
+func init() {
+ def := []time.Time{
+ time.Date(1972, 6, 30, 0, 0, 0, 0, time.UTC),
+ time.Date(1972, 12, 31, 0, 0, 0, 0, time.UTC),
+ time.Date(1973, 12, 31, 0, 0, 0, 0, time.UTC),
+ time.Date(1974, 12, 31, 0, 0, 0, 0, time.UTC),
+ time.Date(1975, 12, 31, 0, 0, 0, 0, time.UTC),
+ time.Date(1976, 12, 31, 0, 0, 0, 0, time.UTC),
+ time.Date(1977, 12, 31, 0, 0, 0, 0, time.UTC),
+ time.Date(1978, 12, 31, 0, 0, 0, 0, time.UTC),
+ time.Date(1979, 12, 31, 0, 0, 0, 0, time.UTC),
+ time.Date(1981, 6, 30, 0, 0, 0, 0, time.UTC),
+ time.Date(1982, 6, 30, 0, 0, 0, 0, time.UTC),
+ time.Date(1983, 6, 30, 0, 0, 0, 0, time.UTC),
+ time.Date(1985, 6, 30, 0, 0, 0, 0, time.UTC),
+ time.Date(1987, 12, 31, 0, 0, 0, 0, time.UTC),
+ time.Date(1989, 12, 31, 0, 0, 0, 0, time.UTC),
+ time.Date(1990, 12, 31, 0, 0, 0, 0, time.UTC),
+ time.Date(1992, 6, 30, 0, 0, 0, 0, time.UTC),
+ time.Date(1993, 6, 30, 0, 0, 0, 0, time.UTC),
+ time.Date(1994, 6, 30, 0, 0, 0, 0, time.UTC),
+ time.Date(1995, 12, 31, 0, 0, 0, 0, time.UTC),
+ time.Date(1997, 6, 30, 0, 0, 0, 0, time.UTC),
+ time.Date(1998, 12, 31, 0, 0, 0, 0, time.UTC),
+ time.Date(2005, 12, 31, 0, 0, 0, 0, time.UTC),
+ time.Date(2008, 12, 31, 0, 0, 0, 0, time.UTC),
+ time.Date(2012, 6, 30, 0, 0, 0, 0, time.UTC),
+ time.Date(2015, 6, 30, 0, 0, 0, 0, time.UTC),
+ time.Date(2016, 12, 31, 0, 0, 0, 0, time.UTC),
+ }
+ db := make([]int64, len(def))
+ for i, t := range def {
+ db[i] = t.Add(time.Second*86400).Unix() + Leapsecs1972 + int64(i)
}
- sort.Sort(Int64s(db))
- LeapsecsDB = db
+ Leapsecs = LeapsecsList(db)
}
--- /dev/null
+// go.cypherpunks.su/tai64n -- Pure Go TAI64/TAI64N implementation
+// Copyright (C) 2020-2025 Sergey Matveev <stargrave@stargrave.org>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 3 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package tai64n
+
+import (
+ "encoding/hex"
+ "errors"
+ "strings"
+ "time"
+)
+
+func (tai *TAI64) Encode() string {
+ raw := make([]byte, 1+TAI64Size*2)
+ raw[0] = '@'
+ hex.Encode(raw[1:], tai[:])
+ return string(raw)
+}
+
+func (tai *TAI64N) Encode() string {
+ raw := make([]byte, 1+TAI64NSize*2)
+ raw[0] = '@'
+ hex.Encode(raw[1:], tai[:])
+ return string(raw)
+}
+
+// Convert "@HEX(TAI64(N?))" format to Time.
+func Decode(s string) (t time.Time, err error) {
+ var raw []byte
+ raw, err = hex.DecodeString(strings.TrimPrefix(s, "@"))
+ if err != nil {
+ return
+ }
+ switch len(raw) {
+ case TAI64Size:
+ tai := TAI64(raw)
+ return tai.Time(), nil
+ case TAI64NSize:
+ tai := TAI64N(raw)
+ return tai.Time(), nil
+ }
+ err = errors.New("unsupported length")
+ return
+}
--- /dev/null
+// go.cypherpunks.su/tai64n -- Pure Go TAI64/TAI64N implementation
+// Copyright (C) 2020-2025 Sergey Matveev <stargrave@stargrave.org>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 3 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+// TAI64/TAI64N (http://cr.yp.to/libtai/tai64.html) dealing library.
+// You can convert time to TAI64/TAI64N and vice versa with it.
+//
+// var tai tai64n.TAI64N
+// tai.FromTime(time.Now())
+// printable := tai.Encode()
+// decoded, err := tai64n.Decode(printable)
+// tai.Time() == decoded
+package tai64n
+
+import (
+ "encoding/binary"
+ "time"
+)
+
+const (
+ TAI64Size = 8
+ TAI64NSize = TAI64Size + 4
+ TAI64NASize = TAI64NSize + 4
+ LocalFmt = "2006-01-02 15:04:05.000000000"
+ Base = 0x4000000000000000
+)
+
+type (
+ TAI64 [TAI64Size]byte
+ TAI64N [TAI64NSize]byte
+ TAI64NA [TAI64NASize]byte
+)
+
+func (tai *TAI64) FromTime(src time.Time) {
+ binary.BigEndian.PutUint64(tai[:], uint64(Base)+uint64(src.Unix()))
+}
+
+func (tai *TAI64N) FromTime(src time.Time) {
+ binary.BigEndian.PutUint64(tai[:], uint64(Base)+uint64(src.Unix()))
+ binary.BigEndian.PutUint32(tai[TAI64Size:], uint32(src.Nanosecond()))
+}
+
+func (tai *TAI64) Time() time.Time {
+ return time.Unix(int64(binary.BigEndian.Uint64(tai[:TAI64Size]))-Base, 0)
+}
+
+func (tai *TAI64N) Time() time.Time {
+ return time.Unix(
+ int64(binary.BigEndian.Uint64(tai[:TAI64Size]))-Base,
+ int64(binary.BigEndian.Uint32(tai[TAI64Size:])),
+ )
+}
+++ /dev/null
-// go.cypherpunks.su/tai64n -- Pure Go TAI64/TAI64N implementation
-// Copyright (C) 2020-2024 Sergey Matveev <stargrave@stargrave.org>
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3 of the License.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-// TAI64/TAI64N (http://cr.yp.to/libtai/tai64.html) dealing library.
-// You can convert time to TAI64/TAI64N and vice versa with it.
-//
-// tai := new(tai64n.TAI64N)
-// tai.FromTime(time.Now())
-// printable := tai64n.Encode(tai[:])
-// decoded, err := tai64n.Decode(printable)
-// tai64n.ToTime(tai[:]) == decoded
-//
-// By default TAI64 timestamps contain initial 1972-01-01 10-seconds
-// TAI<->UTC difference. If you need honest TAI representation, then you
-// should also use Leapsecs* functions.
-package tai64n
-
-import (
- "encoding/binary"
- "encoding/hex"
- "errors"
- "strings"
- "time"
-)
-
-const (
- TAI64Size = 8
- TAI64NSize = TAI64Size + 4
- TAI64NASize = TAI64Size + 4 + 4
- LocalFmt = "2006-01-02 15:04:05.000000000"
- Base = 0x4000000000000000 + Leapsecs1972
-)
-
-type (
- TAI64 [TAI64Size]byte
- TAI64N [TAI64NSize]byte
- TAI64NA [TAI64NASize]byte
-)
-
-func (tai *TAI64) FromTime(src time.Time) {
- binary.BigEndian.PutUint64(tai[:], uint64(Base)+uint64(src.Unix()))
-}
-
-func (tai *TAI64N) FromTime(src time.Time) {
- binary.BigEndian.PutUint64(tai[:], uint64(Base)+uint64(src.Unix()))
- binary.BigEndian.PutUint32(tai[8:], uint32(src.Nanosecond()))
-}
-
-func ToTime(tai []byte) time.Time {
- var secs, nano int64
- switch len(tai) {
- case TAI64NASize:
- panic("TAI64NA can not be converted to time.Time")
- case TAI64NSize:
- nano = int64(binary.BigEndian.Uint32(tai[8:]))
- fallthrough
- case TAI64Size:
- secs = int64(binary.BigEndian.Uint64(tai[:8])) - Base
- default:
- panic("invalid TAI64* size")
- }
- return time.Unix(secs, nano)
-}
-
-// Convert TAI64* to "@HEX(TAI64)" format.
-func Encode(tai []byte) string {
- raw := make([]byte, 1+hex.EncodedLen(len(tai)))
- raw[0] = '@'
- hex.Encode(raw[1:], tai)
- return string(raw)
-}
-
-// Convert TAI64* "@HEX(TAI64)" format to Time.
-func Decode(s string) (t time.Time, err error) {
- raw, err := hex.DecodeString(strings.TrimPrefix(s, "@"))
- switch len(raw) {
- case TAI64NASize:
- case TAI64NSize:
- case TAI64Size:
- default:
- err = errors.New("invalid length")
- return
- }
- t = ToTime(raw)
- return
-}
+++ /dev/null
-// go.cypherpunks.su/tai64n -- Pure Go TAI64/TAI64N implementation
-// Copyright (C) 2020-2024 Sergey Matveev <stargrave@stargrave.org>
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, version 3 of the License.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-package tai64n
-
-import (
- "testing"
- "time"
-)
-
-func TestVector(t *testing.T) {
- tm, err := Decode("400000002a2b2c2d")
- if err != nil {
- t.Fatal(err)
- }
-
- ref := time.Date(1992, 6, 2, 8, 7, 9, 0, time.UTC).Add(-Leapsecs1972 * time.Second)
- if !tm.Equal(ref) {
- t.Fatal("TAI64 != reference")
- }
-
- tm = LeapsecsSub(tm)
- ref = time.Date(1992, 6, 2, 8, 6, 43, 0, time.UTC)
- if !tm.Equal(ref) {
- t.Fatal("UTC != reference")
- }
-}
-
-func BenchmarkTAI64(b *testing.B) {
- now := time.Now()
- now = time.Unix(now.Unix(), 0)
- tai := new(TAI64)
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- tai.FromTime(now)
- if !ToTime(tai[:]).Equal(now) {
- b.FailNow()
- }
- }
-}
-
-func BenchmarkTAI64N(b *testing.B) {
- now := time.Now()
- now = time.Unix(now.Unix(), now.UnixNano())
- tai := new(TAI64N)
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- tai.FromTime(now)
- if !ToTime(tai[:]).Equal(now) {
- b.FailNow()
- }
- }
-}
--- /dev/null
+// go.cypherpunks.su/tai64n -- Pure Go TAI64/TAI64N implementation
+// Copyright (C) 2020-2025 Sergey Matveev <stargrave@stargrave.org>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 3 of the License.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package tai64n
+
+import (
+ "testing"
+ "time"
+)
+
+func TestVectors(t *testing.T) {
+ tm, err := Decode("400000002a2b2c2d")
+ if err != nil {
+ t.Fatal(err)
+ }
+ ref := time.Date(1992, 6, 2, 8, 7, 9, 0, time.UTC)
+ if !tm.Equal(ref) {
+ t.Fatal("TAI64 != reference")
+ }
+ tm, isLeap := Leapsecs.Sub(tm)
+ ref = time.Date(1992, 6, 2, 8, 6, 43, 0, time.UTC)
+ if !tm.Equal(ref) {
+ t.Fatal("UTC != reference")
+ }
+ if isLeap {
+ t.Fatal("unexpectedly leap")
+ }
+
+ tm, err = Decode("4000000034353637")
+ if err != nil {
+ t.Fatal(err)
+ }
+ ref = time.Date(1997, 10, 3, 18, 15, 19, 0, time.UTC)
+ if !tm.Equal(ref) {
+ t.Fatal("TAI64 != reference")
+ }
+ tm, isLeap = Leapsecs.Sub(tm)
+ ref = time.Date(1997, 10, 3, 18, 14, 48, 0, time.UTC)
+ if !tm.Equal(ref) {
+ t.Fatal("UTC != reference")
+ }
+ if isLeap {
+ t.Fatal("unexpectedly leap")
+ }
+
+ tm, err = Decode("40000000586846a3")
+ if err != nil {
+ t.Fatal(err)
+ }
+ tm, isLeap = Leapsecs.Sub(tm)
+ ref = time.Date(2016, 12, 31, 23, 59, 59, 0, time.UTC)
+ if !tm.Equal(ref) {
+ t.Fatal("UTC != reference")
+ }
+ if isLeap {
+ t.Fatal("unexpectedly leap")
+ }
+
+ tm, err = Decode("40000000586846a4")
+ if err != nil {
+ t.Fatal(err)
+ }
+ tm, isLeap = Leapsecs.Sub(tm)
+ ref = time.Date(2016, 12, 31, 23, 59, 59, 0, time.UTC)
+ if !tm.Equal(ref) {
+ t.Fatal("UTC != reference")
+ }
+ if !isLeap {
+ t.Fatal("unexpectedly non-leap")
+ }
+
+ tm, err = Decode("40000000586846a5")
+ if err != nil {
+ t.Fatal(err)
+ }
+ tm, isLeap = Leapsecs.Sub(tm)
+ ref = time.Date(2017, 1, 1, 0, 0, 0, 0, time.UTC)
+ if !tm.Equal(ref) {
+ t.Fatal("UTC != reference")
+ }
+ if isLeap {
+ t.Fatal("unexpectedly leap")
+ }
+}
+
+func BenchmarkTAI64(b *testing.B) {
+ now := time.Now()
+ now = now.Truncate(time.Second)
+ var tai TAI64
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ tai.FromTime(now)
+ if !tai.Time().Equal(now) {
+ b.FailNow()
+ }
+ }
+}
+
+func BenchmarkTAI64N(b *testing.B) {
+ now := time.Now()
+ var tai TAI64N
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ tai.FromTime(now)
+ if !tai.Time().Equal(now) {
+ b.FailNow()
+ }
+ }
+}