From 574dc1a018152e99c2103a31a7b2d108f306c876 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 30 Nov 2022 19:26:11 -0800 Subject: [PATCH 1/2] add unixDay interval --- README.md | 3 +- src/index.js | 43 +++++++++------- src/ticks.js | 4 +- src/unixDay.js | 15 ++++++ test/unixDay-test.js | 117 ++++++++++++++++++++++++++++++++++++++++++ test/utcTicks-test.js | 7 ++- 6 files changed, 163 insertions(+), 26 deletions(-) create mode 100644 src/unixDay.js create mode 100644 test/unixDay-test.js diff --git a/README.md b/README.md index f17811d..e94a02b 100644 --- a/README.md +++ b/README.md @@ -197,8 +197,9 @@ Hours (e.g., 01:00 AM); 60 minutes. Note that advancing time by one hour in loca # d3.timeDay · [Source](https://github.com/d3/d3-time/blob/master/src/day.js "Source")
# d3.utcDay · [Source](https://github.com/d3/d3-time/blob/master/src/utcDay.js "Source") +
# d3.unixDay · [Source](https://github.com/d3/d3-time/blob/master/src/unixDay.js "Source") -Days (e.g., February 7, 2012 at 12:00 AM); typically 24 hours. Days in local time may range from 23 to 25 hours due to daylight saving. +Days (e.g., February 7, 2012 at 12:00 AM); typically 24 hours. Days in local time may range from 23 to 25 hours due to daylight saving. d3.unixDay is like [d3.utcDay](#timeDay), except it counts days since the UNIX epoch (January 1, 1970) such that *interval*.every returns uniformly-spaced dates rather than varying based on day-of-month. # d3.timeWeek · [Source](https://github.com/d3/d3-time/blob/master/src/week.js "Source")
# d3.utcWeek · [Source](https://github.com/d3/d3-time/blob/master/src/utcWeek.js "Source") diff --git a/src/index.js b/src/index.js index c163d63..9ceb157 100644 --- a/src/index.js +++ b/src/index.js @@ -62,46 +62,51 @@ export { export { default as utcMinute, - utcMinutes as utcMinutes + utcMinutes } from "./utcMinute.js"; export { default as utcHour, - utcHours as utcHours + utcHours } from "./utcHour.js"; export { default as utcDay, - utcDays as utcDays + utcDays } from "./utcDay.js"; +export { + default as unixDay, + unixDays +} from "./unixDay.js"; + export { utcSunday as utcWeek, utcSundays as utcWeeks, - utcSunday as utcSunday, - utcSundays as utcSundays, - utcMonday as utcMonday, - utcMondays as utcMondays, - utcTuesday as utcTuesday, - utcTuesdays as utcTuesdays, - utcWednesday as utcWednesday, - utcWednesdays as utcWednesdays, - utcThursday as utcThursday, - utcThursdays as utcThursdays, - utcFriday as utcFriday, - utcFridays as utcFridays, - utcSaturday as utcSaturday, - utcSaturdays as utcSaturdays + utcSunday, + utcSundays, + utcMonday, + utcMondays, + utcTuesday, + utcTuesdays, + utcWednesday, + utcWednesdays, + utcThursday, + utcThursdays, + utcFriday, + utcFridays, + utcSaturday, + utcSaturdays } from "./utcWeek.js"; export { default as utcMonth, - utcMonths as utcMonths + utcMonths } from "./utcMonth.js"; export { default as utcYear, - utcYears as utcYears + utcYears } from "./utcYear.js"; export { diff --git a/src/ticks.js b/src/ticks.js index e221936..ff71c76 100644 --- a/src/ticks.js +++ b/src/ticks.js @@ -10,7 +10,7 @@ import month from "./month.js"; import year from "./year.js"; import utcMinute from "./utcMinute.js"; import utcHour from "./utcHour.js"; -import utcDay from "./utcDay.js"; +import unixDay from "./unixDay.js"; import {utcSunday as utcWeek} from "./utcWeek.js"; import utcMonth from "./utcMonth.js"; import utcYear from "./utcYear.js"; @@ -58,7 +58,7 @@ function ticker(year, month, week, day, hour, minute) { return [ticks, tickInterval]; } -const [utcTicks, utcTickInterval] = ticker(utcYear, utcMonth, utcWeek, utcDay, utcHour, utcMinute); +const [utcTicks, utcTickInterval] = ticker(utcYear, utcMonth, utcWeek, unixDay, utcHour, utcMinute); const [timeTicks, timeTickInterval] = ticker(year, month, week, day, hour, minute); export {utcTicks, utcTickInterval, timeTicks, timeTickInterval}; diff --git a/src/unixDay.js b/src/unixDay.js new file mode 100644 index 0000000..bbb1bea --- /dev/null +++ b/src/unixDay.js @@ -0,0 +1,15 @@ +import interval from "./interval.js"; +import {durationDay} from "./duration.js"; + +var unixDay = interval(function(date) { + date.setUTCHours(0, 0, 0, 0); +}, function(date, step) { + date.setUTCDate(date.getUTCDate() + step); +}, function(start, end) { + return (end - start) / durationDay; +}, function(date) { + return Math.floor(date / durationDay); +}); + +export default unixDay; +export var unixDays = unixDay.range; diff --git a/test/unixDay-test.js b/test/unixDay-test.js new file mode 100644 index 0000000..60a916b --- /dev/null +++ b/test/unixDay-test.js @@ -0,0 +1,117 @@ +import assert from "assert"; +import {unixDay, unixDays} from "../src/index.js"; +import {utc} from "./date.js"; + +it("unixDays in an alias for unixDay.range", () => { + assert.strictEqual(unixDays, unixDay.range); +}); + +it("unixDay.floor(date) returns days", () => { + assert.deepStrictEqual(unixDay.floor(utc(2010, 11, 31, 23)), utc(2010, 11, 31)); + assert.deepStrictEqual(unixDay.floor(utc(2011, 0, 1, 0)), utc(2011, 0, 1)); + assert.deepStrictEqual(unixDay.floor(utc(2011, 0, 1, 1)), utc(2011, 0, 1)); +}); + +it("unixDay.floor(date) does not observe daylight saving", () => { + assert.deepStrictEqual(unixDay.floor(utc(2011, 2, 13, 7)), utc(2011, 2, 13)); + assert.deepStrictEqual(unixDay.floor(utc(2011, 2, 13, 8)), utc(2011, 2, 13)); + assert.deepStrictEqual(unixDay.floor(utc(2011, 2, 13, 9)), utc(2011, 2, 13)); + assert.deepStrictEqual(unixDay.floor(utc(2011, 2, 13, 10)), utc(2011, 2, 13)); + assert.deepStrictEqual(unixDay.floor(utc(2011, 10, 6, 5)), utc(2011, 10, 6)); + assert.deepStrictEqual(unixDay.floor(utc(2011, 10, 6, 6)), utc(2011, 10, 6)); + assert.deepStrictEqual(unixDay.floor(utc(2011, 10, 6, 7)), utc(2011, 10, 6)); + assert.deepStrictEqual(unixDay.floor(utc(2011, 10, 6, 8)), utc(2011, 10, 6)); +}); + +it("unixDay.round(date) returns days", () => { + assert.deepStrictEqual(unixDay.round(utc(2010, 11, 30, 13)), utc(2010, 11, 31)); + assert.deepStrictEqual(unixDay.round(utc(2010, 11, 30, 11)), utc(2010, 11, 30)); +}); + +it("unixDay.ceil(date) returns days", () => { + assert.deepStrictEqual(unixDay.ceil(utc(2010, 11, 30, 23)), utc(2010, 11, 31)); + assert.deepStrictEqual(unixDay.ceil(utc(2010, 11, 31, 0)), utc(2010, 11, 31)); + assert.deepStrictEqual(unixDay.ceil(utc(2010, 11, 31, 1)), utc(2011, 0, 1)); +}); + +it("unixDay.ceil(date) does not observe daylight saving", () => { + assert.deepStrictEqual(unixDay.ceil(utc(2011, 2, 13, 7)), utc(2011, 2, 14)); + assert.deepStrictEqual(unixDay.ceil(utc(2011, 2, 13, 8)), utc(2011, 2, 14)); + assert.deepStrictEqual(unixDay.ceil(utc(2011, 2, 13, 9)), utc(2011, 2, 14)); + assert.deepStrictEqual(unixDay.ceil(utc(2011, 2, 13, 10)), utc(2011, 2, 14)); + assert.deepStrictEqual(unixDay.ceil(utc(2011, 10, 6, 5)), utc(2011, 10, 7)); + assert.deepStrictEqual(unixDay.ceil(utc(2011, 10, 6, 6)), utc(2011, 10, 7)); + assert.deepStrictEqual(unixDay.ceil(utc(2011, 10, 6, 7)), utc(2011, 10, 7)); + assert.deepStrictEqual(unixDay.ceil(utc(2011, 10, 6, 8)), utc(2011, 10, 7)); +}); + +it("unixDay.offset(date) is an alias for unixDay.offset(date, 1)", () => { + assert.deepStrictEqual(unixDay.offset(utc(2010, 11, 31, 23, 59, 59, 999)), utc(2011, 0, 1, 23, 59, 59, 999)); +}); + +it("unixDay.offset(date, step) does not modify the passed-in date", () => { + const d = utc(2010, 11, 31, 23, 59, 59, 999); + unixDay.offset(d, +1); + assert.deepStrictEqual(d, utc(2010, 11, 31, 23, 59, 59, 999)); +}); + +it("unixDay.offset(date, step) does not round the passed-in date", () => { + assert.deepStrictEqual(unixDay.offset(utc(2010, 11, 31, 23, 59, 59, 999), +1), utc(2011, 0, 1, 23, 59, 59, 999)); + assert.deepStrictEqual(unixDay.offset(utc(2010, 11, 31, 23, 59, 59, 456), -2), utc(2010, 11, 29, 23, 59, 59, 456)); +}); + +it("unixDay.offset(date, step) allows step to be negative", () => { + assert.deepStrictEqual(unixDay.offset(utc(2010, 11, 31), -1), utc(2010, 11, 30)); + assert.deepStrictEqual(unixDay.offset(utc(2011, 0, 1), -2), utc(2010, 11, 30)); + assert.deepStrictEqual(unixDay.offset(utc(2011, 0, 1), -1), utc(2010, 11, 31)); +}); + +it("unixDay.offset(date, step) allows step to be positive", () => { + assert.deepStrictEqual(unixDay.offset(utc(2010, 11, 31), +1), utc(2011, 0, 1)); + assert.deepStrictEqual(unixDay.offset(utc(2010, 11, 30), +2), utc(2011, 0, 1)); + assert.deepStrictEqual(unixDay.offset(utc(2010, 11, 30), +1), utc(2010, 11, 31)); +}); + +it("unixDay.offset(date, step) allows step to be zero", () => { + assert.deepStrictEqual(unixDay.offset(utc(2010, 11, 31, 23, 59, 59, 999), 0), utc(2010, 11, 31, 23, 59, 59, 999)); + assert.deepStrictEqual(unixDay.offset(utc(2010, 11, 31, 23, 59, 58, 0), 0), utc(2010, 11, 31, 23, 59, 58, 0)); +}); + +it("unixDay.count(start, end) counts days after start (exclusive) and before end (inclusive)", () => { + assert.strictEqual(unixDay.count(utc(2011, 0, 1, 0), utc(2011, 4, 9, 0)), 128); + assert.strictEqual(unixDay.count(utc(2011, 0, 1, 1), utc(2011, 4, 9, 0)), 128); + assert.strictEqual(unixDay.count(utc(2010, 11, 31, 23), utc(2011, 4, 9, 0)), 129); + assert.strictEqual(unixDay.count(utc(2011, 0, 1, 0), utc(2011, 4, 8, 23)), 127); + assert.strictEqual(unixDay.count(utc(2011, 0, 1, 0), utc(2011, 4, 9, 1)), 128); +}); + +it("unixDay.count(start, end) does not observe daylight saving", () => { + assert.strictEqual(unixDay.count(utc(2011, 0, 1), utc(2011, 2, 13, 1)), 71); + assert.strictEqual(unixDay.count(utc(2011, 0, 1), utc(2011, 2, 13, 3)), 71); + assert.strictEqual(unixDay.count(utc(2011, 0, 1), utc(2011, 2, 13, 4)), 71); + assert.strictEqual(unixDay.count(utc(2011, 0, 1), utc(2011, 10, 6, 0)), 309); + assert.strictEqual(unixDay.count(utc(2011, 0, 1), utc(2011, 10, 6, 1)), 309); + assert.strictEqual(unixDay.count(utc(2011, 0, 1), utc(2011, 10, 6, 2)), 309); +}); + +it("unixDay.count(start, end) returns 364 or 365 for a full year", () => { + assert.strictEqual(unixDay.count(utc(1999, 0, 1), utc(1999, 11, 31)), 364); + assert.strictEqual(unixDay.count(utc(2000, 0, 1), utc(2000, 11, 31)), 365); // leap year + assert.strictEqual(unixDay.count(utc(2001, 0, 1), utc(2001, 11, 31)), 364); + assert.strictEqual(unixDay.count(utc(2002, 0, 1), utc(2002, 11, 31)), 364); + assert.strictEqual(unixDay.count(utc(2003, 0, 1), utc(2003, 11, 31)), 364); + assert.strictEqual(unixDay.count(utc(2004, 0, 1), utc(2004, 11, 31)), 365); // leap year + assert.strictEqual(unixDay.count(utc(2005, 0, 1), utc(2005, 11, 31)), 364); + assert.strictEqual(unixDay.count(utc(2006, 0, 1), utc(2006, 11, 31)), 364); + assert.strictEqual(unixDay.count(utc(2007, 0, 1), utc(2007, 11, 31)), 364); + assert.strictEqual(unixDay.count(utc(2008, 0, 1), utc(2008, 11, 31)), 365); // leap year + assert.strictEqual(unixDay.count(utc(2009, 0, 1), utc(2009, 11, 31)), 364); + assert.strictEqual(unixDay.count(utc(2010, 0, 1), utc(2010, 11, 31)), 364); + assert.strictEqual(unixDay.count(utc(2011, 0, 1), utc(2011, 11, 31)), 364); +}); + +it("unixDay.every(step) returns every stepth day", () => { + assert.deepStrictEqual(unixDay.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(unixDay.every(5).range(utc(2008, 11, 30, 0, 12), utc(2009, 0, 6, 23, 48)), [utc(2009, 0, 1), utc(2009, 0, 6)]); + assert.deepStrictEqual(unixDay.every(7).range(utc(2008, 11, 30, 0, 12), utc(2009, 0, 8, 23, 48)), [utc(2009, 0, 1), utc(2009, 0, 8)]); +}); diff --git a/test/utcTicks-test.js b/test/utcTicks-test.js index 0f05cb9..fc243a3 100644 --- a/test/utcTicks-test.js +++ b/test/utcTicks-test.js @@ -152,10 +152,9 @@ it("utcTicks(start, stop, count) can generate 1-day ticks", () => { it("utcTicks(start, stop, count) can generate 2-day ticks", () => { assert.deepStrictEqual(utcTicks(utc(2011, 0, 2, 16, 28, 27), utc(2011, 0, 9, 21, 34, 12), 4), [ - utc(2011, 0, 3, 0, 0), - utc(2011, 0, 5, 0, 0), - utc(2011, 0, 7, 0, 0), - utc(2011, 0, 9, 0, 0) + utc(2011, 0, 4, 0, 0), + utc(2011, 0, 6, 0, 0), + utc(2011, 0, 8, 0, 0) ]); }); From a65bfac97376113a7fa7055927b5c1a078ca526e Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 30 Nov 2022 19:28:37 -0800 Subject: [PATCH 2/2] Update README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e94a02b..8433235 100644 --- a/README.md +++ b/README.md @@ -277,8 +277,9 @@ Aliases for [d3.timeHour](#timeHour).[range](#interval_range) and [d3.utcHour](# # d3.timeDays(start, stop[, step]) · [Source](https://github.com/d3/d3-time/blob/master/src/day.js)
# d3.utcDays(start, stop[, step]) · [Source](https://github.com/d3/d3-time/blob/master/src/utcDay.js) +
# d3.unixDays(start, stop[, step]) · [Source](https://github.com/d3/d3-time/blob/master/src/unixDay.js) -Aliases for [d3.timeDay](#timeDay).[range](#interval_range) and [d3.utcDay](#timeDay).[range](#interval_range). +Aliases for [d3.timeDay](#timeDay).[range](#interval_range), [d3.utcDay](#timeDay).[range](#interval_range), and [d3.unixDay](#timeDay).[range](#interval_range). # d3.timeWeeks(start, stop[, step])
# d3.utcWeeks(start, stop[, step])