Files
rands/uuid/uuid.go
Jim Myhrberg fe4308607c feat(strings/uuidv7): add UUIDv7 generation (#10)
The UUID v7 format is a time-ordered random UUID. It uses a timestamp
with millisecond precision in the most significant bits, followed by
random data. This provides both uniqueness and chronological ordering,
making it ideal for database primary keys and situations where sorting
by creation time is desired.

References:
- https://uuid7.com/
- https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-04.html#name-uuid-version-7
2025-02-28 02:16:32 +00:00

102 lines
2.2 KiB
Go

// Package uuid provides a UUID type and associated utilities.
package uuid
import (
"encoding/hex"
"errors"
"fmt"
"math"
"strings"
"time"
)
var (
Err = errors.New("uuid")
ErrInvalidLength = fmt.Errorf("%w: invalid length", Err)
)
const (
hyphen = '-'
)
// UUID represents a Universally Unique Identifier (UUID).
// It is implemented as a 16-byte array.
type UUID [16]byte
// String returns the string representation of the UUID,
// formatted according to RFC 4122 (8-4-4-4-12 hex digits separated by hyphens).
func (u UUID) String() string {
dst := make([]byte, 36)
hex.Encode(dst[0:8], u[0:4])
dst[8] = hyphen
hex.Encode(dst[9:13], u[4:6])
dst[13] = hyphen
hex.Encode(dst[14:18], u[6:8])
dst[18] = hyphen
hex.Encode(dst[19:23], u[8:10])
dst[23] = hyphen
hex.Encode(dst[24:], u[10:])
return string(dst)
}
// FromBytes creates a UUID from a byte slice.
//
// If the slice isn't exactly 16 bytes, it returns an empty UUID.
func FromBytes(b []byte) (UUID, error) {
var u UUID
if len(b) != 16 {
return u, ErrInvalidLength
}
copy(u[:], b)
return u, nil
}
// FromString creates a UUID from a string.
//
// If the string isn't exactly 36 characters, it returns an empty UUID.
func FromString(s string) (UUID, error) {
if len(s) != 36 {
return UUID{}, ErrInvalidLength
}
raw := strings.ReplaceAll(s, "-", "")
u := UUID{}
_, err := hex.Decode(u[:], []byte(raw))
if err != nil {
return UUID{}, err
}
return u, nil
}
// Time returns the timestamp of the UUID if it's a version 7 (time-ordered)
// UUID. Otherwise, it returns the zero time.
func (u UUID) Time() (t time.Time, ok bool) {
if u.Version() != 7 {
return time.Time{}, false
}
// Extract the timestamp from the UUID.
// For UUIDv7, only the first 6 bytes contain the timestamp in milliseconds
timestamp := uint64(u[0])<<40 | uint64(u[1])<<32 | uint64(u[2])<<24 |
uint64(u[3])<<16 | uint64(u[4])<<8 | uint64(u[5])
if timestamp > math.MaxInt64 {
// This shouldn't happen until year 292,272,993.
return time.Time{}, false
}
return time.UnixMilli(int64(timestamp)), true
}
// Version returns the version of the UUID.
func (u UUID) Version() int {
// The version is stored in the 4 most significant bits of byte 6
return int(u[6] >> 4)
}