-
Notifications
You must be signed in to change notification settings - Fork 27.4k
fix(input): allow overriding timezone for date input types #16336
Conversation
0da52ef
to
d172e8e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice work @Narretz
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we should document that changing the value timezone will have no effect until the next parse/format.
src/ng/directive/input.js
Outdated
var previousDate; | ||
|
||
ctrl.$$parserName = type; | ||
ctrl.$parsers.push(function(value) { | ||
if (ctrl.$isEmpty(value)) return null; | ||
if (ctrl.$isEmpty(value)) { | ||
previousDate = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure this change is valid (in fact I think it is not).
AFAICT the idea of previousDate
is to store the last valid date set (to use it for feeling missing date field that cannot be derived from the input value).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's not valid, then why are there no test failures? :D
The formatter is doing the same thing afaict - it sets previousDate = null if the model is not a valid date (empty is not a valid date, too).
The specific test at https://github.com/angular/angular.js/pull/16336/files/d172e8ecb650ad3220e00e46b08325ad3672e9f1#diff-ce6d0f141f60d25eede7abf044d04b93R1968 shows that when clearing out the input, previousDate is still set and this leads the next input to use an incorrect value for the hour part.
We'd need a test case to show that this is a regression.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's not valid, then why are there no test failures? :D
Valid point...nooot 😛
The formatter is doing the same thing afaict - it sets
previousDate = null
if the model is not a valid date (empty is not a valid date, too).
That is the point. The formatter processes the model value and previousDate
keeps track of the last valid (date) model value.
The specific test at https://github.com/angular/angular.js/pull/16336/files/d172e8ecb650ad3220e00e46b08325ad3672e9f1#diff-ce6d0f141f60d25eede7abf044d04b93R1968 shows that when clearing out the input, previousDate is still set and this leads the next input to use an incorrect value for the hour part.
"use an incorrect value for the hour part" is subjective. I'd say currently it uses "the correct value for the hour part".
The idea is to be able to set the hour for a date field. AFAICT, the only way to achieve this today is by setting the model to a date with the desired date. Then, as the user edits the field (including emptying and refilling it) the hour/minute/second part is preserved. I think this is a desired property (I am pretty sure I've used it in my apps 😃).
In any case, I believe this change is breaking not related to this PR. It should go into a separate PR (where it can be debated further 😁).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea is to be able to set the hour for a date field.
Ah, that's right. Did you get this from the code or did you search for the commit? Here's the commit: 1a1ef62
As you can see, there are not tests for type=date ... :<
This is at least confusing, because as you can see in the test, the date is re-using the hour part that was set for -0500, even though the timezone is set to UTC again. But maybe this is a contrived example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't search for the commit (I didn't expect there to be a commit specific for that - I assumed it was part of the initial implementation) 😃
So, I am not sure what the correct behavior is here (wrt to changing the timezone) 😕
Maybe keep track of the previous timezone and re-adjust? Or accept the fact that changing the timezone may change the time part?
Timezones are tricky 😁
src/ng/directive/input.js
Outdated
@@ -1456,6 +1459,7 @@ function createDateInputType(type, regexp, parseDate, format) { | |||
} | |||
if (isValidDate(value)) { | |||
previousDate = value; | |||
var timezone = ctrl.$options.getOption('timezone'); | |||
if (previousDate && timezone) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit (and unrelated to this PR): previousDate
will always be truthy here (else isValidDate()
would be false).
test/ng/directive/inputSpec.js
Outdated
inputElm.controller('ngModel').$overrideModelOptions({timezone: '-0500'}); | ||
|
||
$rootScope.$apply(function() { | ||
$rootScope.value = new Date(Date.UTC(2014, 6, 1)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the year change (here and below)? I would stick to the minimum necessary changes to avoid confusion.
Regarding the docs - any property changes in ngModelOptions only have an effect after the next update. We could add this info in overrideModelOptions generally. |
I've removed the date reset and added a different fix that should account for changing timezones. I'm not 100% happy with the implementation, but I also don't want to spend too much time on this. |
93a98f5
to
3a985cf
Compare
@gkalpak could you give this another look? |
test/ng/directive/inputSpec.js
Outdated
helper.changeInputValueTo('2013-W03'); | ||
expect(+$rootScope.value).toBe(Date.UTC(2013, 0, 17)); | ||
|
||
inputElm.controller('ngModel').$overrideModelOptions({timezone: '+5000'}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this really supposed to be +5000
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Revised the test and added more comments.
test/ng/directive/inputSpec.js
Outdated
inputElm.controller('ngModel').$overrideModelOptions({timezone: '+5000'}); | ||
|
||
$rootScope.$apply(function() { | ||
// the 17. with an offset of +5000 moves the date into next week |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you expand a bit on this comment? It mentions 17.
(which is in German format 😛), but the code uses 18
. Also, how does it move to the next week?
|
||
inputElm.controller('ngModel').$overrideModelOptions({timezone: 'UTC'}); | ||
helper.changeInputValueTo('23:02:00'); | ||
// The year is still set from the previous date |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
test/ng/directive/inputSpec.js
Outdated
@@ -1879,6 +1966,30 @@ describe('input', function() { | |||
dealoc(formElm); | |||
}); | |||
|
|||
it('should not reuse the hour part of a previous date object after changing the timezone', function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how this test checks that the hour part of the previous date object isn't used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also Revised this test and added more comments.
src/ng/directive/input.js
Outdated
// Note: We cannot read ctrl.$modelValue, as there might be a different | ||
// parser/formatter in the processing chain so that the model | ||
// contains some different data format! | ||
if (timezone && previousTimezone && previousTimezone !== timezone) { | ||
// If the timezone has changed, adjust the previousDate to the default timzeone |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
timzeone --> timezone
src/ng/directive/input.js
Outdated
// Note: We cannot read ctrl.$modelValue, as there might be a different | ||
// parser/formatter in the processing chain so that the model | ||
// contains some different data format! | ||
if (timezone && previousTimezone && previousTimezone !== timezone) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need timezone
or previousTimezone
to be truthy? Don't we always need to adjust as long as previousTimezone !== timezone
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do need previousTimezone to be defined, since we pass it to addDateMinutes. timezone must not be defined, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've revised the tests in hope that they are clearer now, or at least it's easier to understand them. Let me know if I have failed :D
src/ng/directive/input.js
Outdated
// Note: We cannot read ctrl.$modelValue, as there might be a different | ||
// parser/formatter in the processing chain so that the model | ||
// contains some different data format! | ||
if (timezone && previousTimezone && previousTimezone !== timezone) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We do need previousTimezone to be defined, since we pass it to addDateMinutes. timezone must not be defined, though.
test/ng/directive/inputSpec.js
Outdated
helper.changeInputValueTo('2013-W03'); | ||
expect(+$rootScope.value).toBe(Date.UTC(2013, 0, 17)); | ||
|
||
inputElm.controller('ngModel').$overrideModelOptions({timezone: '+5000'}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Revised the test and added more comments.
test/ng/directive/inputSpec.js
Outdated
@@ -1879,6 +1966,30 @@ describe('input', function() { | |||
dealoc(formElm); | |||
}); | |||
|
|||
it('should not reuse the hour part of a previous date object after changing the timezone', function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also Revised this test and added more comments.
test/ng/directive/inputSpec.js
Outdated
|
||
//To check that the timezone overwrite works, apply an offset of +24 hours. | ||
//Since January 19 is a Saturday, +24 will turn the formatted Date into January 20 - Sunday - | ||
//which is in calendar week 4 instead of 3. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing whitspace after //
😇
test/ng/directive/inputSpec.js
Outdated
|
||
// Emptying the input should clear the cached date object | ||
helper.changeInputValueTo(''); | ||
// At this point, the cached Date has its hours set to to 20 (00:00 - 05:00 = 19:00) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am confused 😕
Are you saying that 19 === 20?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, just a result of trying to understand timezones. If you log a date, you always get it with the local timezone offset which is +1 for me, which means 20. But 19 is correct.
e843205
to
347a1aa
Compare
347a1aa
to
7a9447c
Compare
This commit also fixes a bug where part of the Date object was re-used even after the input was emptied. Fixes angular#16181 Closes angular#13382
7a9447c
to
6e618ef
Compare
Fixes #13382
Closes #16181
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
fix
What is the current behavior? (You can also link to an open issue here)
See #16181
What is the new behavior (if this is a feature change)?
overrideModelOptions can be used to change the timezone.
Does this PR introduce a breaking change?
No
Please check if the PR fulfills these requirements
Other information: