How to convert time.Time variable to atomic in Go? - 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.

Related

How to use `omitempty` with protobuf Timestamp in Golang

I have an optional field on my struct called ExpireTime. It has a time.Time type and a json:"expire_time,omitempty" tag to not send it, when it is empty. This part works perfectly fine.
When I want to use the same field via GRPC, I run into an issue when converting it to the protobuf timestamp format.
type Timestamp struct {
// Represents seconds of UTC time since Unix epoch
// 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
// 9999-12-31T23:59:59Z inclusive.
Seconds int64 `protobuf:"varint,1,opt,name=seconds,proto3" json:"seconds,omitempty"`
// Non-negative fractions of a second at nanosecond resolution. Negative
// second values with fractions must still have non-negative nanos values
// that count forward in time. Must be from 0 to 999,999,999
// inclusive.
Nanos int32 `protobuf:"varint,2,opt,name=nanos,proto3" json:"nanos,omitempty"`
// contains filtered or unexported fields
}
ExpireTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=expire_time,json=expireTime,proto3" json:"expire_time,omitempty"`
The issue is that an empty time.Time{} object will be converted to a negative seconds value corresponding to 0001-01-01T00:00:00Z. Having the omitEmpty flag will not be applied in this case as the value is not zeroed out. What could I do to omit this field, when it is actually empty? Thanks!
As you say time.Time{} converts to 0001-01-01T00:00:00Z; this is working as intended. Note that you also need to be careful converting in the opposite direction (a zero TimeStamp will become 1970-01-01T00:00:00Z).
However generally the Timestamp will be part of a message, for example:
message MyMessage{
google.protobuf.Timestamp comm_time = 1;
}
Running this through protoc will result in something like:
type MyMessage struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
CommTime *timestamppb.Timestamp `protobuf:"bytes, 1,opt,name=comm_time,json=commTime,proto3" json:"comm_time,omitempty"`
}
This means you should be able to achieve the result you are looking for with CommTime=nil; e.g.
sourceTime := time.Time{} // Whatever time you want to encode
var commTime *timestamp.Timestamp
if !sourceTime.IsZero() {
commTime = timestamppb.New(sourceTime)
}
msg := MyMessage{
CommTime: commTime,
}

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)
}

How to read package documentation

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.

How to organize similar struct to avoid duplication without sacrificating memory

I have to refactor a code, and I don't know how to best define my structs.
I have a list of meters []Meter
With Meter
type Meter struct {
ID string `json:"meter_id"`
ConsoProd string `json:"conso_prod"`
OperationID string `json:"op_id"`
Measures []Measure `json :"measures"`
}
The 3 first field will always be here, but Measure has several variants, depending the step in the process.
Here is the general Measure struct:
// Measure is a measure from a Meter
type Measure struct {
IndexName string `json:"index_name"`
IndexValue int `json:"index_value"`
Timestamp time.Time `json:"timestamp"`
Delta float64 `json:"delta"`
Redistributed float64 `json:"redistributed,omitempty"` // We don t need the redistributed field in Raw Measure
}
First, we get the 'raw value'
type RawMeasure struct {
IndexName string `json:"index_name"`
IndexValue int `json:"index_value"`
Timestamp time.Time `json:"timestamp"`
Delta float64 `json:"delta"`
}
Then we will calculate the redistributed, and store it into the redistributed field
The measure will never have a redistributed.
Also, I have 2 data sources. If Data comes from source2, it will never have IndexName / IndexValue
type RawMeasureFromSource2 struct {
Timestamp time.Time `json:"timestamp"`
Delta float64 `json:"delta"`
}
Here we can see I could create several structs: (RawMeasure, RawMeasureFromSource2, Measure) and then I should create other 3 Meters vars.
As I will manage a lot of data, I need to be careful optimizing memory, but it seems it will complicate my code a lot.
Is there a way to get both a simple code, and optimized memory usage?
As per comments, 1 measure every 10 minutes is definitely not a lot.
I'd go for simplicity:
type Measure struct {
IndexName *string `json:"index_name,omitempty"`
IndexValue *int `json:"index_value,omitempty"`
Timestamp time.Time `json:"timestamp"`
Delta float64 `json:"delta"`
Redistributed *float64 `json:"redistributed,omitempty"`
}
Pros:
Same struct, easy to work with
Cons:
Few bytes lost on every struct instance

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.

Resources