Why is the user defined time's location nil - go

I'm using go 1.13 and I have a user defined type of type time.Time, and when creating that value with a given location of UTC, the loc attribute is still nil (having a nil loc causes panics in certain time functions, so this is not acceptable). Playground here.
type CustomTime time.Time
func main() {
t := CustomTime(time.Date(2020, time.July, 23, 1, 0, 0, 0, time.UTC))
fmt.Printf("%+v",t) // prints {wall:0 ext:63731062800 loc:<nil>}
}
FYI: background info, I'm using this custom time to implement Scan() for my database handler, and when I compare a custom time value defined above (with nil location to the value from the db (non-nil location), my tests are failing due to the comparison failing. Any help or pointers in the right direction would be greatly appreciated.

If you look at the doc, time.Time is of type
type Time struct {
//...
wall uint64
ext int64
// loc specifies the Location that should be used to
// determine the minute, hour, month, day, and year
// that correspond to this Time.
// The nil location means UTC.
// All UTC times are represented with loc==nil, never loc==&utcLoc.
loc *Location
}
nil loc actually means UTC. You can verify the same by printing the equality
fmt.Println(time.UTC == time.Time(t).Location())
// Output: true
You see a nil when you print t because you are literally printing the struct Time without using its default Stringer as you have wrapped it with a Custom Type i.e. CustomTime. Hence the loc field will be nil.
fmt.Printf("%+v", time.Time(t))
// This will print UTC for the location.
If you want to use CustomTime everywhere, instead of creating a type alias you can embed time.Time in a struct so that CustomTime can behave like time.Time
type CustomTime struct {
time.Time
}
func main() {
t := CustomTime{time.Date(2020, time.July, 23, 1, 0, 0, 0, time.UTC)}
fmt.Printf("%+v", t) // Prints: 2020-07-23 01:00:00 +0000 UTC
}

Related

Why does *time.Time display as the timestamp instead of memory location?

I've been working on a hobby project and have gotten to the point where I need to differentiate between an initialized zero value, and an intentional zero value for any partial update capable requests. After a lot of reading, I went with the route of making all of my incoming struct fields pointers. Since pointers initialize to nil, and the JSON marshaller binds zero values, it lets me make that distinguishment. I started sending some API requests over, and was seeing what I expect at first, results looking like:
{0xc00058e240 <nil>}
When I added a time.Time field to the struct though, and sent a timestamp over, I see this:
{0xc0004060d0 <nil> 2004-10-16 00:00:00 +0000 UTC}
I would have expected the timestamp to also be a pointer to some memory location. I tested a little more with the methods below, and it still prints a timestamp. I was under the impression if you tried to use *variable in an operation, it would throw an error for you. The code below works as expected, so it's like time.Time is a pointer even though it prints right from the struct in its normal format.
type updateTestRequest struct {
TestString *string `json:"test_string" binding:"alphanum"`
TestName *string `json:"test_name,omitempty"`
TestTime *time.Time `json:"test_time" time_format:"2006-01-02" binding:"required"`
}
func (server *Server) updateTest(ctx *gin.Context) {
var req updateUserRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusBadRequest, errorResponse(err))
return
}
fmt.Printf("%v", req)
if req.TestTime != nil {
fmt.Printf("value: '%v'", *req.TestTime)
} else {
println("it was nil.")
}
}
{
"test_string":"Testing",
"first_name": "",
"date_of_birth": "2004-10-16T00:00:00.00Z"
}
I'm still pretty new to Golang, so it could be my misunderstanding of the language. Why does timestamp not print out as a memory address like the other pointers?
The time package documentation shows that time.Time has a String() string method. Because the pointer receiver method set includes the value receiver methods, a *time.Time also has a String() string method.
The fmt package documentation says:
If the format (which is implicitly %v for Println etc.) is valid for a string (%s %q %v %x %X), the following two rules apply:
If an operand implements method String() string, that method will be invoked to convert the object to a string, which will then be formatted as required by the verb (if any).
It follows that the *time.Time is displayed using the result of the time's String() method.

Idiomatic way to represent optional time.Time in a struct

I've read both Optional Parameters? and Golang pass nil as optional argument to a function?
And still wonder if my case is more specific.
What I've got is:
type Periodical struct {
Interval *interval.Interval
StartsAt time.Time
EndsAt time.Time
}
to represent periodical event which has a start date and may or may not have an end date (periodical event runs for indefinite amount of time).
eachYear := Periodical{
interval.Years(1),
StartsAt: time.Parse("2 Jan 2006", "1 Jan 1970")}
Will throw
periodical/periodical.go:31:39: cannot use nil as type time.Time in field value
Which is understood, - I didn't specify EndsAt time.Time.
But what do I really do there then?
Am I forced to have a special flag to neglect EndsAt like so?
type Periodical struct {
Interval *interval.Interval
StartsAt time.Time
EndsAt time.Time
isIndefinite bool // This looks ugly already
}
and then if I want Yearly / Anually I do something like
eachYear := Periodical{
interval.Years(1),
time.Parse("2 Jan 2006", "1 Jan 1970"),
time.Parse("2 Jan 2006", "1 Jan 1970"),
isIndefinite: true}
Although, I can then account for this flag in business logic, but this EndsAt set to the same (or any other) date looks kind of dull.
I also define a method on periodical package which allows to have a shorthand periodical event like so:
func Monthly(s, e time.Time) Periodical {
return Periodical{StartsAt: s, EndsAt: e, Interval: interval.Months(1)}
}
What do I do to omit end (the second param)? Am I forced to either have separate method for that or do something that looks a bit funky and lacks readability:
func Monthly(s time.Time, end ...time.Time) Periodical {
if len(end) == 1 {
return Periodical{
StartsAt: s,
EndsAt: end[0],
Interval: interval.Months(1),
isIndefinite: false}
} else if len(end) > 1 {
panic("Multiple end dates are not allowed, don't know what to do with those")
} else {
return Periodical{
StartsAt: s,
EndsAt: time.Now(),
Interval: interval.Months(1),
isIndefinite: true}
}
}
Although it does the trick, it looks ugly, isn't it? My concise one-liner is now scattered along several lines of code.
So, that's why I wonder, what's the go's idiomatic way of achieving what I'm trying to do?
time.Time is a struct. Its zero value–although being a valid time value–is like never used. You can utilize the zero value time to signal the missing or undefined time value. There is even a Time.IsZero() method which tells you if a time.Time value is the zero value.
Note that the zero value time is not Jan 1, 1970 like in some other languages, but Jan 1, year 1, as you can see on the Go Playground. This is also documented at time.Time:
The zero value of type Time is January 1, year 1, 00:00:00.000000000 UTC. As this time is unlikely to come up in practice, the IsZero method gives a simple way of detecting a time that has not been initialized explicitly.
Also, when creating a Periodical value, use keyed composite literal, and you can omit fields which you don't want to set, thus leaving them at their zero value:
eachYear := Periodical{
Interval: interval.Years(1),
StartsAt: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
}
Note that you can't use time.Parse() in this composite literal as that returns 2 values (a time.Time and an error). Either use time.Date() as in the above example, or create the time value prior (handle error), and just use the time value.
To tell if EndsAt is specified:
if eachYear.EndsAt.IsZero() {
fmt.Println("EndsAt is missing")
}
Should you need to zero an already set (non-zero) time value, you may use the time.Time{} composite literal:
eachYear.StartsAt = time.Time{}
Also note though that when marshaling a time.Time value, even if it's the zero value (since it is a valid time value), it will be sent even if you use the omitempty option. In those cases you must use a *time.Time pointer or write custom marshalers. For details, see Golang JSON omitempty With time.Time Field.

How to convert time.Time variable to atomic in Go?

In my RESTFUL web service which is an online game, I'm storing starting time of every question in an global variable like this: var MyTime time.Time which I should update it after every level of the game. My application is distributed, so I want to make sure all of my apps are not updating it at the same time. That's why I've decided to make it atomic.
Actually I'm familiar with Golang sync/atomic package.
I tried to use atomic.LoadPointer() method but it needs specific argument type which isn't safe. Do you any other way for this?
Update:
Okay I solved my problem like this.
I defined time variable as atomic.Value and used atomic Load and Store methods. This is the code:
var myTime atomic.Value
myTime.Store(newTime) and load myTime.Load().(time.Time).
Consider that Load() method returns interface, so you should write (time.Time) at the end in order to convert it to time.Time type.
This can't be done, as such, because time.Time is a compound type:
type Time struct {
// wall and ext encode the wall time seconds, wall time nanoseconds,
// and optional monotonic clock reading in nanoseconds.
//
// From high to low bit position, wall encodes a 1-bit flag (hasMonotonic),
// a 33-bit seconds field, and a 30-bit wall time nanoseconds field.
// The nanoseconds field is in the range [0, 999999999].
// If the hasMonotonic bit is 0, then the 33-bit field must be zero
// and the full signed 64-bit wall seconds since Jan 1 year 1 is stored in ext.
// If the hasMonotonic bit is 1, then the 33-bit field holds a 33-bit
// unsigned wall seconds since Jan 1 year 1885, and ext holds a
// signed 64-bit monotonic clock reading, nanoseconds since process start.
wall uint64
ext int64
// loc specifies the Location that should be used to
// determine the minute, hour, month, day, and year
// that correspond to this Time.
// The nil location means UTC.
// All UTC times are represented with loc==nil, never loc==&utcLoc.
loc *Location
}
However, you can do this with pointers, so *time.Time would be possible, if this suits your needs. But of course, this is discouraged, by virtue of the fact that atomic.LoadPointer and atomic.StorePointer use the unsafe package to accomplish their magic.
A much better approach, if it will work for you, is just to use a mutex to protect your value. There are many ways to do this, but one minimal example:
type MyTime struct {
t time.Time
mu sync.RWMutex
}
func (t *MyTime) Time() time.Time {
t.mu.RLock()
defer t.mu.RUnlock()
return t.t
}
func (t *MyTime) SetTime(tm time.Time) {
t.mu.Lock()
defer t.mu.Unlock()
t.t = tm
}
You can keep unix time https://golang.org/pkg/time/#example_Time_Unix as atomic which is int64. Then convert to go time after you've read atomic value.
If you are only interested in the timestamp, you could simply keep a reference to the unix time, which is int64 and atomically update that.
var ts int64
func main() {
atomic.StoreInt64(&ts, time.Now().Unix())
t := time.Unix(atomic.LoadInt64(&ts), 0)
fmt.Println(t)
}
Instead, if you need the entire Time struct, read on.
Go 1.19 (still in beta)
If you are okay with storing a pointer to a time.Time object, you can use atomic.Pointer, which is a generic struct that abstracts atomic.LoadPointer and atomic.StorePointer. As a simple example:
// instantiate generic struct with time.Time
var at = atomic.Pointer[time.Time]{}
func main() {
t := time.Now()
at.Store(&t)
t = *at.Load()
fmt.Println(t)
}
Playground: https://go.dev/play/p/KwTMgvJIenx?v=gotip
Note that in Go 1.19 you can also use atomic.Int64. The advantage of these atomic types instead of top-level functions is that it's more fool-proof; it's impossible to access the value non-atomically as it's hidden behind the struct.

Parse time zone into a Location struct in Go

Given a time zone such as EDT or CEST is there a way to get a time.Location reference to use it to with func (t Time) In(loc *Location) Time?
It is possible to initialize the location for e.g. CEST with time.LoadLocation("Europe/Berlin") but how to do the same for the actual time zone notation?
Given the very insightful comment by #Svip is there any sensible way to return a list of representative location? That is for WET return e.g. [Europe/London, Atlantik/Reykjavík]. All other WET locations would follow the same time zone arrangements as one of those two.
There is option to parse time for given Location.
(https://golang.org/pkg/time/#LoadLocation)
type CustomTime struct {
time.Time
}
const ctLayout = "Jan 2, 2006 at 3:04pm (MST)"
func (ct *CustomTime) UnmarshalJSON(b []byte) (err error) {
s := strings.Trim(string(b), "\"")
if s == "null" {
ct.Time = time.Time{}
return
}
location, err := time.LoadLocation("Local")
if err != nil {
return err
}
ct.Time, err = time.ParseInLocation(ctLayout, s, location)
return err
}
There exists a package github.com/tkuchiki/go-timezone that provides mapping between zone abbreviations and locations. See its timezones.go.
However, as commenters also pointed out, abbreviated timezone names are ambiguous and it is better to avoid user input with such names at all. As mentioned in other questions (Why doesn't Go's time.Parse() parse the timezone identifier? and How to properly parse timezone codes), when parsing time, Go correctly parses abbreviated timezone when it matches local timezone of the machine running code and UTC timezone. All others are not parsed correctly in my experience.

Golang time error: month out of range

Here is my code:
time.Parse(time.Now().String()[0:19],time.Now().String()[0:19])
error:
parsing time "2016-09-20 16:50:08": month out of range
How to parse time string?
First param is layout, see:
func Parse(layout, value string) (Time, error) {
return parse(layout, value, UTC, Local)
}
Docs:
// Parse parses a formatted string and returns the time value it represents.
// The layout defines the format by showing how the reference time,
// defined to be
// Mon Jan 2 15:04:05 -0700 MST 2006
// would be interpreted if it were the value; it serves as an example of
// the input format. The same interpretation will then be made to the
// input string.
//
// Predefined layouts ANSIC, UnixDate, RFC3339 and others describe standard
// and convenient representations of the reference time. For more information
// about the formats and the definition of the reference time, see the
// documentation for ANSIC and the other constants defined by this package.
// Also, the executable example for time.Format demonstrates the working
// of the layout string in detail and is a good reference.
//
// Elements omitted from the value are assumed to be zero or, when
// zero is impossible, one, so parsing "3:04pm" returns the time
// corresponding to Jan 1, year 0, 15:04:00 UTC (note that because the year is
// 0, this time is before the zero Time).
// Years must be in the range 0000..9999. The day of the week is checked
// for syntax but it is otherwise ignored.
//
// In the absence of a time zone indicator, Parse returns a time in UTC.
//
// When parsing a time with a zone offset like -0700, if the offset corresponds
// to a time zone used by the current location (Local), then Parse uses that
// location and zone in the returned time. Otherwise it records the time as
// being in a fabricated location with time fixed at the given zone offset.
//
// No checking is done that the day of the month is within the month's
// valid dates; any one- or two-digit value is accepted. For example
// February 31 and even February 99 are valid dates, specifying dates
// in March and May. This behavior is consistent with time.Date.
//
// When parsing a time with a zone abbreviation like MST, if the zone abbreviation
// has a defined offset in the current location, then that offset is used.
// The zone abbreviation "UTC" is recognized as UTC regardless of location.
// If the zone abbreviation is unknown, Parse records the time as being
// in a fabricated location with the given zone abbreviation and a zero offset.
// This choice means that such a time can be parsed and reformatted with the
// same layout losslessly, but the exact instant used in the representation will
// differ by the actual zone offset. To avoid such problems, prefer time layouts
// that use a numeric zone offset, or use ParseInLocation.
You may use
t, err := time.Parse("2006-01-02 15:04:05", time.Now().String()[:19])
Try on The Go Playground:
package main
import (
"fmt"
"time"
)
func main() {
t, err := time.Parse("2006-01-02 15:04:05", time.Now().String()[:19])
if err != nil {
panic(err)
}
fmt.Println(t)
}
output:
2009-11-10 23:00:00 +0000 UTC
I had the same problem, so I came here to say golang will some times mean "month" they meant "DAY OF THE MONTH", the error message is wrong, here is an example:
package main
import (
"fmt"
"time"
)
func main() {
dateAsString:= "31/Oct/2019"
layout := "01/Jan/2006" // BAD BAD BAD SHOULD BE 02 INSTEAD OF 01
fmt.Println("INPUT:" + dateAsString)
t, err := time.Parse(layout, dateAsString)
if err != nil {
fmt.Println("DATE UNPARSEABLE:3", err)
}
fmt.Println(t)
}

Resources