Hello Go friends, 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 <https://cs.opensource.google/go/go/+/refs/tags/go1.17.2:src/time/time.go;l=208;drc=refs%2Ftags%2Fgo1.17.2>. I don't fully understand the reason for this; the UTC() call seems to have been bucketed <https://github.com/golang/go/issues/18991#issuecomment-306209288> 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 <https://cs.opensource.google/go/go/+/refs/tags/go1.17.2:src/time/time.go;l=878;drc=refs%2Ftags%2Fgo1.17.2> 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() <https://cs.opensource.google/go/go/+/refs/tags/go1.17.2:src/time/zoneinfo.go;l=92> 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. <https://cs.opensource.google/go/go/+/refs/tags/go1.17.2:src/time/time.go;l=146> Setting time.Local = time.UTC results in time.Now() producing timestamps with local == &utcLoc <https://cs.opensource.google/go/go/+/refs/tags/go1.17.2:src/time/time.go;l=1080>. 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 <http://19486> been <https://github.com/golang/go/issues/45960> 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 <https://github.com/golang/go/issues/19486#issuecomment-292968278> as a bug report in the past. More generally, I see that the whole idea of controlling the process-wide timezone was rejected <https://github.com/golang/go/issues/10701#issuecomment-148777945> (also here <https://github.com/golang/go/issues/19486#issuecomment-292554913>). 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? Thanks! - Andrei -- 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/CAPqkKgmY4mfoeav9%2B3g7RjdURv8nNZ6RPZb0Vivbzf1Lq%3D0Kqw%40mail.gmail.com.