fork of indigo with slightly nicer lexgen

syntax: datetime helpers

+31
atproto/syntax/datetime.go
··· 4 4 "fmt" 5 5 "regexp" 6 6 "strings" 7 + "time" 8 + ) 9 + 10 + const ( 11 + // Prefered atproto Datetime string syntax, for use with [time.Format]. 12 + // 13 + // Note that *parsing* syntax is more flexible. 14 + AtprotoDatetimeLayout = "2006-01-02T15:04:05.999Z" 7 15 ) 8 16 9 17 // Represents the a Datetime in string format, as would pass Lexicon syntax validation: the intersection of RFC-3339 and ISO-8601 syntax. ··· 23 31 return "", fmt.Errorf("Datetime can't use '-00:00' for UTC timezone, must use '+00:00', per ISO-8601") 24 32 } 25 33 return Datetime(raw), nil 34 + } 35 + 36 + // Parses a string to a golang time.Time in a single step. 37 + func ParseDatetimeTime(raw string) (time.Time, error) { 38 + var zero time.Time 39 + d, err := ParseDatetime(raw) 40 + if err != nil { 41 + return zero, err 42 + } 43 + return d.Time() 44 + } 45 + 46 + // Parses the Datetime string in to a golang time.Time. 47 + // 48 + // There are a small number of strings which will pass initial syntax validation but fail when actually parsing, so this function can return an error. Use [ParseDatetimeTime] to fully parse in a single function call. 49 + func (d Datetime) Time() (time.Time, error) { 50 + return time.Parse(time.RFC3339Nano, d.String()) 51 + } 52 + 53 + // Creates a new valid Datetime string matching the current time, in prefered syntax. 54 + func DatetimeNow() Datetime { 55 + t := time.Now().UTC() 56 + return Datetime(t.Format(AtprotoDatetimeLayout)) 26 57 } 27 58 28 59 func (d Datetime) String() string {
+31 -3
atproto/syntax/datetime_test.go
··· 9 9 "github.com/stretchr/testify/assert" 10 10 ) 11 11 12 - func TestInteropDatetimesValid(t *testing.T) { 12 + func TestInteropDatetimeValid(t *testing.T) { 13 13 assert := assert.New(t) 14 14 file, err := os.Open("testdata/datetime_syntax_valid.txt") 15 15 assert.NoError(err) ··· 20 20 if len(line) == 0 || line[0] == '#' { 21 21 continue 22 22 } 23 - _, err := ParseDatetime(line) 23 + _, err := ParseDatetimeTime(line) 24 24 if err != nil { 25 25 fmt.Println("GOOD: " + line) 26 26 } ··· 29 29 assert.NoError(scanner.Err()) 30 30 } 31 31 32 - func TestInteropDatetimesInvalid(t *testing.T) { 32 + func TestInteropDatetimeInvalid(t *testing.T) { 33 33 assert := assert.New(t) 34 34 file, err := os.Open("testdata/datetime_syntax_invalid.txt") 35 35 assert.NoError(err) ··· 48 48 } 49 49 assert.NoError(scanner.Err()) 50 50 } 51 + 52 + func TestInteropDatetimeTimeInvalid(t *testing.T) { 53 + assert := assert.New(t) 54 + file, err := os.Open("testdata/datetime_parse_invalid.txt") 55 + assert.NoError(err) 56 + defer file.Close() 57 + scanner := bufio.NewScanner(file) 58 + for scanner.Scan() { 59 + line := scanner.Text() 60 + if len(line) == 0 || line[0] == '#' { 61 + continue 62 + } 63 + _, err := ParseDatetimeTime(line) 64 + if err == nil { 65 + fmt.Println("BAD: " + line) 66 + } 67 + assert.Error(err) 68 + } 69 + assert.NoError(scanner.Err()) 70 + } 71 + 72 + func TestInteropDatetimeNow(t *testing.T) { 73 + assert := assert.New(t) 74 + 75 + dt := DatetimeNow() 76 + _, err := ParseDatetimeTime(dt.String()) 77 + assert.NoError(err) 78 + }
+7
atproto/syntax/testdata/datetime_parse_invalid.txt
··· 1 + # superficial syntax parses ok, but are not valid datetimes for semantic reasons (eg, "month zero") 2 + #1985-00-12T23:20:50.123Z 3 + #1985-04-00T23:20:50.123Z 4 + #1985-13-12T23:20:50.123Z 5 + #1985-04-12T25:20:50.123Z 6 + #1985-04-12T23:61:50.123Z 7 + #1985-04-12T23:20:61.123Z
-8
atproto/syntax/testdata/datetime_syntax_invalid.txt
··· 16 16 1985-04-12T23:20:50.123Z 17 17 1985-04-12T 23:20:50.123Z 18 18 19 - # TODO: full parse to validate? 20 - #1985-00-12T23:20:50.123Z 21 - #1985-04-00T23:20:50.123Z 22 - #1985-13-12T23:20:50.123Z 23 - #1985-04-12T25:20:50.123Z 24 - #1985-04-12T23:61:50.123Z 25 - #1985-04-12T23:20:61.123Z 26 - 27 19 # not enough zero padding 28 20 1985-4-12T23:20:50.123Z 29 21 1985-04-2T23:20:50.123Z
+14
atproto/syntax/testdata/datetime_syntax_valid.txt
··· 18 18 0985-04-12T23:20:50.123-07:00 19 19 1985-04-12T23:20:50.123-07:00 20 20 0123-01-01T00:00:00.000Z 21 + 22 + # various precisions, up through at least 12 digits 23 + 1985-04-12T23:20:50.1Z 24 + 1985-04-12T23:20:50.12Z 25 + 1985-04-12T23:20:50.123Z 26 + 1985-04-12T23:20:50.1234Z 27 + 1985-04-12T23:20:50.12345Z 28 + 1985-04-12T23:20:50.123456Z 29 + 1985-04-12T23:20:50.1234567Z 30 + 1985-04-12T23:20:50.12345678Z 31 + 1985-04-12T23:20:50.123456789Z 32 + 1985-04-12T23:20:50.1234567890Z 33 + 1985-04-12T23:20:50.12345678901Z 34 + 1985-04-12T23:20:50.123456789012Z