]> Cypherpunks.ru repositories - gotai64n.git/blob - tai64n.go
Unify copyright comment format
[gotai64n.git] / tai64n.go
1 /*
2 go.cypherpunks.ru/tai64n -- Pure Go TAI64/TAI64N implementation
3 Copyright (C) 2020-2023 Sergey Matveev <stargrave@stargrave.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 // TAI64/TAI64N (http://cr.yp.to/libtai/tai64.html) dealing library.
19 // You can convert time to TAI64/TAI64N and vice versa with it.
20 //
21 //     tai := new(tai64n.TAI64N)
22 //     tai.FromTime(time.Now())
23 //     printable := tai64n.Encode(tai[:])
24 //     decoded, err := tai64n.Decode(printable)
25 //     tai64n.ToTime(tai[:]) == decoded
26 //
27 // By default TAI64 timestamps contain initial 1972-01-01 10-seconds
28 // TAI<->UTC difference. If you need honest TAI representation, then you
29 // should also use Leapsecs* functions.
30 package tai64n
31
32 import (
33         "encoding/binary"
34         "encoding/hex"
35         "errors"
36         "strings"
37         "time"
38 )
39
40 const (
41         TAI64Size  = 8
42         TAI64NSize = TAI64Size + 4
43         LocalFmt   = "2006-01-02 15:04:05.000000000"
44         Base       = 0x4000000000000000 + Leapsecs1972
45 )
46
47 type TAI64 [TAI64Size]byte
48 type TAI64N [TAI64NSize]byte
49
50 func (dst *TAI64) FromTime(src time.Time) {
51         binary.BigEndian.PutUint64(dst[:], uint64(Base)+uint64(src.Unix()))
52 }
53
54 func (dst *TAI64N) FromTime(src time.Time) {
55         binary.BigEndian.PutUint64(dst[:], uint64(Base)+uint64(src.Unix()))
56         binary.BigEndian.PutUint32(dst[8:], uint32(src.Nanosecond()))
57 }
58
59 func ToTime(tai []byte) time.Time {
60         var secs, nano int64
61         switch len(tai) {
62         case TAI64NSize:
63                 nano = int64(binary.BigEndian.Uint32(tai[8:]))
64                 fallthrough
65         case TAI64Size:
66                 secs = int64(binary.BigEndian.Uint64(tai[:8])) - Base
67         default:
68                 panic("invalid tai size")
69         }
70         if secs < 0 {
71                 panic("dates < 1970-01-01 are not supported")
72         }
73         return time.Unix(secs, nano)
74 }
75
76 // Convert TAI64/TAI64N to "@HEX(TAI64)" format.
77 func Encode(tai []byte) string {
78         raw := make([]byte, 1+hex.EncodedLen(len(tai)))
79         raw[0] = byte('@')
80         hex.Encode(raw[1:], tai)
81         return string(raw)
82 }
83
84 // Convert TAI64/TAI64N "@HEX(TAI64)" format to Time.
85 func Decode(s string) (time.Time, error) {
86         raw, err := hex.DecodeString(strings.TrimPrefix(s, "@"))
87         if !(len(raw) == TAI64Size || len(raw) == TAI64NSize) {
88                 err = errors.New("invalid length")
89         }
90         if err == nil {
91                 return ToTime(raw), nil
92         }
93         return time.Time{}, err
94 }