diff options
Diffstat (limited to 'hotline/time.go')
| -rw-r--r-- | hotline/time.go | 27 |
1 files changed, 17 insertions, 10 deletions
diff --git a/hotline/time.go b/hotline/time.go index 671e036..b6d2d9c 100644 --- a/hotline/time.go +++ b/hotline/time.go @@ -8,17 +8,26 @@ import ( type Time [8]byte +// macEpoch is the classic Mac OS time epoch: seconds are counted from +// midnight, January 1, 1904 (local time). The 4-byte seconds field of the +// Hotline time format overflows a uint32 on 2040-02-06; the original protocol +// shares this limitation. +var macEpoch = time.Date(1904, time.January, 1, 0, 0, 0, 0, time.Local) + // NewTime converts a time.Time to the 8 byte Hotline time format: -// Year (2 bytes), milliseconds (2 bytes) and seconds (4 bytes) +// Year (2 bytes), milliseconds (2 bytes) and seconds (4 bytes). +// +// The seconds field holds seconds since the classic Mac OS epoch +// (1904-01-01), matching the original Hotline server. Some clients (e.g. +// Pitbull Pro) ignore the year field and decode the seconds field as a raw +// Mac timestamp; encoding seconds-since-start-of-year instead made those +// clients always display the year 1904 (see issue #166). func NewTime(t time.Time) (b Time) { yearBytes := make([]byte, 2) secondBytes := make([]byte, 4) - // Get a time.Time for January 1st 00:00 from t so we can calculate the difference in seconds from t - startOfYear := time.Date(t.Year(), time.January, 1, 0, 0, 0, 0, time.Local) - binary.BigEndian.PutUint16(yearBytes, uint16(t.Year())) - binary.BigEndian.PutUint32(secondBytes, uint32(t.Sub(startOfYear).Seconds())) + binary.BigEndian.PutUint32(secondBytes, uint32(t.Sub(macEpoch).Seconds())) return [8]byte(slices.Concat( yearBytes, @@ -28,14 +37,12 @@ func NewTime(t time.Time) (b Time) { } // Time converts the Hotline Time format to a Go time.Time. -// The Hotline format stores: Year (2 bytes) + milliseconds (2 bytes, unused) + seconds since Jan 1 (4 bytes) +// The Hotline format stores: Year (2 bytes) + milliseconds (2 bytes, unused) + +// seconds since the Mac OS epoch (4 bytes). func (t Time) Time() time.Time { - year := binary.BigEndian.Uint16(t[0:2]) seconds := binary.BigEndian.Uint32(t[4:8]) - // Create start of year, then add seconds - startOfYear := time.Date(int(year), time.January, 1, 0, 0, 0, 0, time.Local) - return startOfYear.Add(time.Duration(seconds) * time.Second) + return macEpoch.Add(time.Duration(seconds) * time.Second) } // Format returns the time formatted according to the layout string. |