Time and Date Handling in Go
Go’s time package provides comprehensive support for working with dates, times, durations, and time zones. Understanding time handling is crucial for applications that deal with scheduling, logging, and data processing.
Current Time
Getting the current time:
package main
import (
"fmt"
"time"
)
func main() {
// Current time
now := time.Now()
fmt.Println("Current time:", now)
// Individual components
fmt.Println("Year:", now.Year())
fmt.Println("Month:", now.Month())
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())
// Formatted time
fmt.Println("Formatted:", now.Format("2006-01-02 15:04:05"))
}Time Formatting
Go uses a specific date for formatting reference:
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
// Common formats
fmt.Println("RFC3339:", now.Format(time.RFC3339))
fmt.Println("RFC822:", now.Format(time.RFC822))
fmt.Println("Kitchen:", now.Format(time.Kitchen))
// Custom formats (use reference date: Mon Jan 2 15:04:05 MST 2006)
fmt.Println("YYYY-MM-DD:", now.Format("2006-01-02"))
fmt.Println("DD/MM/YYYY:", now.Format("02/01/2006"))
fmt.Println("HH:MM:SS:", now.Format("15:04:05"))
fmt.Println("Long format:", now.Format("Monday, January 2, 2006 at 3:04 PM"))
// Unix timestamp
fmt.Println("Unix timestamp:", now.Unix())
fmt.Println("Unix nano:", now.UnixNano())
}Parsing Time Strings
Converting strings to time objects:
package main
import (
"fmt"
"time"
)
func main() {
// Parse RFC3339
t1, err := time.Parse(time.RFC3339, "2023-01-15T10:30:00Z")
if err != nil {
fmt.Println("Error parsing RFC3339:", err)
return
}
fmt.Println("Parsed RFC3339:", t1)
// Parse custom format
t2, err := time.Parse("2006-01-02 15:04:05", "2023-12-25 23:59:59")
if err != nil {
fmt.Println("Error parsing custom format:", err)
return
}
fmt.Println("Parsed custom:", t2)
// Parse with location
loc, _ := time.LoadLocation("America/New_York")
t3, err := time.ParseInLocation("2006-01-02 15:04:05", "2023-07-04 12:00:00", loc)
if err != nil {
fmt.Println("Error parsing with location:", err)
return
}
fmt.Println("Parsed with location:", t3)
}Time Zones
Working with different time zones:
package main
import (
"fmt"
"time"
)
func main() {
// Current time in UTC
now := time.Now()
fmt.Println("Local time:", now)
fmt.Println("UTC time:", now.UTC())
// Load specific time zone
nyc, err := time.LoadLocation("America/New_York")
if err != nil {
fmt.Println("Error loading location:", err)
return
}
// Convert to different time zone
nycTime := now.In(nyc)
fmt.Println("New York time:", nycTime)
// Create time in specific location
tokyo, _ := time.LoadLocation("Asia/Tokyo")
tokyoTime := time.Date(2023, 1, 1, 12, 0, 0, 0, tokyo)
fmt.Println("Tokyo time:", tokyoTime)
// List of common locations
locations := []string{
"UTC",
"America/New_York",
"Europe/London",
"Asia/Tokyo",
"Australia/Sydney",
}
fmt.Println("\nCurrent time in different locations:")
for _, locName := range locations {
loc, err := time.LoadLocation(locName)
if err != nil {
continue
}
fmt.Printf("%s: %s\n", locName, now.In(loc).Format("15:04 MST"))
}
}Durations
Working with time durations:
package main
import (
"fmt"
"time"
)
func main() {
// Create durations
duration1 := time.Hour * 2
duration2 := time.Minute * 30
duration3 := time.Second * 45
fmt.Println("2 hours:", duration1)
fmt.Println("30 minutes:", duration2)
fmt.Println("45 seconds:", duration3)
// Parse duration strings
d1, _ := time.ParseDuration("2h30m")
d2, _ := time.ParseDuration("1h45m30s")
d3, _ := time.ParseDuration("300ms")
fmt.Println("Parsed durations:")
fmt.Println("2h30m:", d1)
fmt.Println("1h45m30s:", d2)
fmt.Println("300ms:", d3)
// Duration operations
total := d1 + d2
fmt.Println("Total duration:", total)
// Convert to different units
fmt.Println("Hours:", total.Hours())
fmt.Println("Minutes:", total.Minutes())
fmt.Println("Seconds:", total.Seconds())
fmt.Println("Nanoseconds:", total.Nanoseconds())
}Time Arithmetic
Adding and subtracting time:
package main
import (
"fmt"
"time"
)
func main() {
now := time.Now()
// Add time
future := now.Add(time.Hour * 24) // 1 day from now
fmt.Println("Now:", now)
fmt.Println("Tomorrow:", future)
// Add different durations
nextWeek := now.AddDate(0, 0, 7)
nextMonth := now.AddDate(0, 1, 0)
nextYear := now.AddDate(1, 0, 0)
fmt.Println("Next week:", nextWeek)
fmt.Println("Next month:", nextMonth)
fmt.Println("Next year:", nextYear)
// Subtract time
past := now.Add(-time.Hour * 2) // 2 hours ago
fmt.Println("2 hours ago:", past)
// Calculate difference
diff := future.Sub(now)
fmt.Println("Difference:", diff)
fmt.Println("Hours until future:", diff.Hours())
// Compare times
if future.After(now) {
fmt.Println("Future is after now")
}
if past.Before(now) {
fmt.Println("Past is before now")
}
if now.Equal(now) {
fmt.Println("Times are equal")
}
}Timers and Tickers
Using timers for one-time events and tickers for repeated events:
package main
import (
"fmt"
"time"
)
func main() {
// Timer - fires once after duration
timer := time.NewTimer(2 * time.Second)
fmt.Println("Timer started, waiting...")
<-timer.C // Wait for timer
fmt.Println("Timer fired!")
// Reset timer
timer.Reset(1 * time.Second)
<-timer.C
fmt.Println("Timer fired again!")
// Ticker - fires repeatedly
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
go func() {
for i := 0; i < 5; i++ {
<-ticker.C
fmt.Println("Tick", i+1)
}
ticker.Stop()
}()
// Wait for ticker to finish
time.Sleep(3 * time.Second)
}Sleeping and Waiting
Pause execution:
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("Starting...")
// Sleep for 1 second
time.Sleep(time.Second)
fmt.Println("1 second passed")
// Sleep for duration
duration, _ := time.ParseDuration("500ms")
time.Sleep(duration)
fmt.Println("500ms passed")
// Wait until specific time
targetTime := time.Now().Add(2 * time.Second)
<-time.After(time.Until(targetTime))
fmt.Println("Reached target time")
}Measuring Execution Time
Benchmarking code execution:
package main
import (
"fmt"
"time"
)
func slowOperation() {
time.Sleep(100 * time.Millisecond)
}
func fastOperation() {
sum := 0
for i := 0; i < 1000000; i++ {
sum += i
}
}
func measureExecutionTime(fn func(), name string) {
start := time.Now()
fn()
elapsed := time.Since(start)
fmt.Printf("%s took %v\n", name, elapsed)
}
func main() {
measureExecutionTime(slowOperation, "Slow operation")
measureExecutionTime(fastOperation, "Fast operation")
// More precise measurement
var total time.Duration
iterations := 100
for i := 0; i < iterations; i++ {
start := time.Now()
fastOperation()
total += time.Since(start)
}
average := total / time.Duration(iterations)
fmt.Printf("Average execution time: %v\n", average)
}Time in JSON
Handling time in JSON serialization:
package main
import (
"encoding/json"
"fmt"
"time"
)
type Event struct {
ID int `json:"id"`
Name string `json:"name"`
Timestamp time.Time `json:"timestamp"`
}
func main() {
event := Event{
ID: 1,
Name: "System Startup",
Timestamp: time.Now(),
}
// Marshal to JSON (time becomes RFC3339 string)
jsonData, err := json.MarshalIndent(event, "", " ")
if err != nil {
fmt.Println("Error marshaling:", err)
return
}
fmt.Println("JSON:")
fmt.Println(string(jsonData))
// Unmarshal from JSON
jsonString := `{
"id": 2,
"name": "User Login",
"timestamp": "2023-01-15T10:30:00Z"
}`
var decodedEvent Event
err = json.Unmarshal([]byte(jsonString), &decodedEvent)
if err != nil {
fmt.Println("Error unmarshaling:", err)
return
}
fmt.Printf("Decoded event: %+v\n", decodedEvent)
fmt.Println("Timestamp:", decodedEvent.Timestamp.Format("2006-01-02 15:04:05 MST"))
}Custom Time Formatting
For custom JSON time formats:
package main
import (
"encoding/json"
"fmt"
"time"
)
type CustomTime struct {
time.Time
}
func (ct CustomTime) MarshalJSON() ([]byte, error) {
return json.Marshal(ct.Time.Format("2006-01-02 15:04:05"))
}
func (ct *CustomTime) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
t, err := time.Parse("2006-01-02 15:04:05", s)
if err != nil {
return err
}
ct.Time = t
return nil
}
type Record struct {
ID int `json:"id"`
Time CustomTime `json:"time"`
}
func main() {
record := Record{
ID: 1,
Time: CustomTime{time.Now()},
}
jsonData, _ := json.MarshalIndent(record, "", " ")
fmt.Println("Custom JSON:")
fmt.Println(string(jsonData))
}Best Practices
- Use UTC for storage: Store times in UTC to avoid timezone issues
- Be explicit with locations: Don’t rely on local timezone
- Use time.Time for all time values: Avoid storing as strings or ints
- Handle parsing errors: Always check for errors when parsing
- Use consistent formats: Pick one format and stick with it
- Consider monotonic clocks: For measuring elapsed time
- Avoid time zones in calculations: Convert to UTC first
- Use time.After for timeouts: Instead of time.Sleep in select statements
- Be careful with daylight saving: Time zones can be tricky
- Test time-dependent code: Use fixed times in tests
Go’s time package is powerful and well-designed. With proper understanding of time zones, formatting, and arithmetic, you can handle temporal data effectively in your applications.
For more on JSON handling, check our JSON tutorial. If you need to work with files, see the file I/O tutorial.