Skip to content

Commit

Permalink
src: workaround moment issue with DST transitions
Browse files Browse the repository at this point in the history
On DST end transition, setting the date to the `startOf` `hour`,
`minute` or `second` after having added 1 `hour`, `minute` or `second`
to the date, would result in the date set to the initial value causing
an infinite loop.

Example:

```js
'use strict';

const moment = require('moment-timezone');

var m = moment(new Date('Sun Oct 30 2016 02:00:00 GMT+0200'));
console.log(m.toString(),
            '<-- Just 1 hour before the DST end @ Europe/Madrid');
m.add(1, 'hours');
console.log(m.toString(),
            '<-- DST end transition @ Europe/Madrid works correctly');
m.startOf('hour');
console.log(m.toString(),
            '<-- Smthing wrong?? It goes back to the previous hour');
```

Fixes: harrisiirak#72
Ref: moment/moment#2749
  • Loading branch information
santigimeno committed Oct 15, 2016
1 parent 6ca9d62 commit 473bb02
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 0 deletions.
12 changes: 12 additions & 0 deletions lib/date.js
Expand Up @@ -15,15 +15,27 @@ CronDate.prototype.addDay = function() {
};

CronDate.prototype.addHour = function() {
const prev = this.getTime();
this._date.add(1, 'hour').startOf('hour');
if (this.getTime() <= prev) {
this._date.add(1, 'hour');
}
};

CronDate.prototype.addMinute = function() {
const prev = this.getTime();
this._date.add(1, 'minute').startOf('minute');
if (this.getTime() < prev) {
this._date.add(1, 'hour');
}
};

CronDate.prototype.addSecond = function() {
const prev = this.getTime();
this._date.add(1, 'second').startOf('second');
if (this.getTime() < prev) {
this._date.add(1, 'hour');
}
};

CronDate.prototype.getDate = function() {
Expand Down
107 changes: 107 additions & 0 deletions test/timezone.js
Expand Up @@ -264,6 +264,113 @@ test('It works on DST end', function(t) {
t.throws(function() {
date = interval.next();
});

options = {
currentDate : new Date('Sun Oct 29 2016 01:00:00 GMT+0200')
}

interval = CronExpression.parse('0 12 * * *', options);
t.ok(interval, 'Interval parsed');

date = interval.next();
t.equal(date.getHours(), 12, '12');
t.equal(date.getDate(), 29, '29th');
date = interval.next();
t.equal(date.getHours(), 12, '12');
t.equal(date.getDate(), 30, '30th');
date = interval.next();
t.equal(date.getHours(), 12, '12');
t.equal(date.getDate(), 31, '31st');

options = {
currentDate : new Date('Sun Oct 29 2016 02:59:00 GMT+0200')
}

interval = CronExpression.parse('0 12 * * *', options);
t.ok(interval, 'Interval parsed');

date = interval.next();
t.equal(date.getHours(), 12, '12');
t.equal(date.getDate(), 29, '29th');
date = interval.next();
t.equal(date.getHours(), 12, '12');
t.equal(date.getDate(), 30, '30th');
date = interval.next();
t.equal(date.getHours(), 12, '12');
t.equal(date.getDate(), 31, '31st');

options = {
currentDate : new Date('Sun Oct 29 2016 02:59:59 GMT+0200')
}

interval = CronExpression.parse('0 12 * * *', options);
t.ok(interval, 'Interval parsed');

date = interval.next();
t.equal(date.getHours(), 12, '12');
t.equal(date.getDate(), 29, '29th');
date = interval.next();
t.equal(date.getHours(), 12, '12');
t.equal(date.getDate(), 30, '30th');
date = interval.next();
t.equal(date.getHours(), 12, '12');
t.equal(date.getDate(), 31, '31st');

options = {
currentDate : new Date('Sun Oct 30 2016 01:00:00 GMT+0200')
}

interval = CronExpression.parse('0 12 * * *', options);
t.ok(interval, 'Interval parsed');

date = interval.next();
t.equal(date.getHours(), 12, '12');
t.equal(date.getDate(), 30, '30th');
date = interval.next();
t.equal(date.getHours(), 12, '12');
t.equal(date.getDate(), 31, '31st');

options = {
currentDate : new Date('Sun Oct 30 2016 01:59:00 GMT+0200')
}

interval = CronExpression.parse('0 12 * * *', options);
t.ok(interval, 'Interval parsed');

date = interval.next();
t.equal(date.getHours(), 12, '12');
t.equal(date.getDate(), 30, '30th');
date = interval.next();
t.equal(date.getHours(), 12, '12');
t.equal(date.getDate(), 31, '31st');

options = {
currentDate : new Date('Sun Oct 30 2016 01:59:59 GMT+0200')
}

interval = CronExpression.parse('0 12 * * *', options);
t.ok(interval, 'Interval parsed');

date = interval.next();
t.equal(date.getHours(), 12, '12');
t.equal(date.getDate(), 30, '30th');
date = interval.next();
t.equal(date.getHours(), 12, '12');
t.equal(date.getDate(), 31, '31st');

options = {
currentDate : new Date('Sun Oct 30 2016 02:59:00 GMT+0200')
}

interval = CronExpression.parse('0 12 * * *', options);
t.ok(interval, 'Interval parsed');

date = interval.next();
t.equal(date.getHours(), 12, '12');
t.equal(date.getDate(), 30, '30th');
date = interval.next();
t.equal(date.getHours(), 12, '12');
t.equal(date.getDate(), 31, '31st');
} catch (err) {
t.ifError(err, 'Interval parse error');
}
Expand Down

0 comments on commit 473bb02

Please sign in to comment.