keystone-classic: DateTime field type timezone bug
Hi. We’ve discovered a bug when using a DateTime field type. It occurs when Keystone admin UI is accessed by a client from a timezone different to the one on the server. Say the client machine is 2h ahead of server (EET), and the server itself is in UTC. Steps to reproduce:
- Fill out a DateTime input with any valid value.
- Save the document.
- Edit the document - you’ll see previously saved value change by timezone delta.
This is a presentation thing - the value in the database, and the value returned to Keystone UI view is correct - but it is then rendered in user’s timezone. When you hit “save” - the value rendered (e.g. 2 hours ahead) gets saved down to the database as a new UTC time.
Here’s the model we used:
var Banner = new keystone.List('Banner', {
map: { name: 'imageTitle' },
autokey: { from: 'imageTitle', path: 'key', unique: true },
sortable: true
});
Banner.add({
imageTitle: { type: Types.Text, label: 'Title' },
alt: { type: Types.Text , label: 'Alternative Text'},
filename: { type: Types.LocalFile, dest: 'public/images/uploads' },
inApp: { type: Types.Boolean, default: true },
targetUri: { type: Types.Text },
validityPeriodStart: { type: Types.Datetime, required: true, initial: true },
validityPeriodEnd: { type: Types.Datetime, required: true, initial: true },
disabled: { type: Types.Boolean, default: false , label: 'Inactive'},
uriSmall: { type: Types.Url, hidden: true },
widthSmall: { type: Types.Number, hidden: true },
heightSmall: { type: Types.Number, hidden: true },
uriMedium: { type: Types.Url, hidden: true },
widthMedium: { type: Types.Number, hidden: true },
heightMedium: { type: Types.Number, hidden: true },
categoryId: { type: Types.Relationship, ref: 'sportCategory' },
categoryName: { type: Types.Text, default: 'default', hidden: true },
brand: { type: Types.Text, default: 'bma', hidden: true },
lang: { type: Types.Text, default: 'en', hidden: true }
});
About this issue
- Original URL
- State: closed
- Created 9 years ago
- Comments: 28 (10 by maintainers)
Exactly, but we should display in the browsers timezone. The problem at the moment, which you correctly identified, is that we’re getting UTC dates from Mongo, displaying them as-is but them saving them as if they were in the timezone.
Instead, we should save and get everything from Mongo in UTC, and then only for display purposes convert it to the browsers timezone, and when they save convert it back to UTC.
The big blocker on this issue is that I don’t have a server in another timezone at the moment, so I can’t replicate & fix it. If you have one, please submit a PR with a fix!
There offending line is here (on master): https://github.com/keystonejs/keystone/blob/master/fields/types/datetime/DatetimeField.js#L23
Without
utc: true
, no timezone conversion is supposed to ever happen (this is foolery anyway, but oh well). However, the API returns a formatted date that has a “Z” tacked to the end (meaning UTC), because MongoDB stores UTC dates). Momentjs then converts this to local time and BOOM! It’s arguable whether the date value from the API (it should not have any timezone information because the timezone is unspecified) or the conversion of timezones on the frontend is at fault.Ways to work around this:
Always parse dates as if they were UTC (even when they are not):
https://github.com/keystonejs/keystone/blob/master/fields/types/datetime/DatetimeField.js#L34 Turn
var m = moment(value);
intovar m = moment.utc(value);
Return the date from the API without any timezone information. I have no idea how to do this, but I was able to simulate it by chopping of the ‘Z’ at the end of the time string:
https://github.com/keystonejs/keystone/blob/master/fields/types/datetime/DatetimeField.js#L23
timeValue: this.props.value && this.moment(this.props.value.slice(0, -1)).format(this.timeInputFormat),
I believe, for the future, keystone should abandon the idea of transmitting or storing local time values altogether and switch to UTC. The only other way to do it correctly is to always store and transmit timezone information as well.
Not sure if this helps. I ran into similar issue with how the admin UI was displaying the local time instead of UTC, in which it was saved. Just add ‘utc: true’ as an option to the field. ie. startTime: {type: Types.Datetime, utc: true}
I am running into the same issue with Date fields. The date is always saved with time T00:00:00Z but when it is retrieved it seems to be localizing the time (-4:00) which results in my Date shifting one day forward (technically the value only shifts 4 hours forward but when only displaying the date, this shifts the day forwards. Additionally the problem compounds… editing a different field and saving saves the new date with time 00:00:00Z and the reload shows another day shifted forward.