X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=tai64n.go;h=d3071a37f461625757e450d7be76c737a52fc227;hb=f1c943309e457d9bf9617677584a2f8db828e025;hp=5087361dbbd178975ac962788101893f2b65e650;hpb=dd683a282f1b02438967936e85a8601f90f29400;p=gotai64n.git diff --git a/tai64n.go b/tai64n.go index 5087361..d3071a3 100644 --- a/tai64n.go +++ b/tai64n.go @@ -1,6 +1,6 @@ /* -go.cypherpunks.ru/tai64n -- Pure Go TAI64N implementation -Copyright (C) 2020 Sergey Matveev +go.cypherpunks.ru/tai64n -- Pure Go TAI64/TAI64N implementation +Copyright (C) 2020-2022 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 @@ -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 }