Skip to content
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

Utc time #1943

Merged
merged 1 commit into from Mar 3, 2021
Merged

Utc time #1943

merged 1 commit into from Mar 3, 2021

Conversation

matthiashanel
Copy link
Contributor

@matthiashanel matthiashanel commented Feb 27, 2021

I went through all structs with json fields and replaced them with a custom type UtcTime.
From there I changed all occurrences of time.Time that end up in such a struct as well.
Because if was a different type the changes where rather large.
Because these changes made it all the way to events/monitoring, the new type would break backwards compatibility for the embedding use case. (See commit one of this PR)
Therefore I reverted and stuck with time.Time but added .UTC wherever it was missing.

If we wanted we could use the type (pasted below) in jetstream only where it does not interfere with embedding yet.
The implementation would make sure we use UTC when writing json and only accept times when in UTC.
Comments?

// Time that assures json parsing only emits/accepts times in UTC
type UtcTime time.Time

func (t UtcTime) MarshalJSON() ([]byte, error) {
	return []byte(`"` + time.Time(t).UTC().Format(time.RFC3339Nano) + `"`), nil
}

func (t *UtcTime) UnmarshalJSON(data []byte) error {
	if len(data) <= 2 {
		return fmt.Errorf("time not formated properly: %q", (*time.Time)(t).String())
	}
	pt, err := time.Parse(time.RFC3339Nano, string(data[1:len(data)-1]))
	if err != nil {
		return err
	}
	if pt.Location() != time.UTC {
		return fmt.Errorf("time not in UTC: %q", (*time.Time)(t).String())
	}
	*t = UtcTime(pt)
	return nil
}

Expires time.Time `json:"expires,omitempty"`
Batch int `json:"batch,omitempty"`
NoWait bool `json:"no_wait,omitempty"`
Expires time.Duration `json:"expires,omitempty"`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fyi for Java client that this one would change /fyi @ColinSullivan1 @scottf

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matthiashanel can we switch this change into a different branch? I think there is consensus on changing this one to duration so we can merge and integrate the change into the clients, but not sure right now on the side effects of changing some of the other ones.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, will move the duration into a separate branch

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wallyqs moved the duration change into #1944 and removed it from here

Copy link
Member

@kozlovic kozlovic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite a change, not talking about code changes, but may surprise some users.

As for having a custom type with own Unmarshal, I would not go there if possible. I had a case where I added a custom marshal, but this because really tricky when using embedded types. For instance if Foo embeds Bar and you have a (un)marshaljson for Bar, then it will apply to Foo which means that all fields from Foo will not be marshaled/unmarshaled, etc..

@matthiashanel
Copy link
Contributor Author

matthiashanel commented Feb 27, 2021

This is quite a change, not talking about code changes, but may surprise some users.

As for having a custom type with own Unmarshal, I would not go there if possible. I had a case where I added a custom marshal, but this because really tricky when using embedded types. For instance if Foo embeds Bar and you have a (un)marshaljson for Bar, then it will apply to Foo which means that all fields from Foo will not be marshaled/unmarshaled, etc..

@kozlovic yeah with Unmarshal this became quite unruly. I moved the duration change out as per Wally's suggestion. Now this deals with time only.

@kozlovic
Copy link
Member

@kozlovic yeah with Unmarshal this became quite unruly. I moved the duration change out as per Wally's suggestion. Now this deals with time only.

But still, this is what I meant, that changing time to UTC() may still cause issues/surprises, no? I mean what if users had apps/scripts checking those? That may break no? That being said, 2.2.0 would be a good time and could be listed as a [CHANGED] entry in the release notes.

@ripienaar
Copy link
Contributor

I think just converting to UTC everywhere we need is fine, that's what we should have done before. We might also look at the places we receive timeTime from the user and converting those before storing, this should be fine and safe.

So to me this seems fine - if we already also force all user input to be utc before sticking it into our internal structures? Definitely would not be fine with custom marshallers this late.

I wonder if we need a utility function for getting time.Now().UTC() to normalise thinking about that utilty instead of always having to remember the .UTC() something that I also forget quite often

@matthiashanel
Copy link
Contributor Author

@ripienaar I will add utcNow() and replace time.Now().UTC() with it.

@ripienaar
Copy link
Contributor

@ripienaar I will add utcNow() and replace time.Now().UTC() with it.

well best ask Ivan or Derek what they think about that :)

@matthiashanel
Copy link
Contributor Author

@ripienaar I will add utcNow() and replace time.Now().UTC() with it.

well best ask Ivan or Derek what they think about that :)

@derekcollison do you have an opinion on this?

@derekcollison
Copy link
Member

I agree just make sure all time.Now() have a UTC() on the end is best for now.

@matthiashanel
Copy link
Contributor Author

matthiashanel commented Mar 1, 2021

This change adds .UTC wherever needed (time is exposed externally).
I manually checked all 48 remaining invocations of time.Now().
These are only used locally (mostly to compute timeouts).
@derekcollison Should I add .UTC() to these as well? (Double checking on this. So far I've changed the necessary ones and you mentioned all in you last comment)

Where we end up using .Unix() or .UnixNano() we essentially convert to UTC already.
Where such a time is converted back using time.Unix(...) the resulting time will be in UTC.
Because of this, these calls where skipped.
Initializations for seeds and setting deadlines where skipped too.

List remaining calls: grep time.Now server/*.go -n | grep -e "_test.go:" -e ".UTC\(\)" -e "rand.NewSource.time.Now" -e "rand.Seed" -e "SetReadDeadline.time.Now" -e "SetWriteDeadline.time.Now" -e "time.Now.*.Unix" -v

This also applies to times that end up in that json.
Where applicable moved time.Now() to where it is used.
Moved calls to .UTC() to where time is created it that time is converted
later anyway.

Signed-off-by: Matthias Hanel <mh@synadia.com>
Copy link
Member

@kozlovic kozlovic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@matthiashanel matthiashanel merged commit c50ee2a into master Mar 3, 2021
@matthiashanel matthiashanel deleted the utc-time branch March 3, 2021 02:37
wallyqs added a commit that referenced this pull request May 4, 2023
In #1943 it was adopted to use `UTC()` in some timestamps,
but an unintended side effect from this is that it strips 
the monotonic time, so it can be prone to clock skews when
subtracting time in other areas of the code.
golang/go@e5646b2
wallyqs added a commit that referenced this pull request May 5, 2023
In #1943 it was adopted to use `UTC()` in some timestamps, but an
unintended side effect from this is that it strips the monotonic time
(golang/go@e5646b2),
so it can be prone to clock skews when subtracting time in other areas
of the code.
ReubenMathew pushed a commit to ReubenMathew/nats-server that referenced this pull request May 11, 2023
In nats-io#1943 it was adopted to use `UTC()` in some timestamps,
but an unintended side effect from this is that it strips 
the monotonic time, so it can be prone to clock skews when
subtracting time in other areas of the code.
golang/go@e5646b2
ReubenMathew pushed a commit to ReubenMathew/nats-server that referenced this pull request May 12, 2023
In nats-io#1943 it was adopted to use `UTC()` in some timestamps,
but an unintended side effect from this is that it strips 
the monotonic time, so it can be prone to clock skews when
subtracting time in other areas of the code.
golang/go@e5646b2
ReubenMathew pushed a commit to ReubenMathew/nats-server that referenced this pull request May 30, 2023
In nats-io#1943 it was adopted to use `UTC()` in some timestamps,
but an unintended side effect from this is that it strips 
the monotonic time, so it can be prone to clock skews when
subtracting time in other areas of the code.
golang/go@e5646b2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants