/*
-go.cypherpunks.ru/tai64n -- Pure Go TAI64N implementation
-Copyright (C) 2020 Sergey Matveev <stargrave@stargrave.org>
+go.cypherpunks.ru/tai64n -- Pure Go TAI64/TAI64N implementation
+Copyright (C) 2020-2023 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
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 (
)
const (
- Size = 12
- Base = 0x400000000000000a
- LocalFmt = "2006-01-02 15:04:05.000000000"
+ TAI64Size = 8
+ TAI64NSize = TAI64Size + 4
+ LocalFmt = "2006-01-02 15:04:05.000000000"
+ Base = 0x4000000000000000 + Leapsecs1972
)
-type TAI64N [Size]byte
+type TAI64 [TAI64Size]byte
+type TAI64N [TAI64NSize]byte
-func FromTime(src time.Time, dst *TAI64N) {
+func (dst *TAI64) FromTime(src time.Time) {
+ binary.BigEndian.PutUint64(dst[:], uint64(Base)+uint64(src.Unix()))
+}
+
+func (dst *TAI64N) FromTime(src time.Time) {
binary.BigEndian.PutUint64(dst[:], uint64(Base)+uint64(src.Unix()))
binary.BigEndian.PutUint32(dst[8:], uint32(src.Nanosecond()))
}
func ToTime(tai []byte) time.Time {
- if len(tai) != Size {
- panic("invalid size")
+ var secs, nano int64
+ switch len(tai) {
+ case TAI64NSize:
+ nano = int64(binary.BigEndian.Uint32(tai[8:]))
+ fallthrough
+ case TAI64Size:
+ secs = int64(binary.BigEndian.Uint64(tai[:8])) - Base
+ default:
+ panic("invalid tai size")
+ }
+ if secs < 0 {
+ panic("dates < 1970-01-01 are not supported")
}
- secs := int64(binary.BigEndian.Uint64(tai[:8]))
- nano := int64(binary.BigEndian.Uint32(tai[8:]))
- return time.Unix(secs-Base, nano)
+ return time.Unix(secs, nano)
}
-func (tai TAI64N) Encode() string {
- return "@" + hex.EncodeToString(tai[:])
+// Convert TAI64/TAI64N to "@HEX(TAI64)" format.
+func Encode(tai []byte) string {
+ raw := make([]byte, 1+hex.EncodedLen(len(tai)))
+ raw[0] = byte('@')
+ hex.Encode(raw[1:], tai)
+ return string(raw)
}
+// Convert TAI64/TAI64N "@HEX(TAI64)" format to Time.
func Decode(s string) (time.Time, error) {
- tai, err := hex.DecodeString(strings.TrimPrefix(s, "@"))
- if len(tai) != Size {
- return time.Time{}, errors.New("invalid ts length")
+ raw, err := hex.DecodeString(strings.TrimPrefix(s, "@"))
+ if !(len(raw) == TAI64Size || len(raw) == TAI64NSize) {
+ err = errors.New("invalid length")
}
- if err != nil {
- return time.Time{}, err
+ if err == nil {
+ return ToTime(raw), nil
}
- return ToTime(tai), nil
+ return time.Time{}, err
}