What is "m=+" in GO time string? [duplicate] - go

This question already has answers here:
Unexpected output from time.Time
(3 answers)
Closed 4 years ago.
I'm experimenting with Google OAuth2 and I encountered this in the expiry time of my refresh token. It comes from 2018-10-15 15:42:37.5989253 +1100 AEDT m=+3610.688917401
I know it's a time format but I couldn't find any information about m=+ anywhere. Is it used internally by Google? I tried to parse it with time.RFC3339 but as you can guess, it ignores the m=+. It says
parsing time "2018-10-15 15:42:37.5989253 +1100 AEDT
m=+3610.688917401" as "2006-01-02T15:04:05Z07:00": cannot parse "
15:42:37.5989253 +1100 AEDT m=+3610.688917401" as "T"
So what's this m=+ in the time string?

The m=±<value> is monotonic clock reading in second.
Explanation from time.Time.String documentation:
If the time has a monotonic clock reading, the returned string includes a final field "m=±", where value is the monotonic clock reading formatted as a decimal number of seconds.
Afaik, golang doesn't provide layout for parsing monotonic clock, so in my opinion it's safe to remove it.
dateFormat := "2006-01-02 15:04:05.999999999 -0700 MST"
dateString := "2018-10-15 15:42:37.5989253 +1100 AEDT m=+3610.688917401"
t, err := time.Parse(dateFormat, strings.Split(dateString, " m=")[0])
if err != nil {
fmt.Println(err)
os.Exit(0)
}
fmt.Println(t) // 2018-10-15 15:42:37.5989253 +1100 AEDT
Starting from go 1.9, calling .String() will generate date string output with monotonic clock in it. So I suggest try to use .Format() for normal usage instead of .String().
The monotonic clock info is only useful for debugging purposes.

Related

Parsing ticker with datetime and dropping time elapsed

I would like to get a datetime from a ticker.C formatted string (over the network) and parse it into a Time object. ticker.C would look like 2023-01-03 17:24:13.986722973 +0100 CET m=+1.002332450. It would probably have to drop the m=+1.002332450 elapsed time as I don't see a way of keeping that in a Time object.
Also, is there a way to get a format string out of a Time object? Something like mytime.GetFormat()
The Stringer format of Time is documented here, https://pkg.go.dev/time#go1.19.4#Time.String:
String returns the time formatted using the format string
"2006-01-02 15:04:05.999999999 -0700 MST"
If the time has a monotonic clock reading, the returned string includes a final field "m=±<value>", where value is the monotonic clock reading formatted as a decimal number of seconds.
The returned string is meant for debugging; for a stable serialized representation, use t.MarshalText, t.MarshalBinary, or t.Format with an explicit format string.
Which suggests you should not try to consume that value and instead depend on a properly marshalled (or formatted) string.
Not mentioned/suggested, time.MarshalJSON is an option:
MarshalJSON implements the json.Marshaler interface. The time is a quoted string in RFC 3339 format, with sub-second precision added if present.
The sender and receiver don't have to do any special work to encode the time.Time value in JSON and then decode it again:
type wireTick struct {
Tick time.Time `json:"tick"`
}
Here's a small example of encoding and decoding the ticker on the wire with that struct, https://go.dev/play/p/Fx73q8-kVFa, which produces output like:
Sent JSON-encoded tick on wire: {"tick":"2009-11-10T23:00:01Z"}
Received tick from wire: {2009-11-10 23:00:01 +0000 UTC}
Sent JSON-encoded tick on wire: {"tick":"2009-11-10T23:00:02Z"}
Received tick from wire: {2009-11-10 23:00:02 +0000 UTC}
...
Can you modify the value being sent on the wire, or ask someone else to modify it so that it's proper?
If not, this should work:
const stringerLayout = "2006-01-02 15:04:05.999999999 -0700 MST"
timeStr := "2009-11-10 23:00:10 +0000 UTC m=+10.000000001"
tickStr := timeStr[:strings.Index(timeStr, "m=")-1]
tick, _ := time.Parse(stringerLayout, tickStr)
fmt.Printf("Received from wire: \t %q\n", timeStr)
fmt.Printf("Chopped off monotonic: \t %q\n", tickStr)
fmt.Printf("Tick is: \t\t %v\n", tick)
Received from wire: "2009-11-10 23:00:10 +0000 UTC m=+10.000000001"
Chopped off monotonic: "2009-11-10 23:00:10 +0000 UTC"
Tick is: 2009-11-10 23:00:10 +0000 UTC

Subtracting time to get age

My aim is to calculate the age of the pod by doing the subtraction of "current_time - pod_creation_time" so that I will get the age, I am getting creation time from metadata but it's in the format "2021-07-13 16:34:22 +0530 IST", so when I trying to subtract it from time.Now(), I am getting parsing error like below:
invalid operation: "t2 : " + t2 (mismatched types string and time.Time)
Anyone could please help how to have creation time "2021-07-13 16:34:22 +0530 IST" from metadata in the proper format so that I can do "time.Now - (creation time)"
I tried some workaround like below:
creatTime, err := time.Parse("2006-01-02 15:04:05 -0700 MST",
pod.ObjectMeta.CreationTimestamp.String())
and then subtracted creationTime from Current Time. It works, but I think this is not the right way.
There's a type mismatch as time.Now() return the current time stored in the type time.Time whereas 2021-07-13 16:34:22 +0530 IST is a string. You can perform the required subtraction operation on mismatched types i.e., time.Time and string.
You have to parse the string by specifying the layout. I'd recommend reading the time package's doc.
I've explained every operation in the sample code below; I hope it helps. If you understand this, you can also then look at helper functions like time.Since that can help you write the same program in fewer lines.
package main
import (
"fmt"
"time"
)
func main() {
// K8s timestamp
t := "2021-07-13 16:34:22 +0530 IST"
// Format of K8s timestamp
format := "2006-01-02 15:04:05 -0700 MST" // Mon Jan 2 15:04:05 -0700 MST 2006
// Parse the timestamp so that it's stored in time.Time
cur, err := time.Parse(format, t)
if err != nil {
panic(err)
}
// Current time
now := time.Now()
// As both are of type time.Time, it's subtractable
dur := now.Sub(cur)
// Print duration
fmt.Println(dur)
// Print duration (in seconds)
fmt.Println(dur.Seconds())
}
Also, I'd like you to learn how to write questions on StackOverflow. The formatting of your question is pretty bad. When seeking good solutions; it is the OP's duty to post the question correctly first so that everybody could understand it and then expect answers.
Read: https://stackoverflow.com/help/how-to-ask

Parsing a string into a timestamp is cutting off the year part of the string [duplicate]

This question already has answers here:
Parsing RFC-3339 / ISO-8601 date-time string in Go
(8 answers)
Closed 2 years ago.
I have a string that has a timestamp in the format
"2021-02-04 23:45:00" but when I try and parse this with time.parse it seemingly cuts off the year part.
The code is
case "period_end":
fmt.Println(record[i])
ts, err := time.Parse("2021-02-04 23:45:00", record[i])
if err != nil {
log.Printf("Time conversion failed: %v", err)
return
}
reading.Interval = t
where record[i] at this point is a string with
2021-02-04 00:15:00
and reading.Interval is time.Time
The error returned in the Printf is
Time conversion failed: parsing time "2021-02-04 00:15:00" as "2021-02-04 23:45:00": cannot parse "-02-04 00:15:00" as "1"
which I can't find in any search I've done. What am I missing here?
Replace the first parameter in time.Parse:
from
"2021-02-04 23:45:00"
to
"2006-01-02 15:04:00"
Golang uses a specific date for formatting, no idea why https://golang.org/src/time/format.go
Go uses this default time for setting up the layout:
"2006-01-02T15:04:05.000Z"
More info for this layout:
The reference time used in the layouts is the specific time:
Mon Jan 2 15:04:05 MST 2006
which is Unix time 1136239445. Since MST is GMT-0700, the reference time can be thought of as
01/02 03:04:05PM '06 -0700
So to solve your problem:
package main
import (
"fmt"
"time"
)
func main() {
recordTime := "2021-02-04 23:45:00"
ts, err := time.Parse("2006-01-02 15:04:05", recordTime)
if err != nil {
fmt.Println("error: ", err)
return
}
fmt.Println(ts)
}
This code can be found here.

Go parse time to string and back

I am trying to do something in Go that is very simple in languages like Java
I want to parse current time to string and then parse it back to time.
This is the code I tried but as can be seen here it gives unexpected results.
I am facing two problems
time.Now().String() gives a wrong date
If I cast the time to string
and cast it back to time, it gives a totally different date.
What is the right (and easy) way to do this?
p := fmt.Println
startStr := time.Now().String() //2009-11-10 23:00:00 +0000 UTC m=+0.000000001
p(startStr)
startTime, _ := time.Parse(
"2009-11-10 23:00:00 +0000 UTC m=+0.000000001",
startStr)
p(startTime) //0001-01-01 00:00:00 +0000 UTC
time.Now().String() is meant for debugging only (see go doc).
You should instead use time.Format().
For example:
p := fmt.Println
now := time.Now().Format(time.RFC3339)
p(now)
parsed, _ := time.Parse(time.RFC3339, now)
p(parsed.Format(time.RFC3339))
produces:
2009-11-10T23:00:00Z
2009-11-10T23:00:00Z
Your other concern regarding time.Now().String() gives a wrong date is likely due to where you're running the code. e.g. if you're running in "The Go Playgounrd", then the time won't be accurate. You should run it on your own computer, and assuming your computer has the correct time, then you should get the right time printed.
Unlike some other languages, Go does not treat String() as a de facto marshaling method -- instead, it's meant just to print the value out for debugging purposes. You could parse back from that format into a Time if you used a proper format string; however, a proper format string must be for the exact time of Mon Jan 2 15:04:05 MST 2006, not any time; but the format that String() prints out isn't captured by a constant within the Time package so it's probably not worth doing.
Instead, however, what you're trying to do may be better captured by the MarshalText and UnmarshalText methods:
startStr, _ := time.Now().MarshalText()
fmt.Println(string(startStr)) // 2009-11-10T23:00:00Z
startTime := new(time.Time)
startTime.UnmarshalText(startStr)
fmt.Println(startTime) // 2009-11-10 23:00:00 +0000 UTC
The time in the playground is fixed, it is always the date and time of
the Go announcement.
https://github.com/golang/go/issues/10663
So to play with time correctly, you need to run it on your local.
About the parsing time to string or back, you have to pass the format of time string:
For example:
package main
import (
"fmt"
"time"
)
func main() {
current := time.Now()
fmt.Println("Init Time:", current.String())
timeCustomFormatStr := current.Format("2006-01-02 15:04:05 -0700")
fmt.Println("Custom format", timeCustomFormatStr)
parsedTime, err := time.Parse("2006-01-02 15:04:05 -0700",timeCustomFormatStr)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("parsedTime From Custom:", parsedTime)
timeFormatRFC3339 := current.Format(time.RFC3339)
fmt.Println("RFC3339 format", timeFormatRFC3339)
parsedTimeRFC3339, err := time.Parse(time.RFC3339,timeFormatRFC3339)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("parsedTime From Custom:", parsedTimeRFC3339)
}
Ref:
1 https://golang.org/pkg/time/#Time.Format

Convert timestamp to ISO format in golang

I'm trying to convert the timestamp 2018-12-17T15:03:49.000+0000 to ISO format in golang, but am getting an error cannot parse "+0000" as "Z07:00"
This is what I tried
ts, err := time.Parse(time.RFC3339, currentTime)
Any ideas?
Beware, a long answer ahead
(tl;dr) use:
ts, err := time.Parse("2006-01-02T15:04:05-0700", currentTime)
ts.Format(time.RFC3339)
I really like go documentation, and you should do :)
All from https://golang.org/pkg/time/#pkg-constants
RFC3339 = "2006-01-02T15:04:05Z07:00"
Some valid layouts are invalid time values for time.Parse, due to
formats such as _ for space padding and Z for zone information
Which means you can't parse +0000 with layout Z07:00.
Also:
The reference time used in the layouts is the specific time:
Mon Jan 2 15:04:05 MST 2006
which is Unix time 1136239445. Since MST is GMT-0700, the reference
time can be thought of as
01/02 03:04:05PM '06 -0700
You can either parse numeric time zone offsets format as follows:
-0700 ±hhmm
-07:00 ±hh:mm
-07 ±hh
Or replacing the sign in the format with a Z:
Z0700 Z or ±hhmm
Z07:00 Z or ±hh:mm
Z07 Z or ±hh
fraction:
From this go example https://play.golang.org/p/V9ubSN6gTdG
// If the fraction in the layout is 9s, trailing zeros are dropped.
do("9s for fraction", "15:04:05.99999999", "11:06:39.1234")
So you can parse it like:
ts, err := time.Parse("2006-01-02T15:04:05.999-0700", currentTime)
Also, From the doc
A decimal point followed by one or more zeros represents a fractional
second, printed to the given number of decimal places. A decimal point
followed by one or more nines represents a fractional second, printed
to the given number of decimal places, with trailing zeros removed.
When parsing (only), the input may contain a fractional second field
immediately after the seconds field, even if the layout does not
signify its presence. In that case a decimal point followed by a
maximal series of digits is parsed as a fractional second.
Which means you can leave out the decimal points from the layout and it will parse correctly
ts, err := time.Parse("2006-01-02T15:04:05-0700", currentTime)
For getting the time in UTC simply write ts.UTC()
And for formatting it to RFC3339, you can use
ts.Format(time.RFC3339)
Example
currentTime := "2018-12-17T17:02:04.123+0530"
ts, err := time.Parse("2006-01-02T15:04:05-0700", currentTime)
if err != nil {
panic(err)
}
fmt.Println("ts: ", ts)
fmt.Println("ts in utc: ", ts.UTC())
fmt.Println("RFC3339: ", ts.Format(time.RFC3339))
// output
// ts: 2018-12-17 17:02:04.123 +0530 +0530
// ts in utc: 2018-12-17 11:32:04.123 +0000 UTC
// RFC3339: 2018-12-17T17:02:04+05:30
playground: https://play.golang.org/p/vfERDm_YINb
How about this?
ts, err := time.Parse("2006-01-02T15:04:05.000+0000", currentTime)
since time.RFC3339 is just 2006-01-02T15:04:05Z07:00

Resources