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:
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 }
});
could you elaborate further? So say you save 3pm EET that's 1pm UTC. And when the admin UI retrieves the model you see 1pm displayed?
Nope it's the other way around. The exact timezones don't matter - whatever the diff between client machine's and server machine's timezone is - that's how many hours the date will jump by. Let's call that diff Xh for clarity. My server machine is in UTC timezone.
If I create a new document with a DateTime time value of say 1pm and save it - 1pm UTC will end up in the database, but next time I edit the document I see 1pm + Xh - that's 1pm UTC rendered correctly in my client's timezone. But when I hit save again - that 1pm + Xh goes into the database, as UTC this time. So every time I re-save the document DateTimes shift by Xh.
Does that make sense now?
There is no timezone information in database. DateTime is always saved as UTC.
1) Converting string (or other input formats) to date: String is interpreted as local time based on the enviroment where this conversion happens (in server, typically). Javascript Date internally uses UTC time and this UTC time will be saved to database.
2) Converting saved date to object: The UTC time will be converted to the local time based on the timezone where this conversion happens.
Am hitting the same problem. The dates are converted from my local time to UTC when saving the document, and are displayed as the UTC time in admin ui. This means that when saving the document a second time the date is converted to UTC again.
As @vredchenko stated, every time the document is saved the date shifts forward by the time difference.
Is your server using UTC or your local timezone?
Server is using UTC, hence the date shifting by the timezone difference.
Changing the server timezone is not an acceptable fix.
On Sat, May 30, 2015 at 7:25 pm, keystonejs/keystone
[email protected]
wrote:
Is your server using UTC or your local timezone?
—
Reply to this email directly or view it on GitHub
[https://github.com/keystonejs/keystone/issues/1345#issuecomment-107001815] .[https://github.com/notifications/beacon/ADAoNmFFfX_NMvCtO57_ajf7VE-0i_Ndks5oOV1zgaJpZM4EJekq.gif]
I think this is the problem because currently Keystone doesn't know anything about the local timezone. To fix this, there should a timezone setting which is applied when times are handled.
It would be ideal to have a timezone option in the dialog box in admin,
that is saved with the object.
On Sat, May 30, 2015 at 7:42 PM, Teemu Sirkiä [email protected]
wrote:
I think this is the problem because currently Keystone doesn't know
anything about the local timezone. To fix this, there should a timezone
setting which is applied when times are handled.—
Reply to this email directly or view it on GitHub
https://github.com/keystonejs/keystone/issues/1345#issuecomment-107002532
.
I have this problem in production. The fix above by @lojack is only a workaround in my opinion, cause you can only set the default timezone of the client if i'm correct(?). So if i have users in different countries the fix will only work for my users in a single timezone.
I think this issue can only be fixed by listen to the clients timezone or fill in the value on client instead of render it on server.
Are there any plans to fix this in near future, @JedWatson?
Yes, sorry for the delay! I fixed some timezone issues in the last release but it's a stopgap measure. Going to address this asap.
Great!:) Thanks for the fast reply :+1:
I just discovered the same issue. Has it been fixed yet?
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.
same here
+1
Also experiencing this.
Using version 0.3.16
I have this problem too.
Is there already a solution for this? For a 0.3.x version?
Been working with the same problem for a while now... @JedWatson Any chance a fix is in the works?
We're aware of this bug, and definitely want to fix it. I've marked it critical too!
@mxstbr @vredchenko Here's a workaround.
If you throw this into templates/views/item.jade page it should work. Obviously, I hard coded my field name in there start_time.
$('form').submit(function(e) {
var dateTime = $('[name=start_time_date]').val() + ' ' + $('[name=start_time_time]').val();
var momentDate = moment(dateTime, 'YYYY-MM-DD hh:mm:ss a');
momentDate.add('minutes', momentDate.zone()); // Adjust for Timezone difference!
$('[name=start_time_date]').val(momentDate.format('YYYY-MM-DD'));
$('[name=start_time_time]').val(momentDate.format('h:mm:ss a'));
});
The Admin UI should probably just be sending back an ISO date. Looks like it sends back individual pieces of data, one for the date part and one for the time part. Obviously, it's missing the timezone by doing that.
@techpines is this fix working for you? I tried a similar approach and the form submit callback is not firing.
@bringking Yes, it works for me.
Make sure you wrap it in document ready.
$(function() {
/* code goes here */
});
@techpines thanks, that makes sense.
Just want to say that I'm also experiencing this bug!
Also @techpines I'm still a bit confused about how to utilize this workaround you propose. How does it integrate with items.jade, exactly?
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}
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:
https://github.com/keystonejs/keystone/blob/master/fields/types/datetime/DatetimeField.js#L34
Turn var m = moment(value); into var m = moment.utc(value);
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.
I believe, for the future, keystone should abandon the idea of transmitting or storing local time values altogether and switch to UTC.
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!
Me neither, but if you're on Linux / UNIX / bash, just start your server like this (assuming you don't live in Kiritimati):
TZ="Pacific/Kiritimati" node keystone.js