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