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

question - keeping calendar times from millisecond timestamps #59

Closed
SamMatthewsIsACommonName opened this issue Nov 8, 2017 · 7 comments

Comments

@SamMatthewsIsACommonName
Copy link

SamMatthewsIsACommonName commented Nov 8, 2017

Hi,
I'm a bit confused by this but I'd be happy to make some documentation / examples once I understand it a bit more?
basically we store shift clock ins and clock outs as numerical Date.now() values. This enabled us to diff them very easily for lengths of shifts etc.
However as you can imagine problems arise when we try and display those times across different time zones. We want to keep the calendar time (e.g if they clocked in at 7am, we want it to show 7am everywhere).

I see this example:

var rezoned = local.setZone('America/Los_Angeles', { keepCalendarTime: true });

Although I'm not 100% sure at what stage I'd set the zone in our case. My attempts to set it from the utc time or the millisecond time and then format them using toFormat() seem to just result in them being adjusted for the device's location (i.e the calendar times change)

I've read the docs quite a bit and I still can't figure out how this is possible (except that you recommend storing utc strings, so we will start storing these as well).
We also have access to the timezone of the user's device at each end, although I'm still a bit confused as to where and when we need that.

As I said, an example of how to do this would be amazing and then I could make a pull request showing it in a bit more detail if you'd like?

@icambron
Copy link
Member

icambron commented Nov 8, 2017

You have three options:

Option 1: Do something like you're suggesting above. The trick you're missing is that you need to store the zone from the clock in, not the zone from where you're displaying it.

// let's say the clock in happens at 7am US Pacific Time
dt = DateTime.local()
dt.toUTC().toISO() //=> '2017-11-08T15:00:00.000Z' (can store however you want)
dt.zoneName //=> 'America/Los_Angeles' (also store this)

// then another client in, say, US Eastern time handles that time:
DateTime.fromISO( '2017-11-08T15:00:00.000Z').setZone('America/Los_Angeles').toLocaleString(DateTime.TIME_SIMPLE) //=>
'7:00 AM'

In other words, if you want to display the times in a different local time, you need to know that other local time's zone. Depending on what you're storing the datetime in, you may or may not need to stash that time zone separately. Option 1 is the most thorough and flexible approach.

Option 2: forget zones and just use offsets. As long as you don't need to do math on the times (e.g. adding or subtracting hours to and from it), this suffices:

// in LA:
dt = DateTime.local()
dt.toISO() //=> '2017-11-08T07:00:00.000-08:00'

// in NY:
dt = DateTime.fromISO('2017-11-08T07:00:00.000-08:00', { setZone: true })
dt.toFormat('hh:mm') // 7:00

// although this is a bug!!
dt.toLocaleString(DateTime.TIME_SIMPLE) //=> '3:00 PM' (??)

Not sure what's up with that last part. I'll have a look.

Option 3: do the exact opposite of my recommendation re: UTC times and instead only store local times. If you're using something like Postgres, that's TIMESTAMP [WITHOUT TIMEZONE ]. If you're just storing a string, you'd need to cut the offset off the output to Luxon's toISO output. Regardless, the point is that what you're storing doesn't encode the offset or zone, just the time. Then you let that time be interpreted as a local time anywhere it's read:

// in NY:
dt = DateTime.fromISO('2017-11-08T07:00:00.000') // notice no offset
dt.toLocaleString(DateTime.DATETIME_SHORT) //=> '11/8/2017, 7:00 AM'

I don't really recommend this because if you need to do anything other than display it, you'll find yourself working with the wrong time.

@SamMatthewsIsACommonName
Copy link
Author

Thanks a lot for the detailed response. I think I was essentially implementing the equivalent of option two at first and not seeing any change, which may have been due to that bug you mentioned?
Option one definitely makes the most sense though! Thanks

@icambron
Copy link
Member

Pulling this over from the other ticket:

Should I maybe be using { keepCalendarTime: true } when I set the zone from the clock in point of the shift?

No, you really shouldn't need to. If you've provided a specific epoch millisecond (or an ISO string with an offset or Z), that identifies the right global, universal time exactly, regardless of zone. The only thing you need to do is to tell that date time to render in the zone where that time is called 7am instead of whatever the browser's time zone tells you to call that time. So if you setZone() to the time zone where the clock in was generated, it should give you the expected result.

You'll need to post some code.

@SamMatthewsIsACommonName
Copy link
Author

SamMatthewsIsACommonName commented Nov 11, 2017

OK thanks for clarifying that makes sense. So is it safe to say the toLocaleString method works like so:

  • Adjust time from utc time or epoch milliseconds to a) whatever time zone was explicitly set or b) the time zone the browser or device considers itself to be in if zone isn't set?

I wasn't seeing the time being modified consitsently across timezones (it was jumping to an adjustment to the local time even after setting the zone) but I'll update to new version and see with my colleague overseas if he is seeing a change with the updated version and

const formattedTime = DateTime.fromISO(ourDatabaseIsoUtcTime).setZone(ourDatabaseShiftTimezone).toLocaleString(DateTime.TIME_SIMPLE)

thanks!

@icambron
Copy link
Member

icambron commented Nov 11, 2017

That code should work, assuming that ourDatabaseIsoUtcTime has offset information on it, such as (preferably) Z or (less preferably but functionally no different) -400 (if it doesn't, it will be interpreted as a local time, which is not what you want).

Adjust time from utc time or epoch milliseconds to a) whatever time zone was explicitly set or b) the time zone the browser or device considers itself to be in if zone isn't set?

Mostly. It is true that toLocaleString() will format the date in the configured zone, which by default is just the local zone of the system. The "adjust the time from UTC" thing doesn't happen in toLocaleString though; it happens in fromISO; that's why fromISO takes some options about the resulting zone will be. It's also why the first paragraph here is important. This is discussed in Strings that specify an offset

Also of use when thinking about that is this little snippet from the docs:

It's important to remember that a DateTime represents a specific instant in time and that instant has an unambiguous meaning independent of what time zone you're in; the zone is really piece of social metadata that affects how humans interact with the time, rather than a fact about the passing of time itself. Of course, Luxon is a library for humans, so that social metadata affects Luxon's behavior too. It just doesn't change what time it is.

@SamMatthewsIsACommonName
Copy link
Author

OK thanks. We're going to dry an do some debugging remotely with a colleague in another timezone, and have a deep look at what we're getting as a result at each step. Will get back with results, thanks!

@icambron
Copy link
Member

@SamMatthewsIsACommonName going to close this out. But LMK if you run into more trouble and we can reopen and continue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants