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.

Reply via email to