-
Notifications
You must be signed in to change notification settings - Fork 7k
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
[feature] Durations gain validity #3611
Conversation
src/test/moment/duration.js
Outdated
@@ -53,10 +53,17 @@ test('milliseconds instantiation', function (assert) { | |||
|
|||
test('undefined instantiation', function (assert) { | |||
assert.equal(moment.duration(undefined).milliseconds(), 0, 'milliseconds'); | |||
assert.equal(moment.duration(undefined)._isValid, true, '_isValid'); |
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.
This needs an isValid()
accessor function. We don't want people checking underscored properties.
src/test/moment/duration.js
Outdated
@@ -53,10 +53,17 @@ test('milliseconds instantiation', function (assert) { | |||
|
|||
test('undefined instantiation', function (assert) { | |||
assert.equal(moment.duration(undefined).milliseconds(), 0, 'milliseconds'); | |||
assert.equal(moment.duration(undefined)._isValid, true, '_isValid'); | |||
}); | |||
|
|||
test('null instantiation', function (assert) { | |||
assert.equal(moment.duration(null).milliseconds(), 0, 'milliseconds'); |
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.
This doesn't really fix the #3225 because humanize
will still output "in a few seconds".
@icambron let me know what you think! I've fixed as per your review. |
@marwahaha this is a good starting point. But to make it really work isValid needs to be viral. What I mean is that if a duration is invalid most operations should return invalid durations/values/whatever. Here's a list of all the prototype functions and what they should return. This should be implemented and tested (check
|
src/lib/duration/iso-string.js
Outdated
@@ -50,3 +54,8 @@ export function toISOString() { | |||
(m ? m + 'M' : '') + | |||
(s ? s + 'S' : ''); | |||
} | |||
|
|||
export function toJSON() { | |||
// this may not be fully implemented |
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.
For some reason, .toJSON
was previously mapped to .toISOString
. I've created a separate function, at least, to handle the difference when the duration is invalid.
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.
@maggiepint answered your question -- just patch toISOString
and leave toJSON
an alias to it.
Ok, I've done this @ichernev Two nits: |
How does this look? |
Okay, circling back to this. This is good work, thank you. What other cases do you expect to handle with As for what constitutes an invalid duration - that one might best be handled by @mj1856 who is probably the most interested in standards, though I'm pretty sure @butterflyhug is pretty darn good at durations too from his old gig. Based on the Wikipedia interpretation of the standard though, I believe that any duration with a decimal on a unit other than the smallest unit is technically invalid. |
Ok @maggiepint !
|
Let me know how I can help here @mj1856 @butterflyhug |
Pinging this thread again @mj1856 @maggiepint @butterflyhug |
What you are saying is correct. Numbers other than the smallest should be integers according to the spec. |
Ok this is updated! @maggiepint I did change two tests because they used an invalid duration (-0.5 years, 1 month). Let me know if I can help further. |
Bumping this thread. |
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 code and tests look good, just a few small tweaks and its ready to go.
As I said in one of the comments, we can't go too harsh on bad input, because we were happily accepting it in the previous release, and its not really obvious what is right
anymore: duration({d: null})
-- so is this invalid, or should it be treated as 0
? I'd say keep it rolling for now and rething in v3.
src/lib/duration/constructor.js
Outdated
var normalizedInput = normalizeObjectUnits(duration); | ||
|
||
this._isValid = isDurationValid(normalizedInput); | ||
this.isValid = 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.
isValid
should be a regular prototype function, not something attached to this
on every object creation.
src/lib/duration/iso-string.js
Outdated
@@ -50,3 +54,8 @@ export function toISOString() { | |||
(m ? m + 'M' : '') + | |||
(s ? s + 'S' : ''); | |||
} | |||
|
|||
export function toJSON() { | |||
// this may not be fully implemented |
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.
@maggiepint answered your question -- just patch toISOString
and leave toJSON
an alias to it.
src/lib/duration/prototype.js
Outdated
@@ -8,7 +8,7 @@ import { as, asMilliseconds, asSeconds, asMinutes, asHours, asDays, asWeeks, asM | |||
import { bubble } from './bubble'; | |||
import { get, milliseconds, seconds, minutes, hours, days, months, years, weeks } from './get'; | |||
import { humanize } from './humanize'; | |||
import { toISOString } from './iso-string'; | |||
import { toISOString, toJSON } from './iso-string'; |
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.
So no changes in this file whatsoever.
src/lib/duration/valid.js
Outdated
export default function isDurationValid(m) { | ||
for (var key in m) { | ||
if (ordering.indexOf(key) === -1 || | ||
m[key] !== undefined && isNaN(parseInt(m[key]))) { |
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'm amazed jscs doesn't catch this but this line needs to be indented 8 more spaces (continuation indent).
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.
Rewrite to:
if (!(ordering.indexOf(key) !== -1 && (m[key] == null || !isNaN(m[key]))))
The tricky part is -- I want to explicitly allow both null and undefined (they'll be treated as 0
because of the || 0
code later). Also you don't need parseInt when doing isNaN (its built in).
I hope we can have some stricter rules in a future major release, but right now even this is getting dangerous (as it will break someone's code).
src/lib/duration/constructor.js
Outdated
return this._isValid; | ||
}; | ||
|
||
var years = this._isValid && normalizedInput.year || 0, |
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 have a one var
policy in jshint, so this won't fly. Just don't change the code (leave it as it was) -- if its invalid its safe to exit this function. All other functions check for isValid
, so its fine if the internals are messed up.
src/lib/duration/valid.js
Outdated
if (unitHasDecimal) { | ||
return false; // only allow non-integers for smallest unit | ||
} | ||
if (parseFloat(m[ordering[i]]) !== parseInt(m[ordering[i]])) { |
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.
Use toInt
functions from the src/lib/utils/to-int.js
instead of parseInt
.
}); | ||
|
||
test('NaN instantiation', function (assert) { | ||
assert.ok(isNaN(moment.duration(NaN).milliseconds()), 'milliseconds should be NaN'); |
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.
That is good. For tests it makes sense to add a helper method moment.duration.invalid(), the same way we have for moment, so you can properly construct an invalid duration.
src/test/moment/duration_invalid.js
Outdated
for (i = 0; i < invalids.length; ++i) { | ||
invalid = invalids[i]; | ||
|
||
assert.ok(!invalid.add(5, 'hours').isValid(), 'invalid.add is invalid'); |
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.
For loops like this -- include the i
into the string description (so when it fails you can debug it), or scrap the loop altogether -- this is unit test, you check first what is invalid, and then you check what invalid means, you don't have to test end-to-end
.
Ok @ichernev !
|
src/test/moment/duration_invalid.js
Outdated
assert.ok(isNaN(m.valueOf())); | ||
}); | ||
|
||
test('valid duration - as per @ichernev', function (assert) { |
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.
Well, no need for that mention, I'm famous enough already ;-)
@marwahaha thank you!
Follow the chain from
Well, the |
Ok @ichernev I think I am done making revisions. Do let me know. |
@marwahaha thank you, looks great. |
Merged in a05fbb2 |
[feature] Durations gain validity
@marwahaha lets PR the v3 change agains the |
In version 2.18.0, durations gained validity: https://gist.github.com/ichernev/78920c5a1e419fb28c6e4546d1b7235c More info: moment/moment#3611
In version 2.18.0, durations gained validity: https://gist.github.com/ichernev/78920c5a1e419fb28c6e4546d1b7235c More info: moment/moment#3611
This was hoping to resolve #3225
_isValid
property for durations, and an accessorisValid()
0
and set._isValid
tofalse
.humanize()
will return the translated version ofInvalid Date
if not validduration(null)
,duration(undefined)
,duration()
will all be validduration(NaN)
is invalid