diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b15a6c2..ec0e28d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -20,7 +20,6 @@ jobs: cache: 'yarn' - run: yarn --frozen-lockfile - run: yarn test - - run: yarn lint - run: yarn build - run: npm publish env: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3175b1e..82ed55a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,4 +17,3 @@ jobs: cache: yarn - run: yarn --frozen-lockfile - run: yarn test - - run: yarn lint diff --git a/package.json b/package.json index 5cd05e0..53f4117 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,6 @@ }, "scripts": { "test": "TZ=America/Los_Angeles mocha 'test/**/*-test.js' && eslint src test", - "lint": "eslint src test", "build": "rm -rf dist && rollup -c" }, "engines": { diff --git a/src/day.js b/src/day.js index 2b5d4b6..2574f8a 100644 --- a/src/day.js +++ b/src/day.js @@ -1,35 +1,20 @@ import {timeInterval} from "./interval.js"; import {durationDay, durationMinute} from "./duration.js"; +import {timeEpoch} from "./epoch.js"; export const timeDay = timeInterval( - date => date.setHours(0, 0, 0, 0), + (date) => date.setHours(0, 0, 0, 0), (date, step) => date.setDate(date.getDate() + step), (start, end) => (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationDay, - date => date.getDate() - 1 + timeEpoch ); export const timeDays = timeDay.range; -export const utcDay = timeInterval((date) => { - date.setUTCHours(0, 0, 0, 0); -}, (date, step) => { - date.setUTCDate(date.getUTCDate() + step); -}, (start, end) => { - return (end - start) / durationDay; -}, (date) => { - return date.getUTCDate() - 1; -}); +export const utcDay = timeInterval( + (date) => date.setUTCHours(0, 0, 0, 0), + (date, step) => date.setUTCDate(date.getUTCDate() + step), + (start, end) => (end - start) / durationDay +); export const utcDays = utcDay.range; - -export const unixDay = timeInterval((date) => { - date.setUTCHours(0, 0, 0, 0); -}, (date, step) => { - date.setUTCDate(date.getUTCDate() + step); -}, (start, end) => { - return (end - start) / durationDay; -}, (date) => { - return Math.floor(date / durationDay); -}); - -export const unixDays = unixDay.range; diff --git a/src/epoch.js b/src/epoch.js new file mode 100644 index 0000000..fc6fa0a --- /dev/null +++ b/src/epoch.js @@ -0,0 +1 @@ +export const timeEpoch = new Date(1970, 0, 1); diff --git a/src/hour.js b/src/hour.js index 77b77a3..f7f8c38 100644 --- a/src/hour.js +++ b/src/hour.js @@ -1,26 +1,20 @@ import {timeInterval} from "./interval.js"; import {durationHour, durationMinute, durationSecond} from "./duration.js"; +import {timeEpoch} from "./epoch.js"; -export const timeHour = timeInterval((date) => { - date.setTime(date - date.getMilliseconds() - date.getSeconds() * durationSecond - date.getMinutes() * durationMinute); -}, (date, step) => { - date.setTime(+date + step * durationHour); -}, (start, end) => { - return (end - start) / durationHour; -}, (date) => { - return date.getHours(); -}); +export const timeHour = timeInterval( + (date) => date.setTime(date - date.getMilliseconds() - date.getSeconds() * durationSecond - date.getMinutes() * durationMinute), + (date, step) => date.setTime(+date + step * durationHour), + (start, end) => (end - start) / durationHour, + timeEpoch +); export const timeHours = timeHour.range; -export const utcHour = timeInterval((date) => { - date.setUTCMinutes(0, 0, 0); -}, (date, step) => { - date.setTime(+date + step * durationHour); -}, (start, end) => { - return (end - start) / durationHour; -}, (date) => { - return date.getUTCHours(); -}); +export const utcHour = timeInterval( + (date) => date.setUTCMinutes(0, 0, 0), + (date, step) => date.setTime(+date + step * durationHour), + (start, end) => (end - start) / durationHour +); export const utcHours = utcHour.range; diff --git a/src/index.js b/src/index.js index d6dfd90..cc277b5 100644 --- a/src/index.js +++ b/src/index.js @@ -35,8 +35,8 @@ export { timeDays, utcDay, utcDays, - unixDay, - unixDays + utcDay as unixDay, // deprecated! use utcDay + utcDays as unixDays // deprecated! use utcDays } from "./day.js"; export { diff --git a/src/interval.js b/src/interval.js index 492ae90..2783c92 100644 --- a/src/interval.js +++ b/src/interval.js @@ -1,6 +1,6 @@ const t0 = new Date, t1 = new Date; -export function timeInterval(floori, offseti, count, field) { +export function timeInterval(floori, offseti, count, epoch = 0) { function interval(date) { return floori(date = arguments.length === 0 ? new Date : new Date(+date)), date; @@ -59,9 +59,9 @@ export function timeInterval(floori, offseti, count, field) { step = Math.floor(step); return !isFinite(step) || !(step > 0) ? null : !(step > 1) ? interval - : interval.filter(field - ? (d) => field(d) % step === 0 - : (d) => interval.count(0, d) % step === 0); + : interval.filter(typeof epoch === "function" // deprecated field + ? (d) => epoch(d) % step === 0 + : (d) => interval.count(epoch, d) % step === 0); }; } diff --git a/src/minute.js b/src/minute.js index ba8e4d9..b7b5297 100644 --- a/src/minute.js +++ b/src/minute.js @@ -1,26 +1,20 @@ import {timeInterval} from "./interval.js"; import {durationMinute, durationSecond} from "./duration.js"; +import {timeEpoch} from "./epoch.js"; -export const timeMinute = timeInterval((date) => { - date.setTime(date - date.getMilliseconds() - date.getSeconds() * durationSecond); -}, (date, step) => { - date.setTime(+date + step * durationMinute); -}, (start, end) => { - return (end - start) / durationMinute; -}, (date) => { - return date.getMinutes(); -}); +export const timeMinute = timeInterval( + (date) => date.setTime(date - date.getMilliseconds() - date.getSeconds() * durationSecond), + (date, step) => date.setTime(+date + step * durationMinute), + (start, end) => (end - start) / durationMinute, + timeEpoch +); export const timeMinutes = timeMinute.range; -export const utcMinute = timeInterval((date) => { - date.setUTCSeconds(0, 0); -}, (date, step) => { - date.setTime(+date + step * durationMinute); -}, (start, end) => { - return (end - start) / durationMinute; -}, (date) => { - return date.getUTCMinutes(); -}); +export const utcMinute = timeInterval( + (date) => date.setUTCSeconds(0, 0), + (date, step) => date.setTime(+date + step * durationMinute), + (start, end) => (end - start) / durationMinute +); export const utcMinutes = utcMinute.range; diff --git a/src/month.js b/src/month.js index 6dab69c..cbc37be 100644 --- a/src/month.js +++ b/src/month.js @@ -1,27 +1,19 @@ +import {timeEpoch} from "./epoch.js"; import {timeInterval} from "./interval.js"; -export const timeMonth = timeInterval((date) => { - date.setDate(1); - date.setHours(0, 0, 0, 0); -}, (date, step) => { - date.setMonth(date.getMonth() + step); -}, (start, end) => { - return end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12; -}, (date) => { - return date.getMonth(); -}); +export const timeMonth = timeInterval( + (date) => (date.setDate(1), date.setHours(0, 0, 0, 0)), + (date, step) => date.setMonth(date.getMonth() + step), + (start, end) => end.getMonth() - start.getMonth() + (end.getFullYear() - start.getFullYear()) * 12, + timeEpoch +); export const timeMonths = timeMonth.range; -export const utcMonth = timeInterval((date) => { - date.setUTCDate(1); - date.setUTCHours(0, 0, 0, 0); -}, (date, step) => { - date.setUTCMonth(date.getUTCMonth() + step); -}, (start, end) => { - return end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12; -}, (date) => { - return date.getUTCMonth(); -}); +export const utcMonth = timeInterval( + (date) => (date.setUTCDate(1), date.setUTCHours(0, 0, 0, 0)), + (date, step) => date.setUTCMonth(date.getUTCMonth() + step), + (start, end) => end.getUTCMonth() - start.getUTCMonth() + (end.getUTCFullYear() - start.getUTCFullYear()) * 12 +); export const utcMonths = utcMonth.range; diff --git a/src/second.js b/src/second.js index 47b2f43..a3fa04a 100644 --- a/src/second.js +++ b/src/second.js @@ -1,14 +1,10 @@ import {timeInterval} from "./interval.js"; import {durationSecond} from "./duration.js"; -export const second = timeInterval((date) => { - date.setTime(date - date.getMilliseconds()); -}, (date, step) => { - date.setTime(+date + step * durationSecond); -}, (start, end) => { - return (end - start) / durationSecond; -}, (date) => { - return date.getUTCSeconds(); -}); +export const second = timeInterval( + (date) => date.setTime(date - date.getMilliseconds()), + (date, step) => date.setTime(+date + step * durationSecond), + (start, end) => (end - start) / durationSecond +); export const seconds = second.range; diff --git a/src/ticks.js b/src/ticks.js index c314789..b18261f 100644 --- a/src/ticks.js +++ b/src/ticks.js @@ -4,7 +4,7 @@ import {millisecond} from "./millisecond.js"; import {second} from "./second.js"; import {timeMinute, utcMinute} from "./minute.js"; import {timeHour, utcHour} from "./hour.js"; -import {timeDay, unixDay} from "./day.js"; +import {timeDay, utcDay} from "./day.js"; import {timeSunday, utcSunday} from "./week.js"; import {timeMonth, utcMonth} from "./month.js"; import {timeYear, utcYear} from "./year.js"; @@ -52,7 +52,7 @@ function ticker(year, month, week, day, hour, minute) { return [ticks, tickInterval]; } -const [utcTicks, utcTickInterval] = ticker(utcYear, utcMonth, utcSunday, unixDay, utcHour, utcMinute); +const [utcTicks, utcTickInterval] = ticker(utcYear, utcMonth, utcSunday, utcDay, utcHour, utcMinute); const [timeTicks, timeTickInterval] = ticker(timeYear, timeMonth, timeSunday, timeDay, timeHour, timeMinute); export {utcTicks, utcTickInterval, timeTicks, timeTickInterval}; diff --git a/src/week.js b/src/week.js index f75ff52..b535b79 100644 --- a/src/week.js +++ b/src/week.js @@ -1,15 +1,14 @@ import {timeInterval} from "./interval.js"; import {durationMinute, durationWeek} from "./duration.js"; +import {timeEpoch} from "./epoch.js"; function timeWeekday(i) { - return timeInterval((date) => { - date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7); - date.setHours(0, 0, 0, 0); - }, (date, step) => { - date.setDate(date.getDate() + step * 7); - }, (start, end) => { - return (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationWeek; - }); + return timeInterval( + (date) => (date.setDate(date.getDate() - (date.getDay() + 7 - i) % 7), date.setHours(0, 0, 0, 0)), + (date, step) => date.setDate(date.getDate() + step * 7), + (start, end) => (end - start - (end.getTimezoneOffset() - start.getTimezoneOffset()) * durationMinute) / durationWeek, + timeEpoch + ); } export const timeSunday = timeWeekday(0); @@ -29,14 +28,11 @@ export const timeFridays = timeFriday.range; export const timeSaturdays = timeSaturday.range; function utcWeekday(i) { - return timeInterval((date) => { - date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7); - date.setUTCHours(0, 0, 0, 0); - }, (date, step) => { - date.setUTCDate(date.getUTCDate() + step * 7); - }, (start, end) => { - return (end - start) / durationWeek; - }); + return timeInterval( + (date) => (date.setUTCDate(date.getUTCDate() - (date.getUTCDay() + 7 - i) % 7), date.setUTCHours(0, 0, 0, 0)), + (date, step) => date.setUTCDate(date.getUTCDate() + step * 7), + (start, end) => (end - start) / durationWeek + ); } export const utcSunday = utcWeekday(0); diff --git a/src/year.js b/src/year.js index 9eadff3..2d63587 100644 --- a/src/year.js +++ b/src/year.js @@ -1,49 +1,35 @@ +import {timeEpoch} from "./epoch.js"; import {timeInterval} from "./interval.js"; -export const timeYear = timeInterval((date) => { - date.setMonth(0, 1); - date.setHours(0, 0, 0, 0); -}, (date, step) => { - date.setFullYear(date.getFullYear() + step); -}, (start, end) => { - return end.getFullYear() - start.getFullYear(); -}, (date) => { - return date.getFullYear(); -}); +export const timeYear = timeInterval( + (date) => (date.setMonth(0, 1), date.setHours(0, 0, 0, 0)), + (date, step) => date.setFullYear(date.getFullYear() + step), + (start, end) => end.getFullYear() - start.getFullYear(), + timeEpoch +); // An optimized implementation for this simple case. timeYear.every = (k) => { - return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : timeInterval((date) => { - date.setFullYear(Math.floor(date.getFullYear() / k) * k); - date.setMonth(0, 1); - date.setHours(0, 0, 0, 0); - }, (date, step) => { - date.setFullYear(date.getFullYear() + step * k); - }); + return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : timeInterval( + (date) => (date.setFullYear(Math.floor(date.getFullYear() / k) * k), date.setMonth(0, 1), date.setHours(0, 0, 0, 0)), + (date, step) => date.setFullYear(date.getFullYear() + step * k) + ); }; export const timeYears = timeYear.range; -export const utcYear = timeInterval((date) => { - date.setUTCMonth(0, 1); - date.setUTCHours(0, 0, 0, 0); -}, (date, step) => { - date.setUTCFullYear(date.getUTCFullYear() + step); -}, (start, end) => { - return end.getUTCFullYear() - start.getUTCFullYear(); -}, (date) => { - return date.getUTCFullYear(); -}); +export const utcYear = timeInterval( + (date) => (date.setUTCMonth(0, 1), date.setUTCHours(0, 0, 0, 0)), + (date, step) => date.setUTCFullYear(date.getUTCFullYear() + step), + (start, end) => end.getUTCFullYear() - start.getUTCFullYear() +); // An optimized implementation for this simple case. utcYear.every = (k) => { - return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : timeInterval((date) => { - date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k); - date.setUTCMonth(0, 1); - date.setUTCHours(0, 0, 0, 0); - }, (date, step) => { - date.setUTCFullYear(date.getUTCFullYear() + step * k); - }); + return !isFinite(k = Math.floor(k)) || !(k > 0) ? null : timeInterval( + (date) => (date.setUTCFullYear(Math.floor(date.getUTCFullYear() / k) * k), date.setUTCMonth(0, 1), date.setUTCHours(0, 0, 0, 0)), + (date, step) => date.setUTCFullYear(date.getUTCFullYear() + step * k) + ); }; export const utcYears = utcYear.range; diff --git a/test/day-test.js b/test/day-test.js index 771616b..0ac7bb5 100644 --- a/test/day-test.js +++ b/test/day-test.js @@ -208,8 +208,8 @@ it("timeDay.count(start, end) returns 364 or 365 for a full year", () => { assert.strictEqual(timeDay.count(local(2011, 0, 1), local(2011, 11, 31)), 364); }); -it("timeDay.every(step) returns every stepth day, starting with the first day of the month", () => { - assert.deepStrictEqual(timeDay.every(3).range(local(2008, 11, 30, 0, 12), local(2009, 0, 5, 23, 48)), [local(2008, 11, 31), local(2009, 0, 1), local(2009, 0, 4)]); - assert.deepStrictEqual(timeDay.every(5).range(local(2008, 11, 30, 0, 12), local(2009, 0, 6, 23, 48)), [local(2008, 11, 31), local(2009, 0, 1), local(2009, 0, 6)]); - assert.deepStrictEqual(timeDay.every(7).range(local(2008, 11, 30, 0, 12), local(2009, 0, 8, 23, 48)), [local(2009, 0, 1), local(2009, 0, 8)]); +it("timeDay.every(step) returns every stepth day without resetting on the first of the month", () => { + assert.deepStrictEqual(timeDay.every(3).range(local(2008, 11, 30, 0, 12), local(2009, 0, 5, 23, 48)), [local(2008, 11, 31), local(2009, 0, 3)]); + assert.deepStrictEqual(timeDay.every(5).range(local(2008, 11, 25, 0, 12), local(2009, 0, 6, 23, 48)), [local(2008, 11, 27), local(2009, 0, 1), local(2009, 0, 6)]); + assert.deepStrictEqual(timeDay.every(7).range(local(2008, 11, 23, 0, 12), local(2009, 0, 8, 23, 48)), [local(2008, 11, 25), local(2009, 0, 1), local(2009, 0, 8)]); }); diff --git a/test/ticks-test.js b/test/ticks-test.js index 36c0318..0c03aa8 100644 --- a/test/ticks-test.js +++ b/test/ticks-test.js @@ -136,6 +136,16 @@ it("timeTicks(start, stop, count) can generate 6-hour ticks", () => { ]); }); +// Note: not aligned at midnight because of daylight savings time! +it("timeTicks(start, stop, count) can generate 6-hour ticks across daylight savings time", () => { + assert.deepStrictEqual(timeTicks(local(2011, 3, 1, 16, 28, 27), local(2011, 3, 2, 14, 34, 12), 4), [ + local(2011, 3, 1, 19, 0), + local(2011, 3, 2, 1, 0), + local(2011, 3, 2, 7, 0), + local(2011, 3, 2, 13, 0) + ]); +}); + it("timeTicks(start, stop, count) can generate 12-hour ticks", () => { assert.deepStrictEqual(timeTicks(local(2011, 0, 1, 16, 28, 27), local(2011, 0, 3, 21, 34, 12), 4), [ local(2011, 0, 2, 0, 0), @@ -156,10 +166,9 @@ it("timeTicks(start, stop, count) can generate 1-day ticks", () => { it("timeTicks(start, stop, count) can generate 2-day ticks", () => { assert.deepStrictEqual(timeTicks(local(2011, 0, 2, 16, 28, 27), local(2011, 0, 9, 21, 34, 12), 4), [ - local(2011, 0, 3, 0, 0), - local(2011, 0, 5, 0, 0), - local(2011, 0, 7, 0, 0), - local(2011, 0, 9, 0, 0) + local(2011, 0, 4, 0, 0), + local(2011, 0, 6, 0, 0), + local(2011, 0, 8, 0, 0) ]); }); diff --git a/test/utcDay-test.js b/test/utcDay-test.js index f521c91..b6be6e0 100644 --- a/test/utcDay-test.js +++ b/test/utcDay-test.js @@ -110,8 +110,8 @@ it("utcDay.count(start, end) returns 364 or 365 for a full year", () => { assert.strictEqual(utcDay.count(utc(2011, 0, 1), utc(2011, 11, 31)), 364); }); -it("utcDay.every(step) returns every stepth day, starting with the first day of the month", () => { - assert.deepStrictEqual(utcDay.every(3).range(utc(2008, 11, 30, 0, 12), utc(2009, 0, 5, 23, 48)), [utc(2008, 11, 31), utc(2009, 0, 1), utc(2009, 0, 4)]); - assert.deepStrictEqual(utcDay.every(5).range(utc(2008, 11, 30, 0, 12), utc(2009, 0, 6, 23, 48)), [utc(2008, 11, 31), utc(2009, 0, 1), utc(2009, 0, 6)]); - assert.deepStrictEqual(utcDay.every(7).range(utc(2008, 11, 30, 0, 12), utc(2009, 0, 8, 23, 48)), [utc(2009, 0, 1), utc(2009, 0, 8)]); +it("utcDay.every(step) returns every stepth day without resetting on the first of the month", () => { + assert.deepStrictEqual(utcDay.every(3).range(utc(2008, 11, 30, 0, 12), utc(2009, 0, 5, 23, 48)), [utc(2008, 11, 31), utc(2009, 0, 3)]); + assert.deepStrictEqual(utcDay.every(5).range(utc(2008, 11, 25, 0, 12), utc(2009, 0, 6, 23, 48)), [utc(2008, 11, 27), utc(2009, 0, 1), utc(2009, 0, 6)]); + assert.deepStrictEqual(utcDay.every(7).range(utc(2008, 11, 23, 0, 12), utc(2009, 0, 8, 23, 48)), [utc(2008, 11, 25), utc(2009, 0, 1), utc(2009, 0, 8)]); });