]> Cypherpunks.ru repositories - gotai64n.git/blobdiff - tai64n.go
Unify copyright comment format
[gotai64n.git] / tai64n.go
index 473c7e8083e66db6c9118f2956dce816c29ba798..3802bb85786994e3645921c7c252bd5cc2dbd0e3 100644 (file)
--- a/tai64n.go
+++ b/tai64n.go
@@ -1,20 +1,30 @@
-/*
-go.cypherpunks.ru/tai64n -- Pure Go TAI64N implementation
-Copyright (C) 2020-2021 Sergey Matveev <stargrave@stargrave.org>
-
-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 <http://www.gnu.org/licenses/>.
-*/
+// go.cypherpunks.ru/tai64n -- Pure Go TAI64/TAI64N implementation
+// Copyright (C) 2020-2024 Sergey Matveev <stargrave@stargrave.org>
+//
+// 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 <http://www.gnu.org/licenses/>.
 
+// 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
 }