gotimeprotocol-buffers

Converting time.Time to datetime.DateTime


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?


Solution

  • 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