···11+package main
22+33+import (
44+ "fmt"
55+ "log/slog"
66+ "os"
77+88+ "github.com/bluesky-social/indigo/atproto/syntax"
99+1010+ "github.com/urfave/cli/v2"
1111+)
1212+1313+func main() {
1414+ app := cli.App{
1515+ Name: "atp-syntax",
1616+ Usage: "informal debugging CLI tool for atproto syntax (identifiers)",
1717+ }
1818+ app.Commands = []*cli.Command{
1919+ &cli.Command{
2020+ Name: "parse-tid",
2121+ Usage: "parse a TID and output timestamp",
2222+ Action: runParseTID,
2323+ },
2424+ }
2525+ h := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug})
2626+ slog.SetDefault(slog.New(h))
2727+ app.RunAndExitOnError()
2828+}
2929+3030+func runParseTID(cctx *cli.Context) error {
3131+ s := cctx.Args().First()
3232+ if s == "" {
3333+ return fmt.Errorf("need to provide identifier as an argument")
3434+ }
3535+3636+ tid, err := syntax.ParseTID(s)
3737+ if err != nil {
3838+ return err
3939+ }
4040+ fmt.Printf("TID: %s\n", tid)
4141+ fmt.Printf("Time: %s\n", tid.Time())
4242+4343+ return nil
4444+}
+26
atproto/syntax/datetime.go
···5050 return d.Time(), nil
5151}
52525353+// Similar to ParseDatetime, but more flexible about some parsing.
5454+//
5555+// Note that this may mutate the internal string, so a round-trip will fail. This is intended for working with legacy/broken records, not to be used in an ongoing way.
5656+func ParseDatetimeLenient(raw string) (Datetime, error) {
5757+ // fast path: it is a valid overall datetime
5858+ valid, err := ParseDatetime(raw)
5959+ if nil == err {
6060+ return valid, nil
6161+ }
6262+6363+ if strings.HasSuffix(raw, "-00:00") {
6464+ return ParseDatetime(strings.Replace(raw, "-00:00", "+00:00", 1))
6565+ }
6666+6767+ // try adding timezone if it is missing
6868+ var hasTimezoneRegex = regexp.MustCompile(`^.*(([+-]\d\d:?\d\d)|[a-zA-Z])$`)
6969+ if !hasTimezoneRegex.MatchString(raw) {
7070+ withTZ, err := ParseDatetime(raw + "Z")
7171+ if nil == err {
7272+ return withTZ, nil
7373+ }
7474+ }
7575+7676+ return "", fmt.Errorf("Datetime could not be parsed, even leniently: %v", err)
7777+}
7878+5379// Parses the Datetime string in to a golang [time.Time].
5480//
5581// This method assumes that [ParseDatetime] was used to create the Datetime, which already verified parsing, and thus that [time.Parse] will always succeed. In the event of an error, zero/nil will be returned.