Live video on the AT Protocol
79
fork

Configure Feed

Select the types of activity you want to include in your feed.

at natb/resolution-display 90 lines 2.5 kB view raw
1package aqtime 2 3import ( 4 "fmt" 5 "regexp" 6 "strings" 7 "time" 8) 9 10// RE matches the canonical internal format: 2006-01-02T15:04:05.000Z 11// It also accepts the file-safe variant with dashes/dots swapped, for backward compat. 12var RE *regexp.Regexp 13var Pattern string = `(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d)(?:[:-])(\d\d)(?:[:-])(\d\d)(?:[.-])(\d\d\d)Z` 14 15func init() { 16 RE = regexp.MustCompile(fmt.Sprintf(`^%s$`, Pattern)) 17} 18 19var fstr = "2006-01-02T15:04:05.000Z" 20 21type AQTime string 22 23// return a consistently formatted timestamp 24func FromMillis(ms int64) AQTime { 25 return AQTime(time.UnixMilli(ms).UTC().Format(fstr)) 26} 27 28// return a consistently formatted timestamp 29func FromSec(sec int64) AQTime { 30 return AQTime(time.Unix(sec, 0).UTC().Format(fstr)) 31} 32 33func FromString(str string) (AQTime, error) { 34 // Reject -00:00 (valid RFC 3339 but disallowed by ATProto) 35 if strings.HasSuffix(str, "-00:00") { 36 return "", fmt.Errorf("bad time format, -00:00 timezone offset is not allowed, got=%s", str) 37 } 38 39 t, err := time.Parse(time.RFC3339Nano, str) 40 if err != nil { 41 // Fall back to file-safe variant (e.g. 2024-09-13T18-10-17-090Z) 42 if bits := RE.FindStringSubmatch(str); bits != nil { 43 if bits[2] < "01" || bits[2] > "12" || bits[3] < "01" || bits[3] > "31" || 44 bits[4] > "23" || bits[5] > "59" || bits[6] > "60" { 45 return "", fmt.Errorf("bad time format, invalid date/time values in %s", str) 46 } 47 return AQTime(str), nil 48 } 49 return "", fmt.Errorf("bad time format: %w", err) 50 } 51 52 // Reject if UTC normalization results in a negative year 53 utc := t.UTC() 54 if utc.Year() < 0 { 55 return "", fmt.Errorf("bad time format, datetime normalizes to negative year: %s", str) 56 } 57 58 // Normalize to canonical UTC millisecond format 59 return AQTime(utc.Format(fstr)), nil 60} 61 62func FromTime(t time.Time) AQTime { 63 return AQTime(t.UTC().Format(fstr)) 64} 65 66// year, month, day, hour, min, sec, millisecond 67func (aqt AQTime) Parts() (string, string, string, string, string, string, string) { 68 bits := RE.FindStringSubmatch(aqt.String()) 69 return bits[1], bits[2], bits[3], bits[4], bits[5], bits[6], bits[7] 70} 71 72func (aqt AQTime) String() string { 73 return string(aqt) 74} 75 76// version of AQTime suitable for saving as a file (esp on windows) 77func (aqt AQTime) FileSafeString() string { 78 str := string(aqt) 79 str = strings.ReplaceAll(str, ":", "-") 80 str = strings.ReplaceAll(str, ".", "-") 81 return str 82} 83 84func (aqt AQTime) Time() time.Time { 85 t, err := time.Parse(fstr, aqt.String()) 86 if err != nil { 87 panic(err) 88 } 89 return t 90}