// go.cypherpunks.ru/tai64n -- Pure Go TAI64/TAI64N implementation // Copyright (C) 2020-2024 Sergey Matveev // // 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 . // 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 LocalFmt = "2006-01-02 15:04:05.000000000" Base = 0x4000000000000000 + Leapsecs1972 ) type TAI64 [TAI64Size]byte type TAI64N [TAI64NSize]byte 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 { 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") } return time.Unix(secs, nano) } // 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) { raw, err := hex.DecodeString(strings.TrimPrefix(s, "@")) if !(len(raw) == TAI64Size || len(raw) == TAI64NSize) { err = errors.New("invalid length") } if err == nil { return ToTime(raw), nil } return time.Time{}, err }