1package main 2 3import ( 4 cryptorand "crypto/rand" 5 "fmt" 6 mathrand "math/rand" 7 "os" 8 "strings" 9 "time" 10 11 "github.com/oklog/ulid" 12 getopt "github.com/pborman/getopt/v2" 13) 14 15const ( 16 defaultms = "Mon Jan 02 15:04:05.999 MST 2006" 17 rfc3339ms = "2006-01-02T15:04:05.999MST" 18) 19 20func main() { 21 // Completely obnoxious. 22 getopt.HelpColumn = 50 23 getopt.DisplayWidth = 140 24 25 fs := getopt.New() 26 var ( 27 format = fs.StringLong("format", 'f', "default", "when parsing, show times in this format: default, rfc3339, unix, ms", "<format>") 28 local = fs.BoolLong("local", 'l', "when parsing, show local time instead of UTC") 29 quick = fs.BoolLong("quick", 'q', "when generating, use non-crypto-grade entropy") 30 zero = fs.BoolLong("zero", 'z', "when generating, fix entropy to all-zeroes") 31 help = fs.BoolLong("help", 'h', "print this help text") 32 ) 33 if err := fs.Getopt(os.Args, nil); err != nil { 34 fmt.Fprintf(os.Stderr, "%v\n", err) 35 os.Exit(1) 36 } 37 if *help { 38 fs.PrintUsage(os.Stderr) 39 os.Exit(0) 40 } 41 42 var formatFunc func(time.Time) string 43 switch strings.ToLower(*format) { 44 case "default": 45 formatFunc = func(t time.Time) string { return t.Format(defaultms) } 46 case "rfc3339": 47 formatFunc = func(t time.Time) string { return t.Format(rfc3339ms) } 48 case "unix": 49 formatFunc = func(t time.Time) string { return fmt.Sprint(t.Unix()) } 50 case "ms": 51 formatFunc = func(t time.Time) string { return fmt.Sprint(t.UnixNano() / 1e6) } 52 default: 53 fmt.Fprintf(os.Stderr, "invalid --format %s\n", *format) 54 os.Exit(1) 55 } 56 57 switch args := fs.Args(); len(args) { 58 case 0: 59 generate(*quick, *zero) 60 default: 61 parse(args[0], *local, formatFunc) 62 } 63} 64 65func generate(quick, zero bool) { 66 entropy := cryptorand.Reader 67 if quick { 68 seed := time.Now().UnixNano() 69 source := mathrand.NewSource(seed) 70 entropy = mathrand.New(source) 71 } 72 if zero { 73 entropy = zeroReader{} 74 } 75 76 id, err := ulid.New(ulid.Timestamp(time.Now()), entropy) 77 if err != nil { 78 fmt.Fprintf(os.Stderr, "%v\n", err) 79 os.Exit(1) 80 } 81 82 fmt.Fprintf(os.Stdout, "%s\n", id) 83} 84 85func parse(s string, local bool, f func(time.Time) string) { 86 id, err := ulid.Parse(s) 87 if err != nil { 88 fmt.Fprintf(os.Stderr, "%v\n", err) 89 os.Exit(1) 90 } 91 92 t := ulid.Time(id.Time()) 93 if !local { 94 t = t.UTC() 95 } 96 fmt.Fprintf(os.Stderr, "%s\n", f(t)) 97} 98 99type zeroReader struct{} 100 101func (zeroReader) Read(p []byte) (int, error) { 102 for i := range p { 103 p[i] = 0 104 } 105 return len(p), nil 106} 107