How to read package documentation - go

I am trying to understand how to use/call libraries & functions of Golang by reading the official documentation but couldn't fully comprehend.
Below are some examples that I hope to get advise from the experts here in SO
Example 1: time
From the documentation:
type Time
func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time
func Now() Time
I interpret the above as type Time has method Now which does not take in any input and returns a Time type.
thus,
var t time.Time
fmt.Println(t)
yields 0001-01-01 00:00:00 +0000 UTC So. t is a valid time.Time type variable.
Question 1:
But why is t.Now() throwing an error t.Now undefined (type time.Time has no field or method Now)?
Question 2:
Interestingly, time.Now() returns the value desired. Does that mean Now() is not a method of type Time?

var t time.Time declares a variable of type time.Time with the zero value for the type.
func Now() Time: Now() is a function with no parameters which returns type time.Time
func (t Time) Month() Month: Month() is a method on the receiver t type time.Time with no parameters which returns type time.Month.
For example,
package main
import (
"fmt"
"time"
)
func main() {
var t time.Time
fmt.Println(t)
t = time.Now()
fmt.Println(t)
m := t.Month()
fmt.Println(m)
}
Playground: https://play.golang.org/p/Ume5kxDAe05
Output:
0001-01-01 00:00:00 +0000 UTC
2009-11-10 23:00:00 +0000 UTC m=+0.000000001
November
Note: In the playground the time begins at 2009-11-10 23:00:00 UTC. This makes it easier to cache programs by giving them deterministic output.
Take A Tour of Go.
See The Go Programming Language Specification

I think what you are really asking here is: why is the package documentation laid out the way it is? That is, for this specific case, in this specific package documentation, we see:
type Time
func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time
func Now() Time
where the type Time line appears by itself, then immediately underneath it, several func declarations appear that return a value of type Time.
The reason these are indented two characters is to indicate that these functions return a value of type Time. The reason that they appear under the type Time line is that these functions return a value of type Time.
In this case, both reasons add up to the same thing—they're redundant. That's OK though! It's just a function of the fact that the Go documentation generator is a program that obeys these sort and indent rules. Nothing here implies that the two functions are receiver functions.
Consider another example from the same package documentation. Somewhat earlier, we see:
type Duration
func ParseDuration(s string) (Duration, error)
func Since(t Time) Duration
func Until(t Time) Duration
Here, this tells us that all three of these functions return a Duration—though the first one returns both a Duration and an error. The ParseDuration function is an ordinary function. It is the functions Since and Until that are receiver functions.1 They take a receiver argument of type Time (and no other arguments) and return a value of type Duration.
In some other design, it might make sense to sort the Since and Until functions underneath the type name Time, since these are receiver functions of type Time. But the package documentation sorts (and groups) by return type, not receiver or argument type. That's all there really is to it here.
1You can—and the spec does—call these methods if you like.

Related

How do I create static dates in golang?

I have some golang code that deals with time.Time dates. I'd like to write some tests that check for specific dates. How do I do that?
It appears as though the contents of time.Time struct are all private so I can't initialize one directly. And the only way to create one that I see is to time.Parse it. But since time.Parse returns two values, (one of which is error), I can't time.Parse in a global var initialization. I'm stuck with creating my containing structs globally, (which is so-so), but then filling in the time.Time elements later, in init. This trips several linters who suggest that init is a bad idea, that creating a struct without setting all fields it a bad idea, etc.
Is there a better way to create a constant time.Time? Am I missing something obvious?
You can just declare the variable, then use methods to change it:
package main
import (
"fmt"
"time"
)
func main() {
var t time.Time
fmt.Println(t.IsZero()) // true
fmt.Println(t) // 0001-01-01 00:00:00 +0000 UTC
t = t.AddDate(1969, 0, 0)
fmt.Println(t) // 1970-01-01 00:00:00 +0000 UTC
}
https://godocs.io/time#Time.AddDate

How to get millisecond value of a timestamp

Go has methods to extract almost every component of a timestamp, eg time.Second(), time.Nano(), but none to extract the millisecond portion of a timestamp.
How does one extract the millisecond value of a timestamp.
eg, in the case of a timestamp like:
2021-01-07 10:33:06.511
i want to extract 511
To access the fraction seconds, you may use time.Nanosecond(). And if we convert it to time.Duration (time.Duration is exactly the nanoseconds count), we can take advantage of its Duration.Milliseconds() method (which of course does no magic but code will be clearer and easier to read):
func extractMs(t time.Time) int64 {
return time.Duration(t.Nanosecond()).Milliseconds()
}
Try it on the Go Playground.
there is an answer in the comments, but i want to post here to be cannonical:
func extractMillisecond(t time.Time) int {
ms := time.Duration(t.Nanosecond()) / time.Millisecond
return int(ms)
}

Go does not support operator overloading, but how do I explain the time package

I have been doing the Go tour for a while now and I just noticed this line of code:-
today := time.Now().Weekday()
switch time.Saturday {
case today + 0:
fmt.Println("Today.")
case today + 1:
fmt.Println("Tomorrow.")
case today + 2:
fmt.Println("In two days.")
default:
fmt.Println("Too far away.")
}
If Go does not support operator overloading how is that I can use the '+' operator to increment the day?
time.Now().Weekday() returns a value of type time.Weekday, which is an int. today+1 works, because 1 is an untyped constant, and its type is determined based on context. In this context, both today and 1 are of type time.Weekday.
Also note that because of the way it is defined, time.Saturday is 6,
so time.Saturday == today+2 works, but time.Saturday+2 is not going to match any valid weekday.
I'm not sure about your problem, but I think you can pay attention to the time package. It's powerful.
There are many time types in go, such as time.weekday, time.duration, and time.month. They support the + operator, because their underlying type is numeric, but time.Time is not supported. Its underlying type is structure. We can use the method of time.Time for operation, such as Time.add, Time.sub, Time.adddate, Time.after, Time.before, Time.equa You can use Time.unix or Time.unixnano to get the Time stamp of numerical type for operation.
As an example, I implemented two methods to calculate the time from a certain time to the next week and the deviation from the current time.
import (
"time"
)
func NextWeekdayTime(now time.Time, weekday time.Weekday) time.Time {
return now.AddDate(0, 0, NextWeekdayNum(now, weekday))
}
func NextWeekdayNum(now time.Time, weekday time.Weekday) int {
diff := (weekday - now.Weekday()) + 7
if diff >= 7 {
diff -= 7
}
return int(diff)
}

Idiomatic way to represent optional time.Time in a struct

I've read both Optional Parameters? and Golang pass nil as optional argument to a function?
And still wonder if my case is more specific.
What I've got is:
type Periodical struct {
Interval *interval.Interval
StartsAt time.Time
EndsAt time.Time
}
to represent periodical event which has a start date and may or may not have an end date (periodical event runs for indefinite amount of time).
eachYear := Periodical{
interval.Years(1),
StartsAt: time.Parse("2 Jan 2006", "1 Jan 1970")}
Will throw
periodical/periodical.go:31:39: cannot use nil as type time.Time in field value
Which is understood, - I didn't specify EndsAt time.Time.
But what do I really do there then?
Am I forced to have a special flag to neglect EndsAt like so?
type Periodical struct {
Interval *interval.Interval
StartsAt time.Time
EndsAt time.Time
isIndefinite bool // This looks ugly already
}
and then if I want Yearly / Anually I do something like
eachYear := Periodical{
interval.Years(1),
time.Parse("2 Jan 2006", "1 Jan 1970"),
time.Parse("2 Jan 2006", "1 Jan 1970"),
isIndefinite: true}
Although, I can then account for this flag in business logic, but this EndsAt set to the same (or any other) date looks kind of dull.
I also define a method on periodical package which allows to have a shorthand periodical event like so:
func Monthly(s, e time.Time) Periodical {
return Periodical{StartsAt: s, EndsAt: e, Interval: interval.Months(1)}
}
What do I do to omit end (the second param)? Am I forced to either have separate method for that or do something that looks a bit funky and lacks readability:
func Monthly(s time.Time, end ...time.Time) Periodical {
if len(end) == 1 {
return Periodical{
StartsAt: s,
EndsAt: end[0],
Interval: interval.Months(1),
isIndefinite: false}
} else if len(end) > 1 {
panic("Multiple end dates are not allowed, don't know what to do with those")
} else {
return Periodical{
StartsAt: s,
EndsAt: time.Now(),
Interval: interval.Months(1),
isIndefinite: true}
}
}
Although it does the trick, it looks ugly, isn't it? My concise one-liner is now scattered along several lines of code.
So, that's why I wonder, what's the go's idiomatic way of achieving what I'm trying to do?
time.Time is a struct. Its zero value–although being a valid time value–is like never used. You can utilize the zero value time to signal the missing or undefined time value. There is even a Time.IsZero() method which tells you if a time.Time value is the zero value.
Note that the zero value time is not Jan 1, 1970 like in some other languages, but Jan 1, year 1, as you can see on the Go Playground. This is also documented at time.Time:
The zero value of type Time is January 1, year 1, 00:00:00.000000000 UTC. As this time is unlikely to come up in practice, the IsZero method gives a simple way of detecting a time that has not been initialized explicitly.
Also, when creating a Periodical value, use keyed composite literal, and you can omit fields which you don't want to set, thus leaving them at their zero value:
eachYear := Periodical{
Interval: interval.Years(1),
StartsAt: time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC),
}
Note that you can't use time.Parse() in this composite literal as that returns 2 values (a time.Time and an error). Either use time.Date() as in the above example, or create the time value prior (handle error), and just use the time value.
To tell if EndsAt is specified:
if eachYear.EndsAt.IsZero() {
fmt.Println("EndsAt is missing")
}
Should you need to zero an already set (non-zero) time value, you may use the time.Time{} composite literal:
eachYear.StartsAt = time.Time{}
Also note though that when marshaling a time.Time value, even if it's the zero value (since it is a valid time value), it will be sent even if you use the omitempty option. In those cases you must use a *time.Time pointer or write custom marshalers. For details, see Golang JSON omitempty With time.Time Field.

How to convert time.Time variable to atomic in Go?

In my RESTFUL web service which is an online game, I'm storing starting time of every question in an global variable like this: var MyTime time.Time which I should update it after every level of the game. My application is distributed, so I want to make sure all of my apps are not updating it at the same time. That's why I've decided to make it atomic.
Actually I'm familiar with Golang sync/atomic package.
I tried to use atomic.LoadPointer() method but it needs specific argument type which isn't safe. Do you any other way for this?
Update:
Okay I solved my problem like this.
I defined time variable as atomic.Value and used atomic Load and Store methods. This is the code:
var myTime atomic.Value
myTime.Store(newTime) and load myTime.Load().(time.Time).
Consider that Load() method returns interface, so you should write (time.Time) at the end in order to convert it to time.Time type.
This can't be done, as such, because time.Time is a compound type:
type Time struct {
// wall and ext encode the wall time seconds, wall time nanoseconds,
// and optional monotonic clock reading in nanoseconds.
//
// From high to low bit position, wall encodes a 1-bit flag (hasMonotonic),
// a 33-bit seconds field, and a 30-bit wall time nanoseconds field.
// The nanoseconds field is in the range [0, 999999999].
// If the hasMonotonic bit is 0, then the 33-bit field must be zero
// and the full signed 64-bit wall seconds since Jan 1 year 1 is stored in ext.
// If the hasMonotonic bit is 1, then the 33-bit field holds a 33-bit
// unsigned wall seconds since Jan 1 year 1885, and ext holds a
// signed 64-bit monotonic clock reading, nanoseconds since process start.
wall uint64
ext int64
// loc specifies the Location that should be used to
// determine the minute, hour, month, day, and year
// that correspond to this Time.
// The nil location means UTC.
// All UTC times are represented with loc==nil, never loc==&utcLoc.
loc *Location
}
However, you can do this with pointers, so *time.Time would be possible, if this suits your needs. But of course, this is discouraged, by virtue of the fact that atomic.LoadPointer and atomic.StorePointer use the unsafe package to accomplish their magic.
A much better approach, if it will work for you, is just to use a mutex to protect your value. There are many ways to do this, but one minimal example:
type MyTime struct {
t time.Time
mu sync.RWMutex
}
func (t *MyTime) Time() time.Time {
t.mu.RLock()
defer t.mu.RUnlock()
return t.t
}
func (t *MyTime) SetTime(tm time.Time) {
t.mu.Lock()
defer t.mu.Unlock()
t.t = tm
}
You can keep unix time https://golang.org/pkg/time/#example_Time_Unix as atomic which is int64. Then convert to go time after you've read atomic value.
If you are only interested in the timestamp, you could simply keep a reference to the unix time, which is int64 and atomically update that.
var ts int64
func main() {
atomic.StoreInt64(&ts, time.Now().Unix())
t := time.Unix(atomic.LoadInt64(&ts), 0)
fmt.Println(t)
}
Instead, if you need the entire Time struct, read on.
Go 1.19 (still in beta)
If you are okay with storing a pointer to a time.Time object, you can use atomic.Pointer, which is a generic struct that abstracts atomic.LoadPointer and atomic.StorePointer. As a simple example:
// instantiate generic struct with time.Time
var at = atomic.Pointer[time.Time]{}
func main() {
t := time.Now()
at.Store(&t)
t = *at.Load()
fmt.Println(t)
}
Playground: https://go.dev/play/p/KwTMgvJIenx?v=gotip
Note that in Go 1.19 you can also use atomic.Int64. The advantage of these atomic types instead of top-level functions is that it's more fool-proof; it's impossible to access the value non-atomically as it's hidden behind the struct.

Resources