Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,14 @@ var end = function (durationInput, startDate) {
// Create two equal timestamps, add duration to 'then' and return time difference
var timestamp = startDate.getTime();
var then = new Date(timestamp);
// Add years/months on the 1st, then clamp the day to the resulting month so
// that e.g. Jan 31 + P1M is Feb 28, not an overflow into March
var dayOfMonth = then.getDate();
then.setDate(1);
then.setFullYear(then.getFullYear() + duration.years);
then.setMonth(then.getMonth() + duration.months);
then.setDate(then.getDate() + duration.days);
var daysInMonth = new Date(then.getFullYear(), then.getMonth() + 1, 0).getDate();
then.setDate(Math.min(dayOfMonth, daysInMonth) + duration.days);
// set time as milliseconds to get fractions working for minutes/hours
var hoursInMs = duration.hours * 3600 * 1000;
var minutesInMs = duration.minutes * 60 * 1000;
Expand Down
11 changes: 10 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,18 @@ export const end = (
const timestamp = startDate.getTime();
const then = new Date(timestamp);

// Add years/months on the 1st, then clamp the day to the resulting month so
// that e.g. Jan 31 + P1M is Feb 28, not an overflow into March
const dayOfMonth = then.getDate();
then.setDate(1);
then.setFullYear(then.getFullYear() + duration.years);
then.setMonth(then.getMonth() + duration.months);
then.setDate(then.getDate() + duration.days);
const daysInMonth = new Date(
then.getFullYear(),
then.getMonth() + 1,
0,
).getDate();
then.setDate(Math.min(dayOfMonth, daysInMonth) + duration.days);
// set time as milliseconds to get fractions working for minutes/hours
const hoursInMs = duration.hours * 3600 * 1000;
const minutesInMs = duration.minutes * 60 * 1000;
Expand Down
16 changes: 16 additions & 0 deletions test/iso8601-tests.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,22 @@ test("toSeconds: with supplied start date", () => {
assert.equal(durFromFeb, expectedFebDuration);
});

[
// Jan 31 + 1 month must clamp to Feb 28, not overflow into March
["P1M", new Date(2021, 0, 31), new Date(2021, 1, 28)],
// Feb 29 + 1 year must clamp to Feb 28 in a non-leap year
["P1Y", new Date(2020, 1, 29), new Date(2021, 1, 28)],
// overflow is clamped before days are added: Feb 28 + 1 day = Mar 1
["P1M1D", new Date(2021, 0, 31), new Date(2021, 2, 1)],
].forEach(([value, start, expectedEnd]) => {
test(`toSeconds: clamps calendar overflow (${value})`, () => {
assert.equal(
toSeconds(parse(value), start),
(expectedEnd.getTime() - start.getTime()) / 1000,
);
});
});

test("usage example test", () => {
// Arrange
const jsonString = JSON.stringify({
Expand Down