0

Many examples on the Internet use rand.Seed(time.Now().UTC().UnixNano()) to initialise the pseudo random number generator seed.

I see that if I omit the UTC() call, it still works fine.

Unix (or UnixNano) time is anyway the number of seconds (or milliseconds) since the epoch, that is, 1970-01-01 00:00:00.000000000 UTC. Unix or UnixNano time is anyway timezone-agnostic.

As an example, take the following code:

package main

import (
        "fmt"
        "time"
)

func main() {
        t := time.Now()
        fmt.Println(t.UnixNano())
        fmt.Println(t.UTC().UnixNano())
}

So my question is: Is there any purpose to UTC() call or is it safe to omit the UTC() call and just call rand.Seed(time.Now().UnixNano()) instead?

Lone Learner
  • 18,088
  • 20
  • 102
  • 200

4 Answers4

5

It is safe to say you can omit the UTC() when using UnixNano()

First see the code of UTC() in time.go:1107:

// UTC returns t with the location set to UTC.
func (t Time) UTC() Time {
    t.setLoc(&utcLoc)
    return t
}

It only sets the Location of the current Time.

Now, according to the comment on the In() methode in the time.go file the Location info are only for "display purposes". See time.go:1119:

// In returns a copy of t representing the same time instant, but
// with the copy's location information set to loc for display
// purposes.
//
// In panics if loc is nil.
func (t Time) In(loc *Location) Time {
    if loc == nil {
        panic("time: missing Location in call to Time.In")
    }
    t.setLoc(loc)
    return t
}

The Location is only used if the Time has to be displayed:

// abs returns the time t as an absolute time, adjusted by the zone offset.
// It is called when computing a presentation property like Month or Hour.
func (t Time) abs() uint64 {
    l := t.loc
    // Avoid function calls when possible.
    if l == nil || l == &localLoc {
        l = l.get()
    }
    sec := t.unixSec()
    if l != &utcLoc {
        if l.cacheZone != nil && l.cacheStart <= sec && sec < l.cacheEnd {
            sec += int64(l.cacheZone.offset)
        } else {
            _, offset, _, _ := l.lookup(sec)
            sec += int64(offset)
        }
    }
    return uint64(sec + (unixToInternal + internalToAbsolute))
}

Run the following code to see the difference. Both are based on the same UnixNano, only the hour changes, since the location is only applied before printing:

var now = time.Now()
var utc = now.UTC()
fmt.Printf("now UnixNano: %d, Hour: %d, Minute: %d, Second: %d\n", now.UnixNano(), now.Hour(), now.Minute(), now.Second())
fmt.Printf("utc UnixNano: %d, Hour: %d, Minute: %d, Second: %d\n", utc.UnixNano(), utc.Hour(), utc.Minute(), utc.Second())

now UnixNano: 1595836999431598000, Hour: 10, Minute: 3, Second: 19
utc UnixNano: 1595836999431598000, Hour: 8, Minute: 3, Second: 19
tobi.g
  • 924
  • 6
  • 23
4

You set the pseudo-random number generator seed, to make generated numbers difficult to guess.

When you look at UTC() method documentation, you'll see that only thing it does is sets location (timezone). It is irrelevant which timezone is used for random seed generation.

What is important, is that UnixNano() is used, and that platform would actually return time with such precision. Otherwise, the random seed might be guessed, which may allow for: random number generator attack

Please consider a safer way to initialize random seed generator in answer: https://stackoverflow.com/a/54491783/5279383

Cezary Butler
  • 807
  • 7
  • 21
4

Time.UnixNano() returns the Unix time of the source time, the number of nanoseconds elapsed since January 1, 1970 UTC. It is always interpreted in UTC zone, it doesn't matter what location the source time has. The unix time is zone-independent. Its documentation clearly states this:

The result does not depend on the location associated with t.

So you do not need to call Time.UTC(), you will get the same result.

See this example:

t1, err := time.Parse("2006-01-02 15:04:05 -0700", "2020-07-27 13:50:00 +0200")
if err != nil {
    panic(err)
}
fmt.Printf("%v\n\t%v\n\t%v\n", t1, t1.UnixNano(), t1.UTC().UnixNano())

t2, err := time.Parse("2006-01-02 15:04:05 -0700", "2020-07-27 13:50:00 +0000")
if err != nil {
    panic(err)
}
fmt.Printf("%v\n\t%v\n\t%v\n", t2, t2.UnixNano(), t2.UTC().UnixNano())

We parse 2 input times, once in a non-UTC zone, and another in UTC zone. We print UnixNano() for both, with and without calling UTC(). The results are identical.

Output (try it on the Go Playground):

2020-07-27 13:50:00 +0200 +0200
    1595850600000000000
    1595850600000000000
2020-07-27 13:50:00 +0000 UTC
    1595857800000000000
    1595857800000000000
icza
  • 389,944
  • 63
  • 907
  • 827
-2
  • Is there any purpose to UTC() call - YES
  • is it safe to omit the UTC() - YES
Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
black blue
  • 798
  • 4
  • 13