1 // go.cypherpunks.ru/tai64n -- Pure Go TAI64/TAI64N implementation
2 // Copyright (C) 2020-2024 Sergey Matveev <stargrave@stargrave.org>
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, version 3 of the License.
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // TAI64/TAI64N (http://cr.yp.to/libtai/tai64.html) dealing library.
17 // You can convert time to TAI64/TAI64N and vice versa with it.
19 // tai := new(tai64n.TAI64N)
20 // tai.FromTime(time.Now())
21 // printable := tai64n.Encode(tai[:])
22 // decoded, err := tai64n.Decode(printable)
23 // tai64n.ToTime(tai[:]) == decoded
25 // By default TAI64 timestamps contain initial 1972-01-01 10-seconds
26 // TAI<->UTC difference. If you need honest TAI representation, then you
27 // should also use Leapsecs* functions.
40 TAI64NSize = TAI64Size + 4
41 LocalFmt = "2006-01-02 15:04:05.000000000"
42 Base = 0x4000000000000000 + Leapsecs1972
45 type TAI64 [TAI64Size]byte
46 type TAI64N [TAI64NSize]byte
48 func (dst *TAI64) FromTime(src time.Time) {
49 binary.BigEndian.PutUint64(dst[:], uint64(Base)+uint64(src.Unix()))
52 func (dst *TAI64N) FromTime(src time.Time) {
53 binary.BigEndian.PutUint64(dst[:], uint64(Base)+uint64(src.Unix()))
54 binary.BigEndian.PutUint32(dst[8:], uint32(src.Nanosecond()))
57 func ToTime(tai []byte) time.Time {
61 nano = int64(binary.BigEndian.Uint32(tai[8:]))
64 secs = int64(binary.BigEndian.Uint64(tai[:8])) - Base
66 panic("invalid tai size")
69 panic("dates < 1970-01-01 are not supported")
71 return time.Unix(secs, nano)
74 // Convert TAI64/TAI64N to "@HEX(TAI64)" format.
75 func Encode(tai []byte) string {
76 raw := make([]byte, 1+hex.EncodedLen(len(tai)))
78 hex.Encode(raw[1:], tai)
82 // Convert TAI64/TAI64N "@HEX(TAI64)" format to Time.
83 func Decode(s string) (time.Time, error) {
84 raw, err := hex.DecodeString(strings.TrimPrefix(s, "@"))
85 if !(len(raw) == TAI64Size || len(raw) == TAI64NSize) {
86 err = errors.New("invalid length")
89 return ToTime(raw), nil
91 return time.Time{}, err