10 const idAlphabet = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
12 // NewID returns a canonical ULID with a millisecond timestamp and 80 random
13 // bits. Times outside the unsigned 48-bit millisecond range are rejected.
14 func NewID(now time.Time) (string, error) {
15 milliseconds := now.UnixMilli()
16 if milliseconds < 0 || milliseconds >= 1<<48 {
17 return "", fmt.Errorf("ID time is outside the ULID range")
22 // Six bytes hold the timestamp and ten bytes hold random data.
23 for index := 5; index >= 0; index-- {
24 data[index] = byte(milliseconds)
28 _, err := rand.Read(data[6:])
30 return "", fmt.Errorf("read ID randomness: %w", err)
35 for index := range text {
39 position := index*5 + bit - 2
42 value |= (data[position/8] >> (7 - position%8)) & 1
46 text[index] = idAlphabet[value]
49 return string(text[:]), nil
52 // ValidID reports whether value is a canonical uppercase ULID.
53 func ValidID(value string) bool {
54 if len(value) != 26 || value[0] > '7' {
58 for _, character := range value {
59 if !strings.ContainsRune(idAlphabet, character) {
67 // ValidIdentifier accepts lowercase ASCII letters and digits with single
68 // internal hyphen or period separators. Empty identifiers are invalid.
69 func ValidIdentifier(value string) bool {
72 for _, character := range value {
74 case character >= 'a' && character <= 'z', character >= '0' && character <= '9':
76 case character == '-' || character == '.':