time · math · math/rand Packages — Time, Math, and Random Numbers
Time handling, mathematical calculations, and random number generation are needed in almost every program. Go's standard library provides powerful packages for all three.
time Package — Everything About Time
Current Time and Basic Operations
package main
import (
"fmt"
"time"
)
func main() {
// current time
now := time.Now()
fmt.Println(now) // 2024-01-15 10:30:00.123456789 +0000 UTC
// time components
fmt.Println("year:", now.Year())
fmt.Println("month:", now.Month()) // January
fmt.Println("month (int):", int(now.Month())) // 1
fmt.Println("day:", now.Day())
fmt.Println("hour:", now.Hour())
fmt.Println("minute:", now.Minute())
fmt.Println("second:", now.Second())
fmt.Println("nanosecond:", now.Nanosecond())
fmt.Println("weekday:", now.Weekday()) // Monday
// create specific time
t := time.Date(2024, time.January, 15, 10, 30, 0, 0, time.UTC)
fmt.Println(t) // 2024-01-15 10:30:00 +0000 UTC
// time comparison
past := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
fmt.Println(now.After(past)) // true
fmt.Println(now.Before(past)) // false
fmt.Println(now.Equal(past)) // false
// elapsed time
elapsed := time.Since(past) // same as now.Sub(past)
fmt.Printf("elapsed: %.0f days\n", elapsed.Hours()/24)
// time remaining until a future time
future := time.Date(2025, 12, 31, 0, 0, 0, 0, time.UTC)
remaining := time.Until(future) // same as future.Sub(now)
fmt.Printf("remaining: %.0f days\n", remaining.Hours()/24)
}
Duration — Time Intervals
package main
import (
"fmt"
"time"
)
func main() {
// Duration constants
d1 := 5 * time.Second // 5 seconds
d2 := 2*time.Hour + 30*time.Minute // 2 hours 30 minutes
d3 := 100 * time.Millisecond // 100 milliseconds
fmt.Println(d1) // 5s
fmt.Println(d2) // 2h30m0s
fmt.Println(d3) // 100ms
// Duration unit conversion
d := 90 * time.Minute
fmt.Printf("%.1f hours\n", d.Hours()) // 1.5 hours
fmt.Printf("%.0f minutes\n", d.Minutes()) // 90 minutes
fmt.Printf("%.0f seconds\n", d.Seconds()) // 5400 seconds
// Adding/subtracting time
now := time.Now()
tomorrow := now.Add(24 * time.Hour)
yesterday := now.Add(-24 * time.Hour)
fmt.Println("tomorrow:", tomorrow.Format("2006-01-02"))
fmt.Println("yesterday:", yesterday.Format("2006-01-02"))
// Difference between two times (Duration)
diff := tomorrow.Sub(yesterday)
fmt.Println("difference:", diff) // 48h0m0s
// Performance measurement pattern
start := time.Now()
// simulate work
sum := 0
for i := 0; i < 1_000_000; i++ {
sum += i
}
elapsed := time.Since(start)
fmt.Printf("elapsed: %v, result: %d\n", elapsed, sum)
}
Time Formatting and Parsing
Go's time formatting is unique. It uses the reference time 2006-01-02 15:04:05 to specify layouts.
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
// Go's reference time: Mon Jan 2 15:04:05 MST 2006
// mnemonic: 1 2 3 4 5 6 (month day hour minute second year)
// standard layouts
fmt.Println(now.Format(time.RFC3339)) // 2024-01-15T10:30:00+09:00
fmt.Println(now.Format(time.RFC3339Nano)) // with nanoseconds
fmt.Println(now.Format(time.RFC1123)) // Mon, 15 Jan 2024 10:30:00 KST
fmt.Println(now.Format(time.DateOnly)) // 2024-01-15 (Go 1.20+)
fmt.Println(now.Format(time.TimeOnly)) // 10:30:00 (Go 1.20+)
// custom layouts
fmt.Println(now.Format("January 2, 2006")) // January 15, 2024
fmt.Println(now.Format("15:04:05")) // 10:30:00
fmt.Println(now.Format("2006-01-02 15:04:05")) // 2024-01-15 10:30:00
fmt.Println(now.Format("01/02/2006")) // 01/15/2024 (US format)
fmt.Println(now.Format("Monday, January 2, 2006")) // Monday, January 15, 2024
fmt.Println(now.Format("3:04 PM")) // 10:30 AM
// Parsing — layout and input format must match
layout := "2006-01-02 15:04:05"
t, err := time.Parse(layout, "2024-01-15 10:30:00")
if err != nil {
fmt.Println("parse error:", err)
return
}
fmt.Println("parsed time:", t)
// RFC3339 parsing
t2, _ := time.Parse(time.RFC3339, "2024-01-15T10:30:00+09:00")
fmt.Println("RFC3339:", t2)
// Parse with specific timezone
loc, _ := time.LoadLocation("America/New_York")
t3, _ := time.ParseInLocation(layout, "2024-01-15 10:30:00", loc)
fmt.Println("New York time:", t3)
}
Timezone Handling
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
// Convert to UTC
utc := now.UTC()
fmt.Println("UTC:", utc.Format(time.RFC3339))
// Load timezones
london, _ := time.LoadLocation("Europe/London")
tokyo, _ := time.LoadLocation("Asia/Tokyo")
ny, _ := time.LoadLocation("America/New_York")
// Timezone conversion (same moment, different representation)
fmt.Println("London:", now.In(london).Format("2006-01-02 15:04:05 MST"))
fmt.Println("Tokyo:", now.In(tokyo).Format("2006-01-02 15:04:05 MST"))
fmt.Println("New York:", now.In(ny).Format("2006-01-02 15:04:05 MST"))
// Fixed offset timezone
est := time.FixedZone("EST", -5*60*60) // UTC-5
t := time.Date(2024, 1, 15, 10, 30, 0, 0, est)
fmt.Println("EST:", t.Format(time.RFC3339))
// Unix timestamps
unixTs := now.Unix() // seconds
unixMs := now.UnixMilli() // milliseconds (Go 1.17+)
unixNs := now.UnixNano() // nanoseconds
fmt.Printf("Unix: %d, milli: %d, nano: %d\n", unixTs, unixMs, unixNs)
// Unix timestamp → time.Time
t2 := time.Unix(unixTs, 0)
fmt.Println("restored:", t2.Format(time.RFC3339))
}
Timers and Tickers
package main
import (
"fmt"
"time"
)
func main() {
// time.After — sends time on channel after specified duration
fmt.Println("running after 3 seconds...")
select {
case t := <-time.After(3 * time.Second):
fmt.Println("executed:", t.Format("15:04:05"))
}
// time.NewTimer — cancellable timer
timer := time.NewTimer(2 * time.Second)
go func() {
<-timer.C
fmt.Println("timer fired")
}()
// cancel the timer
if timer.Stop() {
fmt.Println("timer cancelled")
}
// time.NewTicker — cancellable repeating timer
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
count := 0
for t := range ticker.C {
count++
fmt.Printf("tick #%d: %s\n", count, t.Format("15:04:05.000"))
if count >= 3 {
ticker.Stop()
break
}
}
fmt.Println("done")
}
math Package — Mathematical Functions
package main
import (
"fmt"
"math"
)
func main() {
// Mathematical constants
fmt.Printf("Pi: %.10f\n", math.Pi) // 3.1415926536
fmt.Printf("E: %.10f\n", math.E) // 2.7182818285
fmt.Printf("Phi: %.10f\n", math.Phi) // 1.6180339887 (golden ratio)
fmt.Printf("Sqrt2: %.10f\n", math.Sqrt2) // 1.4142135624
fmt.Printf("MaxFloat64: %e\n", math.MaxFloat64)
fmt.Printf("SmallestNonzeroFloat64: %e\n", math.SmallestNonzeroFloat64)
fmt.Println("MaxInt:", math.MaxInt)
fmt.Println("MinInt:", math.MinInt)
// Basic math functions
fmt.Printf("Abs(-3.14): %.2f\n", math.Abs(-3.14)) // 3.14
fmt.Printf("Ceil(3.2): %.0f\n", math.Ceil(3.2)) // 4
fmt.Printf("Floor(3.8): %.0f\n", math.Floor(3.8)) // 3
fmt.Printf("Round(3.5): %.0f\n", math.Round(3.5)) // 4
fmt.Printf("Trunc(3.9): %.0f\n", math.Trunc(3.9)) // 3
// Roots and powers
fmt.Printf("Sqrt(16): %.0f\n", math.Sqrt(16)) // 4
fmt.Printf("Cbrt(27): %.0f\n", math.Cbrt(27)) // 3 (cube root)
fmt.Printf("Pow(2, 10): %.0f\n", math.Pow(2, 10)) // 1024
fmt.Printf("Pow10(3): %.0f\n", float64(math.Pow10(3))) // 1000
// Logarithms and exponentials
fmt.Printf("Log(math.E): %.4f\n", math.Log(math.E)) // 1.0000 (natural log)
fmt.Printf("Log2(8): %.4f\n", math.Log2(8)) // 3.0000
fmt.Printf("Log10(100): %.4f\n", math.Log10(100)) // 2.0000
fmt.Printf("Exp(1): %.4f\n", math.Exp(1)) // 2.7183 (e^1)
// Trigonometry (radians)
fmt.Printf("Sin(π/2): %.4f\n", math.Sin(math.Pi/2)) // 1.0000
fmt.Printf("Cos(0): %.4f\n", math.Cos(0)) // 1.0000
fmt.Printf("Tan(π/4): %.4f\n", math.Tan(math.Pi/4)) // 1.0000
// Min/Max
fmt.Printf("Max(3, 7): %.0f\n", math.Max(3, 7)) // 7
fmt.Printf("Min(3, 7): %.0f\n", math.Min(3, 7)) // 3
fmt.Printf("Mod(10, 3): %.0f\n", math.Mod(10, 3)) // 1
// Special values
fmt.Println("Inf:", math.IsInf(math.Inf(1), 1)) // true (positive infinity)
fmt.Println("NaN:", math.IsNaN(math.NaN())) // true
// Degrees ↔ radians conversion
degrees := 45.0
radians := degrees * math.Pi / 180
fmt.Printf("45° = %.4f radians\n", radians)
fmt.Printf("Sin(45°) = %.4f\n", math.Sin(radians))
}
math/rand Package — Random Number Generation
Basic Random Numbers (Go 1.20+)
package main
import (
"fmt"
"math/rand"
)
func main() {
// Go 1.20+ — use directly without rand.New, auto-seeded
fmt.Println(rand.Int()) // random int
fmt.Println(rand.Intn(100)) // int in [0, 100)
fmt.Println(rand.Float64()) // float64 in [0.0, 1.0)
fmt.Println(rand.Float32()) // float32 in [0.0, 1.0)
// Range-bounded random number
min, max := 10, 50
n := min + rand.Intn(max-min)
fmt.Printf("range [%d, %d): %d\n", min, max, n)
// Shuffle a slice
s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
rand.Shuffle(len(s), func(i, j int) {
s[i], s[j] = s[j], s[i]
})
fmt.Println("shuffled:", s)
// Pick a random element from a slice
fruits := []string{"apple", "banana", "cherry", "grape", "mango"}
picked := fruits[rand.Intn(len(fruits))]
fmt.Println("picked fruit:", picked)
// Probability-based selection (70% chance)
for i := 0; i < 5; i++ {
if rand.Float64() < 0.7 {
fmt.Println("success (70% probability)")
} else {
fmt.Println("failure (30% probability)")
}
}
}
Reproducible Random Numbers — Seeding
package main
import (
"fmt"
"math/rand"
)
func main() {
// Fixed seed — same result every time (useful for tests, simulations)
r1 := rand.New(rand.NewSource(42))
fmt.Println(r1.Intn(100)) // always the same value
fmt.Println(r1.Intn(100)) // always the same value
r2 := rand.New(rand.NewSource(42))
fmt.Println(r2.Intn(100)) // same as r1's first value
fmt.Println(r2.Intn(100)) // same as r1's second value
// Game map generation — reproducible with seed
generateMap(12345)
generateMap(12345) // identical map
generateMap(99999) // different map
}
func generateMap(seed int64) {
r := rand.New(rand.NewSource(seed))
fmt.Printf("map (seed %d): ", seed)
for i := 0; i < 10; i++ {
terrain := []string{"plains", "mountain", "river", "forest"}
fmt.Printf("%s ", terrain[r.Intn(len(terrain))])
}
fmt.Println()
}
crypto/rand — Cryptographically Secure Random Numbers
package main
import (
"crypto/rand"
"encoding/base64"
"fmt"
"math/big"
)
func main() {
// Cryptographically secure random bytes
token := make([]byte, 32)
if _, err := rand.Read(token); err != nil {
panic(err)
}
fmt.Println("random token:", base64.URLEncoding.EncodeToString(token))
// Cryptographic random number in range
max := big.NewInt(100)
n, err := rand.Int(rand.Reader, max)
if err != nil {
panic(err)
}
fmt.Println("cryptographic random [0, 100):", n)
// Secure token generation function
fmt.Println("API key:", generateAPIKey(32))
fmt.Println("session ID:", generateAPIKey(16))
}
func generateAPIKey(length int) string {
b := make([]byte, length)
if _, err := rand.Read(b); err != nil {
panic(err)
}
return base64.RawURLEncoding.EncodeToString(b)
}
Real-World Example 1 — Task Scheduler
package main
import (
"fmt"
"sync"
"time"
)
// Job — a task to execute
type Job struct {
Name string
Interval time.Duration
Fn func()
}
// Scheduler — periodic task runner
type Scheduler struct {
jobs []Job
tickers []*time.Ticker
wg sync.WaitGroup
stop chan struct{}
}
func NewScheduler() *Scheduler {
return &Scheduler{stop: make(chan struct{})}
}
func (s *Scheduler) AddJob(name string, interval time.Duration, fn func()) {
s.jobs = append(s.jobs, Job{name, interval, fn})
}
func (s *Scheduler) Start() {
for _, job := range s.jobs {
job := job // capture loop variable
ticker := time.NewTicker(job.Interval)
s.tickers = append(s.tickers, ticker)
s.wg.Add(1)
go func() {
defer s.wg.Done()
fmt.Printf("[scheduler] job '%s' registered (interval: %v)\n", job.Name, job.Interval)
for {
select {
case t := <-ticker.C:
fmt.Printf("[%s] executing job '%s'\n",
t.Format("15:04:05"), job.Name)
job.Fn()
case <-s.stop:
return
}
}
}()
}
}
func (s *Scheduler) Stop() {
close(s.stop)
for _, t := range s.tickers {
t.Stop()
}
s.wg.Wait()
fmt.Println("[scheduler] all jobs stopped")
}
func main() {
scheduler := NewScheduler()
// register jobs
scheduler.AddJob("health check", 1*time.Second, func() {
fmt.Println(" → server status: ok")
})
scheduler.AddJob("cache refresh", 2*time.Second, func() {
fmt.Println(" → cache data refreshed")
})
scheduler.AddJob("stats aggregation", 3*time.Second, func() {
fmt.Println(" → hourly stats aggregated")
})
scheduler.Start()
// stop scheduler after 8 seconds
time.Sleep(8 * time.Second)
scheduler.Stop()
}
Real-World Example 2 — Statistics Calculator
package main
import (
"fmt"
"math"
"math/rand"
"sort"
)
// Stats — descriptive statistics result
type Stats struct {
Count int
Sum float64
Mean float64
Median float64
StdDev float64
Min float64
Max float64
P95 float64 // 95th percentile
}
func Calculate(data []float64) Stats {
if len(data) == 0 {
return Stats{}
}
n := len(data)
sorted := make([]float64, n)
copy(sorted, data)
sort.Float64s(sorted)
// sum and mean
sum := 0.0
for _, v := range data {
sum += v
}
mean := sum / float64(n)
// standard deviation
variance := 0.0
for _, v := range data {
diff := v - mean
variance += diff * diff
}
variance /= float64(n)
stdDev := math.Sqrt(variance)
// median
var median float64
if n%2 == 0 {
median = (sorted[n/2-1] + sorted[n/2]) / 2
} else {
median = sorted[n/2]
}
// 95th percentile
p95Idx := int(math.Ceil(0.95*float64(n))) - 1
if p95Idx >= n {
p95Idx = n - 1
}
p95 := sorted[p95Idx]
return Stats{
Count: n,
Sum: math.Round(sum*100) / 100,
Mean: math.Round(mean*100) / 100,
Median: math.Round(median*100) / 100,
StdDev: math.Round(stdDev*100) / 100,
Min: sorted[0],
Max: sorted[n-1],
P95: p95,
}
}
func main() {
// generate normally-distributed data using Box-Muller transform
r := rand.New(rand.NewSource(42))
data := make([]float64, 1000)
for i := range data {
// normal distribution (mean=100, stddev=15)
u1 := r.Float64()
u2 := r.Float64()
z := math.Sqrt(-2*math.Log(u1)) * math.Cos(2*math.Pi*u2)
data[i] = 100 + 15*z
}
stats := Calculate(data)
fmt.Printf("=== Descriptive Statistics ===\n")
fmt.Printf("count: %d\n", stats.Count)
fmt.Printf("sum: %.2f\n", stats.Sum)
fmt.Printf("mean: %.2f\n", stats.Mean)
fmt.Printf("median: %.2f\n", stats.Median)
fmt.Printf("std deviation: %.2f\n", stats.StdDev)
fmt.Printf("min: %.2f\n", stats.Min)
fmt.Printf("max: %.2f\n", stats.Max)
fmt.Printf("95th percentile: %.2f\n", stats.P95)
}
Package Summary
| Package | Key Types/Functions | Notes |
|---|---|---|
time | Time, Duration, Timer, Ticker | timezone support, monotonic clock |
math | Sqrt, Pow, Sin, Log, constants | float64 based |
math/rand | Intn, Float64, Shuffle | fast but predictable |
crypto/rand | Read, Int | cryptographically secure, slower |
math/rand vs crypto/rand: Use math/rand for simulations, games, and tests. Always use crypto/rand for security-sensitive values like passwords, tokens, and session IDs.