-
Notifications
You must be signed in to change notification settings - Fork 18k
proposal: time: direct support for Unix millis and micros #18935
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I'm in favor of this. At $WORK we have an auxiliary time package that defines
and these see a fair amount of use since ms-since-epoch is a common timestamp standard (as one example, you see it a lot in Javascript/web contexts since |
The fact that Date.now() returns ms in Javascript is good context. What common systems use micros? The getters on time are unobjectionable; the new constructor names are not as nice. It would be easier to accept this proposal if we had a better idea for the names. |
@rsc what do you think would be nicer for the constructor names? Is it better to name them |
I don't think we should add milli without adding micro, because it would be a seemingly arbitrary omission to have 10^0, 10^-3, 10^-9, completely skipping 10^-6 But in any case, I looked around and I found a few instances of use for microseconds in time as well: For cases that use floats instead of ints, microsecond precision also seems to be used sometimes: |
@aebrahim I didn't look at all of those, but the ones I did all seemed to be about turning (in Go terms) a time.Duration into microseconds, not about a time stamp expressed in microseconds since 1970. Go already does have time.Microsecond for computing with durations. |
OK, I see what you mean. Here's a list of functions that use seconds, microseconds % seconds since the unix epoch:
|
@aebrahim OK, but that's still not really motivation for adding t.UnixMicros() int64. What I'm hoping for is some evidence that this is a common serialized time format, used in file formats, network protocols, or other places where times are written down. |
So a bunch of thoughts on this:
|
Personally I don't see anything very wrong with
Not that I object strongly to I do agree with Russ: we should add |
My main issue with this is that it's the opposite of what you'd expect from unit conversions. t.UnixNano() * int64(time.Nanosecond) / int64(time.Microsecond) However, this is how you would do a unit conversion, with microseconds in the numerator instead of denominator: This seems makes an already error prone operation even worse. This is way more error prone than just using microseconds everywhere to start with instead of using go's time. |
The formula is not too hard to come up, even if from unit conversion
perspective:
to convert ns to us, we need to multiply us/ns.
assuming Go's abstract time unit is UNIT, then
time.Nanosecond = UNIT/ns, and
time.Microsecond = UNIT/us.
Therefore to get the number of us/ns, we need scale = time.Nanosecond /
time.Microsecond.
That is: t.UnixNano() * time.Nanosecond / time.Microsecond.
|
I personally think that the math working out only with UNIT/ns really violates the principle of least astonishment. Especially when in other places, we do things like |
OK, but that is an argument for a |
I for one would be ecstatic with a function like func (d Duration) ToUnit(u Duration) { and then it would look like: (Warning I am not a golang nor naming expert) |
@ianlancetaylor that's a good point. I just think there has to be a better way to to convert unix timestamps without doing unit conversions. It may not necessarily be what I proposed, and I'd be happy to change it with whatever idea we think is best here Another potential idea I'd like to throw into the mix is: func (d Duration) Microseconds() int64 { ... }
func (d Duration) Milliseconds() int64 { ... }
func (t Time) UnixDuration() Duration { ... } This would let you do time.Now().UnixDuration().Microseconds() However, we'd still need a way to construct the time from a timestamp. |
Here is another proposal which goes both directions (to/from timestamps) and can be done with only 3 additions. We could add var UnixEpoch = time.Unix(0, 0) // a constant of the time at the start of the unix epoch
func (d Duration) Milliseconds() float64 { ... }
func (d Duration) Microseconds() float64 { ... } This would allow the following // int64 timestamp to time
var tsMicro int64 = 1000000
t := time.UnixEpoch.Add(time.Duration(tsMicro) * time.Microsecond)
// time to int64 timestamp
ts = t.Sub(time.UnixEpoch).Microseconds()
now := time.Since(time.UnixEpoch).Microseconds() The one thing I don't like about this is the kind of clunky |
@mattalbr Supporting s and ns is not arbitrary at all. The full precision stored in the time.Time is nanoseconds, hence t.UnixNano() returning nanoseconds. And then it turns out that many common systems and formats - among them tar and gzip in the standard library - store Unix time in seconds (aka time_t), hence t.Unix() returning seconds. What's missing from this proposal is a strong motivation for adding t.UnixMicro and t.UnixMilli and the reverse converters. The best motivation would be easier interoperability or less worry about overflow (since the time package could do a better job than Nano/1000) when using some existing systems, formats, or wire protocols that represent time in these formats. It's true that many Unix-based operating systems use Unix millis and micros in system calls, but Go abstracts those system calls away. At Google we have a variety of systems that store time in Unix micros, and we too have a local conversion library to make that easier. But importance to Google alone is not a compelling argument for adding the API surface to package time, so we haven't. And more recently, the standard proto buffer Timestamp now uses nanoseconds. @cespare mentioned that systems at his work use Unix millis internally, because Java System.currentTimeMillis and Javascript Date.now both return Unix millis, and they have a local conversion library to make that easier. What's needed to move forward is more evidence that this is a widespread need. We have not been able to find existing systems, formats, or wire protocols that use this representation. Those would still be nice, but if there is enough evidence of internal systems at various places that need these formats, that might be enough. |
@rsc I think it's pretty clear this is a pain point between stack overflow, Google code internals, and other testimonials. We're trying to solicit suggestions to alleviate that pain point. It doesn't have to be UnixNano() and UnixMicro(); we would love if you proposed something different that you think more generalizable/useful/idomatic/what have you. I really like @aebrahim's first suggestion of UnixDuration() and the two methods on Duration. Re: going from int64 (micros) to Time, I still don't love that you have to multiply by time.Microsecond. What would people think about a constructor for Duration that takes the units? So: and the reverse |
As I pointed out in my previous message, Google's internal systems don't count by themselves, unless there is reason to believe that experience is shared by others. That's why I'm asking for examples of systems that would need these conversions. Concrete details are great. Vague references to stack overflow and "other testimonials" not so much. Let's focus on understanding the justification / motivation before we start proposing or evaluating alternative APIs. |
I think what Matt and I are trying to say is that time conversions in go are generally problematic. Unix microsecond timestamp conversions are a manifestation of that. I'm not looking for a Because of the pushback on a Unix-specific microseconds function, I'm instead suggesting we change this proposal to make it simpler to just convert units for duration (which is a very common operation in many different languages), and then provide a canonical way to convert from time to Duration since the Unix epoch. The initial list I sent out (I believe) clearly establishes that converting between the equivalent of a Duration and micro/milli seconds is common in many languages. Can we at least agree on these 2 functions? func (d Duration) Milliseconds() float64 { ... }
func (d Duration) Microseconds() float64 { ... } |
A Time is an instant. A Duration is a delta between two instants. They have the same units, but it is important not to confuse them. A Duration can be meaningfully added to a Time. A Time cannot. Adding a t.UnixDuration() method violates that very helpful separation. We are not going to add that method nor in any other way encourage converting Times to Durations. If there is a common need for UnixMicro and UnixMilli, we can add them. I am still waiting for evidence of that. @aebrahim and @mattalbr, you work at Google, where lots of internal systems store Unix micros as a time format. You're generalizing from that setting to the world outside Google, but there's no evidence here that the generalization is valid. Regarding time.Duration conversions, the idioms are:
I'm sorry, but that doesn't seem to me “generally problematic.” In any event, if you want to discuss the Duration API, best to start a new issue. Let's keep this one about whether to add t.UnixMicro and t.UnixMilli. |
Because I was unable to find examples of this being a common need, I am leaning toward not doing this. I did web searches and asked on Twitter and got nothing back except for one or two internal uses. |
Only integer! <=> date! is allowed, the integer value is Unix epoch time in seconds.
https://play.golang.org/p/UaXMVCQ6pW `package main import ( func main() { prints Hello, playground shows that due to int overflow, we have some issues with time representation. Consideration the buzz around the blockchain technology for example and what it can accomplish, and because this is an immutable distributed ledger ... if it becomes very popular maybe those entries would have to still be accessible later than year 2262 (with a consistent timestamp representation). What if we could get a decomposed version of a time (with a Timestamp() int64,int64 method), with a seconds representation, and the additional nanoseconds precision. We already have time.Unix(sec,nanosec) that constructs time from a count of sec and nanosecond. It could help store dates as n1(sec)+n2(nanosecond) . Then a "common" timestamp would be seconds based and we could store some additional precision (to the nanosecond) if required without overflowing. Reducing precision would just be an integer division of the nanosecond count to the precision level (for instance ns/time.Millisecond). The first part count (seconds) of the timestamp is already returned by t.Unix(), the t.UnixNano() is the one that gets overflowed because returning the whole nanoseconds count, instead of only the precision that can not be returned by the t.Unix() |
just discovered that this basically is how the timestamp is represented in protobuf: seconds + nanoseconds. So this decomposition seems relevant. |
@laahs This issue is closed and you seem to be talking about a different problem in any case. Please use a forum (https://golang.org/wiki/Questions) or open a new issue. Thanks. |
Initial tl;dr: Go could make timestamp handling much easier by providing the following functions in its time library:
Golang has time.Time as the idiomatic way of representing a time. However, not every API we encounter has this, and lots of them return Unix-style timestamps as int64 (sometimes uint64) in units of ms, us, or ns. For example, this will always occur when using API's that operate on protocol buffers, which use integers for timestamps instead of a native time type.
If we happen to have nanosecond timestamps, go is pretty nice. However, if we're using microseconds, then it gets fairly annoying to use time.Time, as you'll have to do error-prone type conversions everywhere, or rely on a timeutil library you write yourself for something which feels like it should be built in.
This is the type of stuff we have to do right now when dealing with niceapi that uses time.Time and otherapi that uses microseconds:
But why not
The text was updated successfully, but these errors were encountered: