]> git.ipfire.org Git - thirdparty/moment.git/commitdiff
Instituted bubbling for Durations
authorRocky Meza <rocky@fusionbox.com>
Sun, 15 Apr 2012 08:41:21 +0000 (02:41 -0600)
committerRocky Meza <rocky@fusionbox.com>
Sun, 15 Apr 2012 08:41:21 +0000 (02:41 -0600)
This is according to the discussion in #265.

moment.js
test/moment/duration.js

index ea468625f5f96d9ac8382269997ea01c6589afdc..1be02ecbecb9ab52a4abbf5b5d114a1c8e9ccecc 100644 (file)
--- a/moment.js
+++ b/moment.js
@@ -28,7 +28,7 @@
         timezoneParseRegex = /([\+\-]|\d\d)/gi,
         VERSION = "1.5.0",
         shortcuts = 'Month|Date|Hours|Minutes|Seconds|Milliseconds'.split('|'),
-        durationGetters = 'years|months|weeks|days|hours|minutes|seconds|milliseconds'.split('|');
+        durationGetters = 'years|months|days|hours|minutes|seconds|milliseconds'.split('|');
 
     // Moment prototype object
     function Moment(date, isUTC) {
         this._isUTC = !!isUTC;
     }
 
+    function absRound(number) {
+        if (number < 0) {
+            return Math.ceil(number);
+        } else {
+            return Math.floor(number);
+        }
+    }
+
     // Duration Constructor
     function Duration(duration) {
-        var data = this._data = {};
-        data.years = duration.years || duration.y || 0;
-        data.months = duration.months || duration.M || 0;
-        data.weeks = duration.weeks || duration.w || 0;
-        data.days = duration.days || duration.d || 0;
-        data.hours = duration.hours || duration.h || 0;
-        data.minutes = duration.minutes || duration.m || 0;
-        data.seconds = duration.seconds || duration.s || 0;
-        data.milliseconds = duration.milliseconds || duration.ms || 0;
+        var data = this._data = {},
+            years = duration.years || duration.y || 0,
+            months = duration.months || duration.M || 0, 
+            weeks = duration.weeks || duration.w || 0,
+            days = duration.days || duration.d || 0,
+            hours = duration.hours || duration.h || 0,
+            minutes = duration.minutes || duration.m || 0,
+            seconds = duration.seconds || duration.s || 0,
+            milliseconds = duration.milliseconds || duration.ms || 0;
+
+        // representation for dateAddRemove
+        this._milliseconds = milliseconds +
+            seconds * 1e3 + // 1000
+            minutes * 6e4 + // 1000 * 60
+            hours * 36e5; // 1000 * 60 * 60
+        // Because of dateAddRemove treats 24 hours as different from a
+        // day when working around DST, we need to store them separately
+        this._days = days +
+            weeks * 7;
+        // It is impossible translate months into days without knowing
+        // which months you are are talking about, so we have to store
+        // it separately.
+        this._months = months +
+            years * 12;
+            
+        // The following code bubbles up values, see the tests for
+        // examples of what that means.
+        data.milliseconds = milliseconds % 1000;
+        seconds += absRound(milliseconds / 1000);
+
+        data.seconds = seconds % 60;
+        minutes += absRound(seconds / 60);
+
+        data.minutes = minutes % 60;
+        hours += absRound(minutes / 60);
+
+        data.hours = hours % 24;
+        days += absRound(hours / 24);
+
+        days += weeks * 7;
+        data.days = days % 30;
+        
+        months += absRound(days / 30);
+
+        data.months = months % 12;
+        years += absRound(months / 12);
+
+        data.years = years;
     }
 
     // left zero fill a number
             input = moment.duration(_input);
         }
 
-        ms = input.milliseconds() +
-            (input.seconds()) * 1e3 + // 1000
-            (input.minutes()) * 6e4 + // 1000 * 60
-            (input.hours()) * 36e5; // 1000 * 60 * 60
-        d = (input.days()) +
-            (input.weeks()) * 7;
-        M = (input.months()) +
-            (input.years()) * 12;
+        ms = input._milliseconds;
+        d = input._days;
+        M = input._months;
+
         if (ms) {
             date.setTime(+date + ms * adding);
         }
     makeShortcut('year', 'FullYear');
 
     moment.duration.fn = Duration.prototype = {
+        weeks : function () {
+            return absRound(this.days() / 7);
+        },
+
         valueOf : function () {
-            return this._data.milliseconds + 
-                (this._data.seconds * 1000) + // 1000
-                (this._data.minutes * 60000) + // 60 * 1000
-                (this._data.hours   * 3600000) + // 60 * 60 * 1000
-                (this._data.days    * 86400000) + // 24 * 60 * 60 * 1000
-                (this._data.weeks   * 604800000) + // 7 * 24 * 60 * 60 * 1000
-                (this._data.months  * 2592000000) + // 30 * 24 * 60 * 60 * 1000
-                (this._data.years   * 31536000000); // 365 * 24 * 60 * 60 * 1000
+            return this._milliseconds +
+              this._days * 864e5 +
+              this._months * 2592e6;
         },
 
         humanize : function (withSuffix) {
index cd5aa40e55d420fc90bd33fee2913a13403ea3b5..46971280b095ef09deed8d5b111ba4f27d041085 100644 (file)
@@ -5,7 +5,7 @@ exports.duration = {
         var d = moment.duration({
             years: 2,
             months: 3,
-            weeks: 4,
+            weeks: 2,
             days: 1,
             hours: 8,
             minutes: 9,
@@ -16,8 +16,8 @@ exports.duration = {
         test.expect(8);
         test.equal(d.years(), 2, "years");
         test.equal(d.months(), 3, "months");
-        test.equal(d.weeks(), 4, "weeks");
-        test.equal(d.days(), 1, "days");
+        test.equal(d.weeks(), 2, "weeks");
+        test.equal(d.days(), 15, "days"); // two weeks + 1 day
         test.equal(d.hours(), 8, "hours");
         test.equal(d.minutes(), 9, "minutes");
         test.equal(d.seconds(), 20, "seconds");
@@ -130,6 +130,54 @@ exports.duration = {
         test.done();
     },
 
+    "bubble value up" : function(test) {
+        test.expect(5);
+        test.equal(moment.duration({milliseconds: 61001}).milliseconds(), 1, "61001 milliseconds has 1 millisecond left over");
+        test.equal(moment.duration({milliseconds: 61001}).seconds(),      1, "61001 milliseconds has 1 second left over");
+        test.equal(moment.duration({milliseconds: 61001}).minutes(),      1, "61001 milliseconds has 1 minute left over");
+
+        test.equal(moment.duration({minutes: 350}).minutes(), 50, "350 minutes has 50 minutes left over");
+        test.equal(moment.duration({minutes: 350}).hours(),   5,  "350 minutes has 5 hours left over");
+        test.done();
+    },
+
+    "clipping" : function(test) {
+        test.expect(18);
+        test.equal(moment.duration({months: 11}).months(), 11, "11 months is 11 months");
+        test.equal(moment.duration({months: 11}).years(),  0,  "11 months makes no year");
+        test.equal(moment.duration({months: 12}).months(), 0,  "12 months is 0 months left over");
+        test.equal(moment.duration({months: 12}).years(),  1,  "12 months makes 1 year");
+        test.equal(moment.duration({months: 13}).months(), 1,  "13 months is 1 month left over");
+        test.equal(moment.duration({months: 13}).years(),  1,  "13 months makes 1 year");
+
+        test.equal(moment.duration({days: 29}).days(),   29, "29 days is 29 days");
+        test.equal(moment.duration({days: 29}).months(), 0,  "29 days makes no month");
+        test.equal(moment.duration({days: 30}).days(),   0,  "30 days is 0 days left over");
+        test.equal(moment.duration({days: 30}).months(), 1,  "30 days is a month");
+        test.equal(moment.duration({days: 31}).days(),   1,  "31 days is 1 day left over");
+        test.equal(moment.duration({days: 31}).months(), 1,  "31 days is a month");
+
+        test.equal(moment.duration({hours: 23}).hours(), 23, "23 hours is 23 hours");
+        test.equal(moment.duration({hours: 23}).days(),  0,  "23 hours makes no day");
+        test.equal(moment.duration({hours: 24}).hours(), 0,  "24 hours is 0 hours left over");
+        test.equal(moment.duration({hours: 24}).days(),  1,  "24 hours makes 1 day");
+        test.equal(moment.duration({hours: 25}).hours(), 1,  "25 hours is 1 hour left over");
+        test.equal(moment.duration({hours: 25}).days(),  1,  "25 hours makes 1 day");
+        test.done();
+    },
+
+    "effective equivalency" : function(test) {
+        test.expect(7);
+        test.deepEqual(moment.duration({seconds: 1})._data,  moment.duration({milliseconds: 1000})._data, "1 second is the same as 1000 milliseconds");
+        test.deepEqual(moment.duration({seconds: 60})._data, moment.duration({minutes: 1})._data,         "1 minute is the same as 60 seconds");
+        test.deepEqual(moment.duration({minutes: 60})._data, moment.duration({hours: 1})._data,           "1 hour is the same as 60 minutes");
+        test.deepEqual(moment.duration({hours: 24})._data,   moment.duration({days: 1})._data,            "1 day is the same as 24 hours");
+        test.deepEqual(moment.duration({days: 7})._data,     moment.duration({weeks: 1})._data,           "1 week is the same as 7 days");
+        test.deepEqual(moment.duration({days: 30})._data,    moment.duration({months: 1})._data,          "1 month is the same as 30 days");
+        test.deepEqual(moment.duration({months: 12})._data,  moment.duration({years: 1})._data,           "1 years is the same as 12 months");
+        test.done();
+    },
+
     "isDuration" : function(test) {
         test.expect(3);
         test.ok(moment.isDuration(moment.duration(12345678)), "correctly says true");