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

duration().days() returns inconsistent value. #961

Closed
thatjpk opened this issue Jul 29, 2013 · 16 comments
Closed

duration().days() returns inconsistent value. #961

thatjpk opened this issue Jul 29, 2013 · 16 comments

Comments

@thatjpk
Copy link

thatjpk commented Jul 29, 2013

See this fiddle: http://jsfiddle.net/KbNxf/

Basically, if I create two equivalent duration objects, one with years/months/days and the other with just days, the second's days() method returns a value different than the first.

Am I correct in thinking these two durations should be equivalent, and days() should return the same thing?

@ichernev
Copy link
Contributor

When you specify days only, then moment guesses that months have 30 days and that's how the 2 years and 3 months are formed. Of course that's not 100% correct and it also gives ~5 days per year difference (12 * 30 = 360 vs 365), so that's normal. The implementation can probably try and be smarter (by using 365 for years), but that's not the case, and even if that were true it would still be off (leap years, months with 31 days etc).

@mattjohnsonpint
Copy link
Contributor

I would argue that you shouldn't be able to arbitrarily convert a duration to every possible unit. Other frameworks/languages specifically disallow conversions from variable-length types such as Month and Year. Moment seems to take a different approach, defining a month as 30 days and a year as 365 days (from the documentation). But this seems rather arbitrary. I'd rather not see methods that have hidden connotations.

For reference, I checked TimeSpan in .Net, Period and Duration in Noda Time, timedelta in python, and DateInterval in PHP. None of these support this operation.

Think of this related question in human terms, "How many days are there in 3 months?" Clearly, that depends on when you start counting from. Without a starting moment, the only correct answer is "it depends". Moment.js is saying "exactly 90", but IMHO, it should either say "approximately 90", or it should prevent you from asking the question.

@ichernev
Copy link
Contributor

In most cases this is used for non critical operations, and in that case an approximation works just fine. If you really care about precision you'd implement it yourself, as has always been the case.

@thatjpk
Copy link
Author

thatjpk commented Jul 30, 2013

Yeah, I'm thinking that unless you're working with specific periods in time, then approximations like 365 or 360 days = 1 year, and 30 days = 1 month are okay. As soon as you start involving dates, like asking stuff like "what will the date be 800 days from today?" then sure, leap years and specific month lengths and stuff matter. But working with just durations, I think approximations are okay as long as they're well documented.

Anyway, so if I understand correctly, 365 is used when moment goes from years-to-days, and 360 is used for days-to-years? So like this, http://jsfiddle.net/v7N8K/. Even if the 360 arises by way of the 12 * 30 days/month approximation, I fee like it should stick to one number or the other. I'm not sure if it matters which, but I think it should be consistent.

@ichernev
Copy link
Contributor

ichernev commented Aug 7, 2013

I agree about 360 vs 365. I'd be happy to merge a pull request :)

@smhg
Copy link

smhg commented Aug 30, 2013

I hope I can keep this discussion going.
Please read the following with an open mind. I am not finger-pointing in any way, on the contrary: I am very grateful for the enormous help momentjs has been to me.

Moment.js today is by far the most popular (if not the only) date library.
The comparison @mj1856 makes with other languages is not so far-fetched. Because of its success momentjs is the de-facto standard for JavaScript.
While original goals/motivations might have been different, one could say that its current status justifies an evaluation of some of these original goals/motivations. Simply put: a lot is depending on the solidity of momentjs these days.

Approximations to date calculations/manipulations are definitely useful, but maybe it is not a bad idea to have an underlying "solid" library? The approximations could be done on top of this. Or even as a plugin (don't shoot me).

Only having experience with PHP's Date and Time implementation, I can say a lot of effort went into that to make it both ISO compliant and un-opinionated.

A little background behind my comment: currently I'm trying to create a clone of PHP's DatePeriod as a plugin to momentjs. I have to do a lot of date calculation/manipulation in the application I'm working on. Durations (~PHP's DateInterval) and periods (~PHP's DatePeriod) play a crucial role in this.

@mattjohnsonpint
Copy link
Contributor

@smhg You've summarized my exact feelings. The key question is - is Moment primarily about making it easier to manipulate values for display? Or is it primarily about being precise so you can't screw things up?

The banner on the home page says

A javascript date library for parsing, validating, manipulating, and formatting dates.

Much of what I've loved about moment is in the parsing and formatting areas, and in that regard - I see no problems with approximations. Functions like .fromNow() are wonderful.

But in the validation and manipulation areas, my personal opinion is that we should be more rigid. Making approximations when you are calculating seems like a recipe for shooting the proverbial foot off.

But I guess it comes down to intent of the design. Perhaps @timrwood can weigh in on his thoughts.

There have been other libraries for date/time in javascript before, such as DateJS which got caught up in this problem. People expected it to be accurate, when it wasn't. Then there's libraries like Sugar that does some minor amount of manipulation, but focuses primarily on parsing humanized strings. I'm not sure which Moment is more like. Well, perhaps the comparison is unfair. There is plenty of room for healthy competition in this space.

With that in mind, I'm wondering if perhaps there's room for a separate project that focused less on parsing and formatting, and more on precision and safety. Something like Noda Time or Joda Time for JavaScript. It would have a completely different design approach, so this isn't something that could be a moment plugin, but it could certainly interact with moment or sugar to do parsing and formatting. The key design differences would be:

  • Focused on calculation over formatting - so very little string manipulation if any.
  • Would use numeric types internally, rather then the Date type.
  • All objects would be treated as immutable types, rather than the mutable approach Moment takes.

If there is interest in this, I'd be glad to spike a prototype and start a separate project going. I really love Moment though, and would want to do this in friendly cooperation - not in any sort of aggressive or overtaking manner. The final product would "play nice" with moment, and sugar, and others. Please let me know what you think.

The alternative would be to work out exactly what is and isn't precise in Moment and clearly document those things. The approximations talked about earlier in this thread would probably just be the tip of the iceberg.

@timrwood
Copy link
Member

timrwood commented Sep 3, 2013

The reason for using approximations in moment is because there aren't really any correct answers for some of the problems.

The question at hand relates to converting between days, months, and years.

How many months in a year?

  • Easy, 12. (Unless using a calendar that uses leap months)

How many days in a year?

How many days in a month?

  • In a Gregorian calendar, 28, 29, 30, or 31.
  • Gregorian days in year / months in year, 30.416 or 30.5
  • Julian days in year / months in year, 30.4375

There isn't really a foolproof solution for converting days to months or days to years or back. As there are different definitions for years and months, it is impossible to find a context free solution.

There are libraries like Twix.js that solve this by using a start and end date rather than a unattached duration.

It's not that moment is against being precise and correct. It's that sometimes there are questions that are ambiguous enough to be interpreted many ways, and there is no way to account for all the ambiguities.

We try to find the solution that is most likely answering the intended question.

@mattjohnsonpint
Copy link
Contributor

The other libraries I mentioned (Noda Time, Joda Time) take the approach of preventing you from asking those questions in the first place. You can't ask "how many days in year" - you have to ask "how many days in 2013 in the Gregorian calendar".

The same goes for any other question you might ask that has ambiguities. You either have to be precise in your question, or you can't ask it.

Hence why I think that there may be room for another library. It doesn't feel right to force moment down that path, but often that is exactly the path you should be on. If you're forced to think of these things up front, then they can't come back to bite you down the road.

@smhg
Copy link

smhg commented Sep 4, 2013

While looking up some background to this, I have to correct myself: an issue I bumped into earlier seems to work as expected in 2.1.

But the last part of this discussion is not about choosing between approximations and limitations (no conversions when approximation would be the only answer). Both are needed.

I'll get back to this when I have more time.

@icambron
Copy link
Member

@mj1856

You either have to be precise in your question, or you can't ask it.

I don't like that. The goal is to be as useful as possible, and often imprecise but simple-to-get answers are really useful. The developer has to know whether this is a question that's even possible to answer with precision. That it's a sharp tool that can cut you is...the nature of things.

That precise library sounds useful, and I think the right way to do it would be to make it easy to interop with moment, like toMoment() and fromMoment().

@icambron
Copy link
Member

@smhg Not sure what you've done so far on your plugin, but please consider contributing to Twix.js

@icambron
Copy link
Member

Because the year/day thing now works, I'm closing this out, though this is a great discussion to reference.

> moment.duration({years: 1}).asDays()
365

@smhg
Copy link

smhg commented Sep 15, 2013

(for the sake of simplicity this response is within the context of the Gregorian calendar)

How can anyone expect

moment.duration({years: 1}).asDays();

to return something meaningful?

moment.duration({years: 1}) (P1Y in ISO 8601) is a well defined entity.
Of course you can ask questions about it. But not just anything without providing more context.
If you want to know the number of days in a year, you need to specify which year. Otherwise the question can't be answered.
That is the way time works.

Of course anyone is free to define a year always lasts 365 days. Who am I to judge this.
The only consequence is that you create a new calendar system.
Again, this is how time works. There is no I don't like that to this.

But because moment is so popular, creating a new calendar system is a though decision. The fact that it is not called a "date calculation" library basically covers this, but the many issue-reports related to this show many don't realize (including myself). Many more probably just didn't bump into it (yet).

Personally, I would really love moment to do date calculation 100% accurate. Since it has all these useful methods like startOf, add and subtract which allow it to be so incredibly good at it. In that case non-standard questions like the one above throw an error by default, but can of course be handled in plugins:

moment.duration(1, 'year').someConvertPluginMethod('day');
/*
  someConvertPluginMethod probably needs to allow the definition of conversion rules
  (with a default set to 365 days for instance)
*/

Writing this, I really wonder in which (correct) circumstances this is used?

Anyhow, this last paragraph sounds like a perfect direction for 3.0.0 to me.

@icambron
Copy link
Member

You're assuming that the answers have to be 100% accurate to useful. Perhaps for you, it's not. If the context-free "how long is a year in days?" isn't a meaningful question to your application, it seems silly you'd ask exactly that question of an API. You seem to agree...

But declaring the question verboten for people who do find it useful is a different story. Ask someone on the street how long a year is in days, you'll get "about 365". That's a really useful approximation, and hoards of people use it every day without thinking of themselves as creating a new calendaring system.

The basic clash here, I think, is whether having an approximation that isn't perfectly accurate is more or less useful than not having it at all. That's a reasonable question about which there can be plenty of debate, and I look forward to that debate as moment continues forward. But casting your side of it as The Way Time Works doesn't add much to the discussion.

@mattjohnsonpint
Copy link
Contributor

I agree with parts of both sides of this argument, and I'm very happy that we can have these sort of debates without some of the kneejerk reactions or derogatory arguments that commonly occur in other parts of the interwebs. However, I wish we had a discussion forum so it didn't linger in issue comments, but anyway...

Yes, it is common for people to say there are "365 days in a year" and that's often good enough. The best places for that sort of thing are when displaying text meant to be ready by a human being. In that regard, moment is mostly doing the right thing, and where it's not we can address those issues individually.

But there are many situations where an application's business logic requires algorithmic manipulation of date/time values. In those situations, it's much more necessary to be precise. If the function that returns number of days in a year requires the year to be passed as a parameter, then it can be precise in it's response.

If moment was only ever used in the browser, I'd say that there's no direct need for anything else. There are some pretty decent server-side date/time libraries for various languages already. But moment is also used on the server with node.js, and there aren't a whole lot of other options out there.

Putting it another way, if someone told me today that they wanted to write an employee scheduling system, or an astrology program, or any other app with date/time complexities, I wouldn't recommend they write it in pure JavaScript because it's just not reliable. Besides the calculation issues, you have the ES5 issue (#831), and the general problems that come with the Date class's behavioral dependency on the local time zone.

I think Moment has done a great job of adding missing features to Date, and certainly excels at parsing, formatting, and localization perspective. In those contexts where approximations are useful and desired, I have no argument with Moment's approach.

But I certainly think there is room for a date/time library that doesn't depend on the Date class, and provides a more rigid API for precise calculations. I certainly am going to work on this, and will provide more details when I'm ready to share.

Regarding the "that's how time works" argument, I'd say it's more appropriate to say that there is a disconnect between how the technical details of the ISO-8601/Gregorian calendar system actually works, and the way that the average human being typically describes their perception of that calendar. I think this is best illustrated by a certain movie involving a time traveling DeLorean, with this instrument console:

tdu-bttf1

Knowing the technical details, you would think a time machine would need to set the time in UTC. Even if it was smart enough to use its GPS coordinates to resolve the machine's local time zone, it would have to have some way to resolve DST ambiguities in its input. Had this panel shown October 27 1985 (the next day), it could have been one of two possible moments in time. But try explaining that to a movie audience without boring them to tears!

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

No branches or pull requests

6 participants