On Tue, Oct 26, 2021 at 11:58 AM 'Andrei Matei' via golang-nuts <golang-nuts@googlegroups.com> wrote: > > I'm finding it difficult to satisfactorily control the timezone used by the > time.Now() function. The Go team have taken positions in the past that seem > to not make this easy; I'd like to see if I can gather sympathy for changes, > or perhaps if there's some obvious idea that I'm missing. > > In CockroachDB, we like all our timestamps to have the UTC timezone. This is > important for printing them out similarly across a cluster of > geographically-distributed processes, and also because time.Now() timestamps > sometimes end up exposed as SQL values, where the time zone matters in > various ways. > > If you accept that you want all the time.Now() calls in the process to return > UTC timestamps, the question is how to achieve that. We want a solution that > is confined to our program; we don't want to rely on the environment having > set the TZ envvar. > > For the longest time, we used a utility function like: > > func Now() time.Time { > return time.Now().UTC() > } > > and we had a linter disallowing direct use of time.Now(). The problem with > this is that the UTC() call strips out the monotonic clock part. I don't > fully understand the reason for this; the UTC() call seems to have been > bucketed in with other calls like Truncate() which want to strip the mono > part. So, we lose the benefits of the monotonic clock, and also the > time.Since() calls become twice as expensive because an optimization is > disabled when there's no mono. > Would it be feasible at all to get a flavor of UTC() that does not do the > stripping? > > If not, let's see what we can do to avoid the UTC() call. One possibility is > os.Setenv("TZ", ""). The problem with that, I think, is that it needs to be > done before initLocal() runs, which is not easy to control. > > Another way to do it is by setting the time.Local global. I thought that the > reason why this global variable exists is precisely for setting it in func > init()'s, but then I got a bit disillusioned. > First I tried > > func init() { > time.Local = time.UTC > } > > A problem with doing this is that you end up violating what appears to be a > documented invariant of time.Time: > // All UTC times are represented with loc==nil, never loc==&utcLoc. > > Setting time.Local = time.UTC results in time.Now() producing timestamps with > local == &utcLoc. I don't know if violating that invariant actually has any > consequences for the time library, but at the very least it breaks the > round-tripping of UTC values through marshall/unmarshall (e.g. json), in the > sense that reflect.DeepEqual() no longer considers the un-marhsalled value > equal to the original. The difficulties with round-tripping time.Time have > been reported several times and were not accepted; still, time.Now().UTC() > used to round-trip fine - which was making our tests happy. > > One weird trick that works is setting time.Local = nil. That does what I > want, but seems to be straying even further from intended uses of the API. > > Reading around, I see that the invariant violation above was rejected as a > bug report in the past. More generally, I see that the whole idea of > controlling the process-wide timezone was rejected (also here). And yet > time.Local is exported (and mutable), so I do wonder why that is. > > So, would anyone have another suggestion about how to enforce process-wide > UTC timezones while preserving the monotonic clock readings? > Or, any chance for re-litigating introducing a SetLocal() function?
As you know, the timezone only affects the presentation of the time.Time value, which is when a time.Time turns into something else, such as a string. So I would approach this problem either by 1) consistently running the program with the TZ environment variable set, or 2) consistently controlling the places where a time.Time turns into something else, which is usually a relatively small number of places. I don't know that that helps much, but that is my take on the issue. Ian -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CAOyqgcWDnW9%2BUXW93oTbeQ4JJtfjP6dR9cuM9U%3Dz86DncG%2BZ5Q%40mail.gmail.com.