X-Git-Url: http://www.git.cypherpunks.ru/?p=gotai64n.git;a=blobdiff_plain;f=tai64n.go;h=50a4e559314e13f4a521991b33bb909c127e69fe;hp=473c7e8083e66db6c9118f2956dce816c29ba798;hb=HEAD;hpb=409705fa3bbf9f18c6df91be80199d4d14c234d2 diff --git a/tai64n.go b/tai64n.go index 473c7e8..3802bb8 100644 --- a/tai64n.go +++ b/tai64n.go @@ -1,20 +1,30 @@ -/* -go.cypherpunks.ru/tai64n -- Pure Go TAI64N implementation -Copyright (C) 2020-2021 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 . -*/ +// 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 ( @@ -26,38 +36,57 @@ 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 }