go time.Now() not giving proper time - go

I am running this simple go program and expecting to get different time (a difference of 3-second period) for starting and shutdown logs as I have given sleep of 3 seconds, but I am getting same time.
package main
import (
"fmt"
"time"
)
const (
logInfo = "INFO"
logWarning = "WARNING"
logError = "ERROR"
)
type logEntry struct {
time time.Time
severity string
message string
}
var myLogChannel = make(chan logEntry, 50)
func main() {
go logger()
myLogChannel <- logEntry{ time.Now(), logInfo, "App is starting" }
time.Sleep(3 * time.Second)
fmt.Println("Time is :", time.Now().Format("2006-01-02 15:04:06"))
myLogChannel <- logEntry{ time.Now(), logInfo, "App is shuting down" }
time.Sleep(2 * time.Second)
}
func logger() {
for entry := range myLogChannel {
fmt.Printf("%v : [%v]: %v\n", entry.time.Format("2006-01-02 15:04:06"),entry.severity, entry.message)
}
}
Output:
On my system (running under cygwin env):
2020-10-12 11:03:20 : [INFO]: App is starting
Time is : 2020-10-12 11:03:20
2020-10-12 11:03:20 : [INFO]: App is shuting down
On playground:
2009-11-10 23:00:09 : [INFO]: App is starting
Time is : 2009-11-10 23:00:09
2009-11-10 23:00:09 : [INFO]: App is shuting down

Your time format is wrong. 06 is the last two digits of the year, so it is printing 20 for 2020. Use the format: 2006-01-02 15:04:05

Related

How to get monotonic part of time.Time in Go

am currently working with timestamps and I would like to store in a variable the monotonic clock reading section.
Let's say that I have this: 2022-03-31 10:20:26.370463 +0200 CEST m=+0.007725255 then I would like to get m=+0.007725255 in a different var, or at least the +0.007725255 section. What is the idiomatic way in Go to get it?
A Go time.Time stores 2 timestamps:
Wall clock
Monotonic duration since process start (optional, via time.Now)
m=+0.007725255 represents the monotonic duration since the start of the process (when present in a time.Time).
Go calculates this offset by recording time.startNano timestamp during initialisation (not public). time.Now uses startNano to calculate the monotonic duration stored in time.Time. There is no simple public API to directly retrieve this value since it should never be needed.
In practice, you should simply subtract 2 timestamps generated via time.Now in your current process and the result will be the monotonic duration. If you need to know the duration since process startup you should record a startup timestamp during initalisation.
Example:
package main
import (
"errors"
"fmt"
"math"
"strconv"
"strings"
"time"
)
func main() {
t0 := time.Now()
fmt.Println("...example event...")
time.Sleep(time.Millisecond)
t1 := time.Now()
fmt.Println("Event start:", t0)
fmt.Println("Event completed:", t1)
fmt.Println("=== Not recommended ===")
offsetT0, _ := monoOffset(t0)
fmt.Println("Parsed start offset:", offsetT0)
startNano, _ := calculateStartNano()
fmt.Println("Calculate start offset via startNano: ", t0.Sub(startNano))
fmt.Println("=== Recommended ===")
fmt.Println("Example event duration:", t1.Sub(t0))
fmt.Println("Time since startup", time.Since(t0))
}
// You should never need anything below here (code smell).
func monoOffset(t time.Time) (time.Duration, error) {
// Recommend strings.Cut on Go1.18+.
parts := strings.Split(t.String(), " m=")
if len(parts) != 2 {
return 0, errors.New("missing monotonic offset")
}
seconds, err := strconv.ParseFloat(parts[1], 64)
if err != nil {
return 0, err
}
nanos := math.Round(seconds * 1e9)
return time.Duration(nanos), nil
}
func calculateStartNano() (time.Time, error) {
now := time.Now()
offset, err := monoOffset(now)
if err != nil {
return time.Time{}, err
}
return now.Add(-offset), nil
}
Outputs:
...example event...
Event start: 2022-04-16 16:54:25.088159496 +1000 AEST m=+0.000079273
Event completed: 2022-04-16 16:54:25.089438935 +1000 AEST m=+0.001358685
=== Not recommended ===
Parsed start offset : 79.273µs
Calculate start offset via startNano: 79.273µs
=== Recommended ===
Example event duration: 1.279412ms
Time since startup 2.016789ms
The monotonic clock is just used for differences between times. The absolute value of the monotonic clock is undefined and you should not try to get it. I think what you really want for your timestamp is the duration from a base time.
func init() {
baseTime = time.Now()
}
// NowTimestamp returns really just the duration from the base time
func NowTimestamp() time.Duration {
return time.Now().Sub(baseTime)
}

How to start a cron job at regular intervals starting from a time in the past

I would like to build a program which runs a number of cron jobs which start at a time which, in general, is in the past. Here is a simplified example using gocron:
package main
import (
"time"
"github.com/jasonlvhit/gocron"
"github.com/sirupsen/logrus"
)
// This slice would be obtained from persistent storage
var startTimes = []time.Time{
time.Now().Add(-4 * time.Second),
time.Now().Add(-3 * time.Second),
}
func format(t time.Time) string {
return t.Format("15:04:05")
}
func notify(startTime time.Time) {
logrus.WithField("time", format(time.Now())).Infof("I started at %s\n", format(startTime))
}
func main() {
for _, startTime := range startTimes {
gocron.Every(10).Seconds().From(&startTime).Do(notify, startTime)
}
logrus.Infof("Starting at %s...\n", format(time.Now()))
<-gocron.Start()
}
If I run this, I get the following output:
INFO[0000] Starting at 00:30:54...
INFO[0010] I started at 00:30:50 fields.time="00:31:04"
INFO[0010] I started at 00:30:51 fields.time="00:31:04"
What I observe is that all the events are happening at once, 10 seconds after I start the program.
However, since the startTimes are 4 and 3 seconds before the program starts, what I would like is for the events to occur 6 and 7 seconds after the program starts (and every 10 seconds after that), respectively.
Is there a way to do this with gocron or some other tool?
This seems like just simple math:
interval := 10 * time.Second
nextTime := time.Now().Add((time.Since(startTime) + interval) % interval)
gocron.Every(10).Seconds().From(&nextTime).Do(notify, nextTime)
https://play.golang.org/p/pwEZqy_LUuk
We can try to leverage a simple ticker, it is not a complete solution but should be easy enough to adapt.
package main
import (
"fmt"
"time"
)
func ticker(period time.Duration, length ...time.Duration) <-chan time.Time {
ticker := time.NewTicker(period)
if len(length) > 0 {
done := make(chan bool)
go func() {
time.Sleep(period + length[0])
done <- true
}()
go func() {
<-done
ticker.Stop()
}()
}
return ticker.C
}
func main() {
t1 := ticker(6*time.Second, 4*time.Second)
t2 := ticker(7*time.Second, 3*time.Second)
t3 := ticker(10 * time.Second)
for {
select {
case t1 := <-t1:
fmt.Println("t1: ", t1)
case t2 := <-t2:
fmt.Println("t2: ", t2)
case t3 := <-t3:
fmt.Println("t3: ", t3)
}
}
}

Ticker should not execute if already running

I have some function that has to run periodically. I have used a ticker for this. But if the ticker is already running, and the time interval passes again, it should not execute again.
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(3*time.Second)
flag := 0
defer ticker.Stop()
for {
select {
case t := <-ticker.C:
flag = flag + 1
if (flag % 2 ==0 ) {
time.Sleep(time.Second*4)
}
fmt.Println("Current time: ", t)
}
}
}
https://play.golang.org/p/2xV2MYInn4I
In the playground, the ticker prints every 3 seconds, but every even turn of the ticker the job takes more time than the interval. I expect it to not run then and drop those ticks.
How do I do this?
sleeping inside the same goroutine merely delays execution. ticker meanwhile runs in a separate goroutine. So even if you used a global variable to maintain an execution state - it will not give you your desired result with sleep. However migrating the whole "sleeping" in a separate goroutine yields:
package main
import (
"fmt"
"time"
)
type Tick struct {
ticker *time.Ticker
executing bool
}
func somethingYouWantToDo(tick *Tick, flag *int, t time.Time) {
if tick.executing {
return
}
tick.executing = true
*flag = *flag + 1
if (*flag % 2 ==0 ) {
time.Sleep(time.Second*4)
}
fmt.Println("Current time: ", t)
tick.executing = false
}
func main() {
tick := &Tick{
ticker: time.NewTicker(3*time.Second),
}
flag := 0
defer tick.ticker.Stop()
for {
select {
case t := <-tick.ticker.C:
go somethingYouWantToDo(tick, &flag, t)
}
}
}
// output
// Current time: 2009-11-10 23:00:03 +0000 UTC m=+3.000000001
// Current time: 2009-11-10 23:00:06 +0000 UTC m=+6.000000001
// Current time: 2009-11-10 23:00:12 +0000 UTC m=+12.000000001
// Current time: 2009-11-10 23:00:15 +0000 UTC m=+15.000000001
// Current time: 2009-11-10 23:00:21 +0000 UTC m=+21.000000001
// Current time: 2009-11-10 23:00:24 +0000 UTC m=+24.000000001
// Current time: 2009-11-10 23:00:30 +0000 UTC m=+30.000000001
// Current time: 2009-11-10 23:00:33 +0000 UTC m=+33.000000001
// Current time: 2009-11-10 23:00:39 +0000 UTC m=+39.000000001
// Current time: 2009-11-10 23:00:42 +0000 UTC m=+42.000000001
// Current time: 2009-11-10 23:00:48 +0000 UTC m=+48.000000001
// Current time: 2009-11-10 23:00:51 +0000 UTC m=+51.000000001
// Current time: 2009-11-10 23:00:57 +0000 UTC m=+57.000000001
// Current time: 2009-11-10 23:01:00 +0000 UTC m=+60.000000001
// Current time: 2009-11-10 23:01:06 +0000 UTC m=+66.000000001
// Current time: 2009-11-10 23:01:09 +0000 UTC m=+69.000000001
Try it on the playground
The ticker channel is buffered, which is why you may see multiple triggers right one after the other. You can prevent that by simply transfering the ticker's values to an unbuffered channel (note also that the time.Time value received from the ticker is not the current time but the time of the last tick):
package main
import (
"fmt"
"time"
)
func main() {
c := make(chan time.Time) // unbuffered
ticker := time.NewTicker(3 * time.Second)
defer ticker.Stop()
go func() {
for t := range ticker.C {
select {
case c <- t:
default:
}
}
}()
for flag := 0; flag < 8; flag++ {
<-c
if flag%2 == 0 {
time.Sleep(time.Second * 4)
}
fmt.Println("Current time: ", time.Now())
}
}
// Output:
// Current time: 2020-02-19 12:21:57.095433032 +0100 CET m=+3.000213350
// Current time: 2020-02-19 12:22:04.095585208 +0100 CET m=+10.000365520
// Current time: 2020-02-19 12:22:06.095363327 +0100 CET m=+12.000143680
// Current time: 2020-02-19 12:22:13.095605268 +0100 CET m=+19.000385598
// Current time: 2020-02-19 12:22:15.095371885 +0100 CET m=+21.000152174
// Current time: 2020-02-19 12:22:22.095537562 +0100 CET m=+28.000317857
// Current time: 2020-02-19 12:22:24.095431317 +0100 CET m=+30.000211625
// Current time: 2020-02-19 12:22:31.095524308 +0100 CET m=+37.000304595
Try it on the playground: https://play.golang.org/p/jDe5uJiRVe2

How do you convert a time offset to a location/timezone in Go

Given an arbitrary time offset, how does one go about creating a usable time.Location object that represents that time offset?
The following code parses a time using an offset, but fmt.Println(t.Location()) subsequently returns no information:
func main() {
offset := "+1100"
t, err := time.Parse("15:04 GMT-0700","15:06 GMT"+offset)
if err != nil {
fmt.Println("fail", err)
}
fmt.Println(t)
fmt.Println(t.UTC())
fmt.Println(t.Location())
}
Playground: https://play.golang.org/p/j_E28qJ8Vgy
Basically I have some time data with time offsets, but without location data, I want to create a time.Location object to ensure the GMT offset is recorded. And then be able to output the time relative to the end users actual location time offset.
Use:
loc := time.FixedZone("UTC+11", +11*60*60)
Then set to this location:
t = t.In(loc)
Try this:
package main
import (
"fmt"
"time"
)
func main() {
loc := time.FixedZone("UTC+11", +11*60*60)
t := time.Now()
fmt.Println(t)
fmt.Println(t.Location())
t = t.In(loc)
fmt.Println(t)
fmt.Println(t.Location())
fmt.Println(t.UTC())
fmt.Println(t.Location())
}
Output:
2009-11-10 23:00:00 +0000 UTC m=+0.000000001
UTC
2009-11-11 10:00:00 +1100 UTC+11
UTC+11
2009-11-10 23:00:00 +0000 UTC
UTC+11
if len(offset) == 5 {
hours, ok1 := strconv.ParseInt(offset[:3], 10, 0)
mins, ok2 := strconv.ParseInt(offset[3:5], 10, 0)
if ok1 == nil && ok2 == nil {
t = t.In(time.FixedZone("Fixed", int((hours*60+mins)*60)))
fmt.Println(t)
fmt.Println(t.Location())
}
}

Time conversion issue in go language

I'm trying to understand the issue with time conversion in Go language. Here is code example:
package main
import (
"fmt"
"time"
)
func unix2Str(ts int64) string {
const layout = "20060102"
t := time.Unix(ts, 0)
return t.Format(layout)
}
func unixTime(ts string) int64 {
const layout = "20060102"
t, err := time.Parse(layout, ts)
if err != nil {
fmt.Println(err)
return 0
}
return t.Unix()
}
func main() {
ts1 := "20110320"
ts2 := "20110321"
ut1 := unixTime(ts1)
ut2 := unixTime(ts2)
fmt.Println(ts1, ut1, unix2Str(ut1))
fmt.Println(ts2, ut2, unix2Str(ut2))
}
It prints the following output:
20110320 1300579200 20110319
20110321 1300665600 20110320
But since I do the conversion from string format to Unix and reverse I would expect the same results for the date in string format. But it is not the case. In fact, the printed unix time 1300579200 is converted in python to original date I started with, e.g.
>>> time.strftime("%Y%m%d", time.gmtime(1300579200))
'20110320'
Is it a bug in Go code or am I missing something?
It is because of the difference between your local time zone and UTC. Parse returned UTC time and Unix returned local time. For example,
package main
import (
"fmt"
"time"
)
func unix2Str(ts int64) string {
const layout = "20060102"
t := time.Unix(ts, 0)
fmt.Println(t)
return t.Format(layout)
}
func unixTime(ts string) int64 {
const layout = "20060102"
t, err := time.Parse(layout, ts)
if err != nil {
fmt.Println(err)
return 0
}
fmt.Println(t)
return t.Unix()
}
func main() {
ts1 := "20110320"
ts2 := "20110321"
ut1 := unixTime(ts1)
ut2 := unixTime(ts2)
fmt.Println(ts1, ut1, unix2Str(ut1))
fmt.Println(ts2, ut2, unix2Str(ut2))
}
Output:
2011-03-20 00:00:00 +0000 UTC
2011-03-21 00:00:00 +0000 UTC
2011-03-19 20:00:00 -0400 EDT
20110320 1300579200 20110319
2011-03-20 20:00:00 -0400 EDT
20110321 1300665600 20110320
func Parse
func Parse(layout, value string) (Time, error)
Parse parses a formatted string and returns the time value it
represents. The layout defines the format by showing how the reference
time, defined to be
Mon Jan 2 15:04:05 -0700 MST 2006
would be interpreted if it were the value; it serves as an example of
the input format. The same interpretation will then be made to the
input string.
In the absence of a time zone indicator, Parse returns a time in UTC.
func Unix
func Unix(sec int64, nsec int64) Time
Unix returns the local Time corresponding to the given Unix time, sec
seconds and nsec nanoseconds since January 1, 1970 UTC.

Resources