A high-performance, zero-dependency Go library for working with Discord snowflake IDs.
go get github.com/goland-express/snowflakepackage main
import (
"fmt"
"time"
"github.com/goland-express/snowflake"
)
func main() {
// Parse a snowflake from string
id, _ := snowflake.Parse("175928847299117063")
// Extract timestamp
fmt.Println("Created at:", id.Time())
// Output: Created at: 2016-04-30 11:18:25.796 -0300 -03
// Get components
fmt.Println("Worker ID:", id.WorkerID())
fmt.Println("Process ID:", id.ProcessID())
fmt.Println("Sequence:", id.Sequence())
// Create a new snowflake
newID := snowflake.NewFromTimestamp(time.Now())
// Compare snowflakes
if id.Before(newID) {
fmt.Println("id was created before newID")
}
}// Parse from string
id, err := snowflake.Parse("175928847299117063")
// Parse with validation (checks if within Discord's valid range)
id, err := snowflake.ParseStrict("175928847299117063")
// Parse and panic on error
id := snowflake.MustParse("175928847299117063")
// Parse from environment variable
id := snowflake.Getenv("DISCORD_CHANNEL_ID")
id, found := snowflake.LookupEnv("DISCORD_CHANNEL_ID")// Create from timestamp (worker, process, sequence = 0)
id := snowflake.NewFromTimestamp(time.Now())
// Create with all components
id := snowflake.New(time.Now(), workerID, processID, sequence)
// Create a range for time-based queries
minID, maxID := snowflake.NewRange(startTime, endTime)
// Use in SQL: WHERE id BETWEEN minID AND maxID// Get timestamp
timestamp := id.Time()
// Get individual components
workerID := id.WorkerID() // 5 bits
processID := id.ProcessID() // 5 bits
sequence := id.Sequence() // 12 bits
// Get all at once
deconstructed := id.Deconstruct()
fmt.Printf("Time: %v, Worker: %d, Process: %d, Seq: %d\n",
deconstructed.Time,
deconstructed.WorkerID,
deconstructed.ProcessID,
deconstructed.Sequence)// Check if zero
if id.IsZero() {
fmt.Println("Empty snowflake")
}
// Check if non-zero
if id.Valid() {
fmt.Println("Valid snowflake")
}
// Check if within Discord's theoretical valid range
if id.IsValid() {
fmt.Println("Valid Discord snowflake")
}id1 := snowflake.ID(100)
id2 := snowflake.ID(200)
// Compare
if id1.Before(id2) { }
if id1.After(id2) { }
if id1.Equal(id2) { }
// Three-way comparison (-1, 0, 1)
result := id1.Compare(id2)
// Check if in range (inclusive)
if id.IsBetween(minID, maxID) { }type Message struct {
ID snowflake.ID `json:"id"`
Content string `json:"content"`
}
// Marshals as string: {"id":"175928847299117063"}
msg := Message{ID: 175928847299117063, Content: "Hello"}
json.Marshal(msg)
// Unmarshals from string or number
json.Unmarshal([]byte(`{"id":"175928847299117063"}`), &msg)
json.Unmarshal([]byte(`{"id":175928847299117063}`), &msg)
// Allow unquoted numbers (off by default)
snowflake.AllowUnquoted = true// Convert to string
str := id.String()
// Implements fmt.Stringer
fmt.Printf("ID: %s\n", id)snowflake.Epoch // Discord epoch (1420070400000)
snowflake.MinID // Minimum possible ID (0)
snowflake.MaxID // Maximum possible ID
snowflake.FirstDiscordSnowflake // First real Discord snowflake (175928847299117063)Discord snowflakes are 64-bit unsigned integers with the following structure:
64 22 17 12 0
┌───────────────────────────────────────────┬──────┬──────┬──────────┐
│ Timestamp (42 bits) │Worker│Process│Increment│
│ Milliseconds since Discord Epoch │ (5) │ (5) │ (12) │
└───────────────────────────────────────────┴──────┴──────┴──────────┘
- Timestamp (42 bits): Milliseconds since Discord Epoch (January 1, 2015)
- Worker ID (5 bits): Internal worker ID (0-31)
- Process ID (5 bits): Internal process ID (0-31)
- Increment (12 bits): Sequence number (0-4095) for IDs generated in the same millisecond
Benchmarks:
BenchmarkParse-12 100000000 11.72 ns/op 0 B/op 0 allocs/op
BenchmarkNew-12 1000000000 0.24 ns/op 0 B/op 0 allocs/op
BenchmarkString-12 61432237 19.20 ns/op 0 B/op 0 allocs/op
BenchmarkTime-12 1000000000 0.24 ns/op 0 B/op 0 allocs/op
BenchmarkMarshalJSON-12 58816952 20.28 ns/op 0 B/op 0 allocs/op
BenchmarkUnmarshalJSON-12 44850418 26.43 ns/op 0 B/op 0 allocs/op
BenchmarkComparison-12 1000000000 0.24 ns/op 0 B/op 0 allocs/op
// Get all messages from yesterday
start := time.Now().Add(-24 * time.Hour)
end := time.Now()
minID, maxID := snowflake.NewRange(start, end)
// SQL: SELECT * FROM messages WHERE id BETWEEN ? AND ?
db.Query("SELECT * FROM messages WHERE id BETWEEN ? AND ?", minID, maxID)// Sort messages by snowflake (chronologically)
sort.Slice(messages, func(i, j int) bool {
return messages[i].ID.Before(messages[j].ID)
})// Validate user input
id, err := snowflake.ParseStrict(userInput)
if err != nil {
return fmt.Errorf("invalid Discord ID: %w", err)
}Pull requests are welcome. For major changes, open an issue first to discuss what you want to change.
MIT