--- /dev/null
+/*
+go.cypherpunks.ru/tai64n -- Pure Go TAI64N implementation
+Copyright (C) 2020 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/>.
+*/
+
+package tai64n
+
+import (
+ "encoding/binary"
+ "encoding/hex"
+ "errors"
+ "strings"
+ "time"
+)
+
+const (
+ Size = 12
+ Base = 0x400000000000000a
+ LocalFmt = "2006-01-02 15:04:05.000000000"
+)
+
+type TAI64N [Size]byte
+
+func FromTime(t time.Time, tai *TAI64N) {
+ binary.BigEndian.PutUint64(tai[:], uint64(Base)+uint64(t.Unix()))
+ binary.BigEndian.PutUint32(tai[8:], uint32(t.Nanosecond()))
+}
+
+func ToTime(tai []byte) time.Time {
+ if len(tai) != Size {
+ panic("invalid size")
+ }
+ secs := int64(binary.BigEndian.Uint64(tai[:8]))
+ nano := int64(binary.BigEndian.Uint32(tai[8:]))
+ return time.Unix(secs-Base, nano)
+}
+
+func (tai TAI64N) Encode() string {
+ return "@" + hex.EncodeToString(tai[:])
+}
+
+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")
+ }
+ if err != nil {
+ return time.Time{}, err
+ }
+ return ToTime(tai), nil
+}