X-Git-Url: http://www.git.cypherpunks.ru/?p=gotai64n.git;a=blobdiff_plain;f=tai64n.go;fp=tai64n.go;h=8af8ab3a349899cddb698e132dfbec7d91f8f4b2;hp=473c7e8083e66db6c9118f2956dce816c29ba798;hb=9978840e570dfe3a23e2db36d64237febceaebcf;hpb=266a0ae363ebb2c96e0a1d700c20bfc3a3188788 diff --git a/tai64n.go b/tai64n.go index 473c7e8..8af8ab3 100644 --- a/tai64n.go +++ b/tai64n.go @@ -1,5 +1,5 @@ /* -go.cypherpunks.ru/tai64n -- Pure Go TAI64N implementation +go.cypherpunks.ru/tai64n -- Pure Go TAI64/TAI64N implementation Copyright (C) 2020-2021 Sergey Matveev This program is free software: you can redistribute it and/or modify @@ -15,6 +15,18 @@ 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 +38,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 }