Andrei,

For database times enforce the use of these convenience functions:

func DBNow() time.Time {
    return DBTime(time.Now())
}

func DBTime(t time.Time) time.Time {
    return t.UTC()
}

Peter


On Tuesday, October 26, 2021 at 2:58:16 PM UTC-4 Andrei Matei wrote:

> 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/6a8fb393-4652-43de-ac00-343b3af6f57cn%40googlegroups.com.

Reply via email to