]> Cypherpunks.ru repositories - gotai64n.git/blob - tai64n.go
Unify copyright comment format
[gotai64n.git] / tai64n.go
1 // go.cypherpunks.ru/tai64n -- Pure Go TAI64/TAI64N implementation
2 // Copyright (C) 2020-2024 Sergey Matveev <stargrave@stargrave.org>
3 //
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.
7 //
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.
12 //
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/>.
15
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.
18 //
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
24 //
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.
28 package tai64n
29
30 import (
31         "encoding/binary"
32         "encoding/hex"
33         "errors"
34         "strings"
35         "time"
36 )
37
38 const (
39         TAI64Size  = 8
40         TAI64NSize = TAI64Size + 4
41         LocalFmt   = "2006-01-02 15:04:05.000000000"
42         Base       = 0x4000000000000000 + Leapsecs1972
43 )
44
45 type TAI64 [TAI64Size]byte
46 type TAI64N [TAI64NSize]byte
47
48 func (dst *TAI64) FromTime(src time.Time) {
49         binary.BigEndian.PutUint64(dst[:], uint64(Base)+uint64(src.Unix()))
50 }
51
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()))
55 }
56
57 func ToTime(tai []byte) time.Time {
58         var secs, nano int64
59         switch len(tai) {
60         case TAI64NSize:
61                 nano = int64(binary.BigEndian.Uint32(tai[8:]))
62                 fallthrough
63         case TAI64Size:
64                 secs = int64(binary.BigEndian.Uint64(tai[:8])) - Base
65         default:
66                 panic("invalid tai size")
67         }
68         if secs < 0 {
69                 panic("dates < 1970-01-01 are not supported")
70         }
71         return time.Unix(secs, nano)
72 }
73
74 // Convert TAI64/TAI64N to "@HEX(TAI64)" format.
75 func Encode(tai []byte) string {
76         raw := make([]byte, 1+hex.EncodedLen(len(tai)))
77         raw[0] = byte('@')
78         hex.Encode(raw[1:], tai)
79         return string(raw)
80 }
81
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")
87         }
88         if err == nil {
89                 return ToTime(raw), nil
90         }
91         return time.Time{}, err
92 }