For some of our Protobuf DTOs, we use datetime.DateTime to represent the idea of a date and time with time zone. Internally, however, we work exclusively with time.Time
objects because these have standard functions attached to them and are compatible with SQL drivers. The question becomes: how do we convert between them?
The down-conversion was relatively simple:
// DateTimeToTime converts a DateTime to a time.Time
func DateTimeToTime(dt *datetime.DateTime) (*time.Time, error) {
// Attempt to look up the timezone from the ID; if this fails then return an error
locale, err := time.LoadLocation(dt.GetTimeZone().Id)
if err != nil {
return nil, err
}
// Convert the DateTime to a time.Time and return it
converted := time.Date(
int(dt.Year),
time.Month(dt.Month),
int(dt.Day),
int(dt.Hours),
int(dt.Minutes),
int(dt.Seconds),
int(dt.Nanos),
locale)
return &converted, nil
}
but I'm not sure how to get datetime.DateTime_TimeZone
from a time.Location
object. I figured that Google would have a conversion available since they wrote both time.Time
and datetime.DateTime
but I can't seem to find one. How could this be done?
you can use this code:
package main
import (
"fmt"
"log"
"time"
"google.golang.org/genproto/googleapis/type/datetime"
)
// DateTimeToTime converts a DateTime to a time.Time
func DateTimeToTime(dt *datetime.DateTime) (*time.Time, error) {
// Attempt to look up the timezone from the ID; if this fails then return an error
locale, err := time.LoadLocation(dt.GetTimeZone().Id)
if err != nil {
return nil, err
}
// Convert the DateTime to a time.Time and return it
converted := time.Date(
int(dt.Year),
time.Month(dt.Month),
int(dt.Day),
int(dt.Hours),
int(dt.Minutes),
int(dt.Seconds),
int(dt.Nanos),
locale)
return &converted, nil
}
func TimeToDateTime(t *time.Time) (*datetime.DateTime, error) {
converted := datetime.DateTime{
Year: int32(t.Year()),
Month: int32(t.Month()),
Day: int32(t.Day()),
Hours: int32(t.Hour()),
Minutes: int32(t.Minute()),
Seconds: int32(t.Second()),
Nanos: int32(t.Nanosecond()),
TimeOffset: &datetime.DateTime_TimeZone{
TimeZone: &datetime.TimeZone{
Id: t.Location().String(),
},
},
}
return &converted, nil
}
func main() {
t := time.Now()
dt, err := TimeToDateTime(&t)
if err != nil {
log.Fatalln(err)
}
fmt.Printf("dt: %s\n", dt.String())
t2, err := DateTimeToTime(dt)
if err != nil {
log.Fatalln(err)
}
fmt.Printf("t: %+v\n", t2)
loc, err := time.LoadLocation("Asia/Tehran")
if err != nil {
log.Fatalln(err)
}
t, err = time.ParseInLocation(time.DateTime, time.Now().Format(time.DateTime), loc)
if err != nil {
log.Fatalln(err)
}
dt, err = TimeToDateTime(&t)
if err != nil {
log.Fatalln(err)
}
fmt.Printf("dt: %s\n", dt.String())
t2, err = DateTimeToTime(dt)
if err != nil {
log.Fatalln(err)
}
fmt.Printf("t: %+v\n", t2)
}
Sample output:
dt: year:2025 month:4 day:14 hours:12 minutes:37 seconds:17 nanos:776791506 time_zone:{id:"Local"}
t: 2025-04-14 12:37:17.776791506 +0330 +0330
dt: year:2025 month:4 day:14 hours:12 minutes:37 seconds:17 time_zone:{id:"Asia/Tehran"}
t: 2025-04-14 12:37:17 +0330 +0330