d3-time
当可视化时间序列数据、分析时间模式或在一般情况下使用时间时,传统时间单位的不规则性很快就会变得明显。例如,在公历中,大多数月份有 31 天,但有些有 28、29 或 30 天;大多数年份有 365 天,但闰年有 366 天;并且随着夏令时的实施,大多数日期有 24 小时,但有些有 23 或 25 小时。更复杂的是,夏令时的规则在世界各地各不相同。
由于这些时间上的特殊性,执行看似微不足道的任务可能会很困难。例如,如果您想计算两个日期之间经过的天数,您不能简单地减去并除以 24 小时 (86,400,000 毫秒)
const start = new Date(2015, 02, 01); // 2015-03-01T00:00
const end = new Date(2015, 03, 01); // 2015-04-01T00:00
const days = (end - start) / 864e5; // 30.958333333333332, oops! 🤯
但是,您可以使用d3.timeDay.count
d3.timeDay.count(start, end) // 31 😌
该day interval 是 d3-time 提供的几个区间之一。每个区间代表一个传统的时间单位——小时、周、月,等等——并且具有计算边界日期的方法。例如,d3.timeDay 计算对应日期的午夜 (通常是当地时间的上午 12:00)。除了四舍五入和计数之外,区间还可以用来生成边界日期的数组。例如,要计算本月每个星期天
const start = d3.timeMonth.floor(new Date(2015, 0, 15)); // 2015-01-01T00:00
const stop = d3.timeMonth.ceil(new Date(2015, 0, 15)); // 2015-02-01T00:00
const weeks = d3.timeWeek.range(start, stop); // [2015-01-04T00:00, 2015-01-11T00:00, 2015-01-18T00:00, 2015-01-25T00:00]
d3-time 模块没有实现自己的日历系统;它只是在 ECMAScript Date 之上实现了方便的日历数学 API。因此,它忽略了闰秒,并且只能处理本地时区和协调世界时 (UTC)。
此模块由 D3 的时间比例尺用于生成合理的时间刻度,由 D3 的时间格式使用,也可以直接用于执行日历布局等操作。
interval(date)
d3.utcMonday() // the latest preceding Monday, UTC time
源代码 · 等效于 interval.floor,除了如果未指定 date,则默认为当前时间。例如,d3.timeYear(date) 和 d3.timeYear.floor(date) 等效。
interval.floor(date)
d3.utcMonday.floor(new Date()) // the latest preceding Monday, UTC time
源代码 · 返回一个新的日期,表示在 date 之前或等于 date 的最新区间边界日期。例如,d3.timeDay.floor(date) 通常返回给定 date 的当地时间的上午 12:00。
此方法是幂等的:如果指定的 date 已经针对当前区间进行了下取整,则返回具有相同时间的新的日期。此外,返回的日期是相关区间的最小可表达值,因此 interval.floor(interval.floor(date) - 1) 将返回前一个区间边界日期。
请注意,==
和 ===
运算符不使用 Date 对象按值比较,因此您不能使用它们来判断指定的 date 是否已经进行了下取整。相反,将值强制转换为数字,然后比较
// Returns true if the specified date is a day boundary.
function isDay(date) {
return +d3.timeDay.floor(date) === +date;
}
这比测试时间是否为上午 12:00 更可靠,因为在某些时区,由于夏令时,午夜可能不存在。
interval.round(date)
d3.utcMonday.round(new Date()) // the previous or following Monday, whichever is closer
源代码 · 返回一个新的日期,表示最接近 date 的区间边界日期。例如,如果 date 在中午或之前,则 d3.timeDay.round(date) 通常返回给定 date 的当地时间的上午 12:00;如果 date 在中午之后,则返回下一天的当地时间的上午 12:00。
此方法是幂等的:如果指定的 date 已经针对当前区间进行了四舍五入,则返回具有相同时间的新的日期。
interval.ceil(date)
d3.utcMonday.ceil(new Date()) // the following Monday
源代码 · 返回一个新的日期,表示在 date 之后或等于 date 的最早区间边界日期。例如,d3.timeDay.ceil(date) 通常返回给定 date 的下一天的当地时间的上午 12:00。
此方法是幂等的:如果指定的 date 已经针对当前区间进行了上取整,则返回具有相同时间的新的日期。此外,返回的日期是相关区间的最大可表达值,因此 interval.ceil(interval.ceil(date) + 1) 将返回下一个区间边界日期。
interval.offset(date, step)
d3.utcDay.offset(new Date(), 1) // the same time tomorrow
源代码 · 返回一个新的日期,等于 date 加上 step 个区间。如果未指定 step,则默认为 1。如果 step 为负数,则返回的日期将早于指定的 date;如果 step 为零,则返回指定的 date 的副本;如果 step 不是整数,则进行下取整。此方法不会将指定的 date 四舍五入到区间。例如,如果 date 是今天下午 5:34,则 d3.timeDay.offset(date, 1) 将返回明天的下午 5:34 (即使夏令时改变了!)。
interval.range(start, stop, step)
d3.utcDay.range(new Date("2014-01-01"), new Date("2015-01-01")) // every day in 2014
源代码 · 返回一个日期数组,表示在 start (包含) 之后或等于 start 并且在 stop (不包含) 之前的每个区间边界。如果指定了 step,则将返回每 step 个边界;例如,对于 d3.timeDay 区间,step 为 2 将返回每隔一天。如果 step 不是整数,则进行下取整。
返回数组中的第一个日期是 start 之后或等于 start 的最早边界;后续日期按 step 个区间进行偏移并进行下取整。因此,两个重叠的范围可能是一致的。例如,此范围包含奇数天
d3.timeDay.range(new Date(2015, 0, 1), new Date(2015, 0, 7), 2) // [2015-01-01T00:00, 2015-01-03T00:00, 2015-01-05T00:00]
而此范围包含偶数天
d3.timeDay.range(new Date(2015, 0, 2), new Date(2015, 0, 8), 2) // [2015-01-02T00:00, 2015-01-04T00:00, 2015-01-06T00:00]
要使范围在指定 step 时保持一致,请使用 interval.every 代替。
为了方便起见,还为 interval.range 提供了别名,作为相应区间的复数形式,例如 utcMondays。
interval.filter(test)
源代码 · 返回一个新的区间,它是使用指定的 test 函数对该区间的过滤子集。test 函数将传递一个日期,如果且仅当指定的日期应被视为该区间的一部分,则返回 true。例如,要创建一个返回每个月的第 1、11、21 和 31 天 (如果存在) 的区间
d3.timeDay.filter((d) => (d.getDate() - 1) % 10 === 0)
返回的过滤区间不支持 interval.count。另请参见 interval.every。
interval.every(step)
d3.unixDay.every(3)
源代码 · 返回该区间的过滤视图,表示每 step 个日期。step 的含义取决于该区间由字段函数定义的父区间。例如,d3.timeMinute.every(15) 返回一个表示每十五分钟的区间,从小时开始::00、:15、:30、:45,等等。请注意,对于某些区间,生成的日期可能不是均匀间隔的;d3.timeDay 的父区间是 d3.timeMonth,因此区间号在每个月的开始处重置。如果 step 无效,则返回 null。如果 step 为一,则返回该区间。
此方法可以与 interval.range 结合使用,以确保两个重叠的范围是一致的。例如,此范围包含奇数天
d3.timeDay.every(2).range(new Date(2015, 0, 1), new Date(2015, 0, 7)) // [2015-01-01T00:00, 2015-01-03T00:00, 2015-01-05T00:00]
此范围也是如此
d3.timeDay.every(2).range(new Date(2015, 0, 2), new Date(2015, 0, 8)) // [2015-01-03T00:00, 2015-01-05T00:00, 2015-01-07T00:00]
返回的过滤区间不支持 interval.count。另请参见 interval.filter。
interval.count(start, end)
源代码 · 返回从start(不包含)到end(包含)之间的间隔边界数量。请注意,此行为与 interval.range 略有不同,因为它的目的是返回指定end日期相对于指定start日期的零基数字。例如,要计算当前的零基年内天数
d3.timeDay.count(d3.timeYear(now), now) // 177
同样,要计算以周日开始的当前的零基年内周数
d3.timeSunday.count(d3.timeYear(now), now) // 25
timeInterval(floor, offset, count, field)
const utcDay = d3.timeInterval(
(date) => date.setUTCHours(0, 0, 0, 0), // floor
(date, step) => date.setUTCDate(date.getUTCDate() + step), // offset
(start, end) => (end - start) / 864e5, // count
(date) => date.getUTCDate() - 1 // field
);
源代码 · 使用指定的floor和offset函数以及可选的count函数构建一个新的自定义间隔。
floor函数接受单个日期作为参数,并将其向下舍入到最接近的间隔边界。
offset函数接受日期和整数步长作为参数,并根据指定的边界数推进指定的日期;步长可以是正数、负数或零。
可选的count函数接受已舍入到当前间隔的起始日期和结束日期,并返回起始日期(不包含)和结束日期(包含)之间的边界数量。如果未指定count函数,则返回的间隔不会公开 interval.count 或 interval.every 方法。注意:由于内部优化,指定的count函数不得调用其他时间间隔的interval.count。
可选的field函数接受已舍入到当前间隔的日期,并返回指定日期的字段值,对应于此日期(不包含)和最新上级边界之间的边界数量。例如,对于 d3.timeDay 间隔,这将返回自本月开始以来的天数。如果未指定field函数,它将默认计算自 1970 年 1 月 1 日 UTC 的 UNIX 纪元以来的间隔边界数量。field函数定义 interval.every 的行为。
timeMillisecond
源代码 · 本地时间的毫秒;最短的可用时间单位。
timeSecond
源代码 · 本地时间的秒(例如,凌晨 01:23:45.0000);1,000 毫秒。
timeMinute
源代码 · 本地时间的分钟(例如,凌晨 01:02:00);60 秒。请注意,ECMAScript 忽略闰秒。
timeHour
源代码 · 本地时间的时(例如,凌晨 01:00);60 分钟。请注意,在本地时间推进一个小时可能会返回相同的小时或跳过一个小时,具体取决于夏令时。
timeDay
源代码 · 本地时间的日(例如,2012 年 2 月 7 日凌晨 12:00);通常为 24 小时。由于夏令时,本地时间的日可能在 23 到 25 小时之间。d3.unixDay 类似于 d3.utcDay,只是它计算自 UNIX 纪元(1970 年 1 月 1 日)以来的天数,因此interval.every 返回均匀间隔的日期,而不是根据月中日期而变化。
timeWeek
源代码 · d3.timeSunday 的别名;7 天,通常为 168 小时。由于夏令时,本地时间的周可能在 167 到 169 小时之间。
timeSunday
源代码 · 本地时间的以周日开始的周(例如,2012 年 2 月 5 日凌晨 12:00)。
timeMonday
源代码 · 本地时间的以周一开始的周(例如,2012 年 2 月 6 日凌晨 12:00)。
timeTuesday
源代码 · 本地时间的以周二开始的周(例如,2012 年 2 月 7 日凌晨 12:00)。
timeWednesday
源代码 · 本地时间的以周三开始的周(例如,2012 年 2 月 8 日凌晨 12:00)。
timeThursday
源代码 · 本地时间的以周四开始的周(例如,2012 年 2 月 9 日凌晨 12:00)。
timeFriday
源代码 · 本地时间的以周五开始的周(例如,2012 年 2 月 10 日凌晨 12:00)。
timeSaturday
源代码 · 本地时间的以周六开始的周(例如,2012 年 2 月 11 日凌晨 12:00)。
timeMonth
源代码 · 本地时间的月(例如,2012 年 2 月 1 日凌晨 12:00);范围从 28 到 31 天。
timeYear
源代码 · 本地时间的年(例如,2012 年 1 月 1 日凌晨 12:00);范围从 365 到 366 天。
utcMillisecond
源代码 · UTC 时间的毫秒;最短的可用时间单位。
utcSecond
源代码 · UTC 时间的秒(例如,凌晨 01:23:45.0000);1,000 毫秒。
utcMinute
源代码 · UTC 时间的分钟(例如,凌晨 01:02:00);60 秒。请注意,ECMAScript 忽略闰秒。
utcHour
源代码 · UTC 时间的时(例如,凌晨 01:00);60 分钟。
utcDay
源代码 · UTC 时间的日(例如,2012 年 2 月 7 日凌晨 12:00);24 小时。
utcWeek
源代码 · d3.timeSunday 的别名;7 天和 168 小时。
utcSunday
源代码 · UTC 时间的以周日开始的周(例如,2012 年 2 月 5 日凌晨 12:00)。
utcMonday
源代码 · UTC 时间的以周一开始的周(例如,2012 年 2 月 6 日凌晨 12:00)。
utcTuesday
源代码 · UTC 时间的以周二开始的周(例如,2012 年 2 月 7 日凌晨 12:00)。
utcWednesday
源代码 · UTC 时间的以周三开始的周(例如,2012 年 2 月 8 日凌晨 12:00)。
utcThursday
源代码 · UTC 时间的以周四开始的周(例如,2012 年 2 月 9 日凌晨 12:00)。
utcFriday
源代码 · UTC 时间的以周五开始的周(例如,2012 年 2 月 10 日凌晨 12:00)。
utcSaturday
源代码 · UTC 时间的以周六开始的周(例如,2012 年 2 月 11 日凌晨 12:00)。
utcMonth
源代码 · UTC 时间的月(例如,2012 年 2 月 1 日凌晨 12:00);范围从 28 到 31 天。
utcYear
源代码 · UTC 时间的年(例如,2012 年 1 月 1 日凌晨 12:00);范围从 365 到 366 天。
unixDay
类似于 d3.utcDay,只是它计算自 UNIX 纪元(1970 年 1 月 1 日)以来的天数,因此 interval.every 返回均匀间隔的日期,而不是根据月中日期而变化。
timeMilliseconds(start, stop, step)
d3.timeMillisecond.range 的别名。
timeSeconds(start, stop, step)
d3.timeSecond.range 的别名。
timeMinutes(start, stop, step)
d3.timeMinute.range 的别名。
timeHours(start, stop, step)
d3.timeHour.range 的别名。
timeDays(start, stop, step)
d3.timeDay.range 的别名。
timeWeeks(start, stop, step)
d3.timeWeek.range 的别名。
timeSundays(start, stop, step)
d3.timeSunday.range 的别名。
timeMondays(start, stop, step)
d3.timeMonday.range 的别名。
timeTuesdays(start, stop, step)
d3.timeTuesday.range 的别名。
timeWednesdays(start, stop, step)
这是 d3.timeWednesday 的别名。range。
timeThursdays(start, stop, step)
这是 d3.timeThursday 的别名。range。
timeFridays(start, stop, step)
这是 d3.timeFriday 的别名。range。
timeSaturdays(start, stop, step)
这是 d3.timeSaturday 的别名。range。
timeMonths(start, stop, step)
这是 d3.timeMonth 的别名。range。
timeYears(start, stop, step)
这是 d3.timeYear 的别名。range。
utcMilliseconds(start, stop, step)
这是 d3.utcMillisecond 的别名。range。
utcSeconds(start, stop, step)
这是 d3.utcSecond 的别名。range。
utcMinutes(start, stop, step)
这是 d3.utcMinute 的别名。range。
utcHours(start, stop, step)
这是 d3.utcHour 的别名。range。
utcDays(start, stop, step)
utcWeeks(start, stop, step)
这是 d3.utcWeek 的别名。range。
utcSundays(start, stop, step)
这是 d3.utcSunday 的别名。range。
utcMondays(start, stop, step)
这是 d3.utcMonday 的别名。range。
utcTuesdays(start, stop, step)
这是 d3.utcTuesday 的别名。range。
utcWednesdays(start, stop, step)
这是 d3.utcWednesday 的别名。range。
utcThursdays(start, stop, step)
这是 d3.utcThursday 的别名。range。
utcFridays(start, stop, step)
这是 d3.utcFriday 的别名。range。
utcSaturdays(start, stop, step)
这是 d3.utcSaturday 的别名。range。
utcMonths(start, stop, step)
这是 d3.utcMonth 的别名。range。
utcYears(start, stop, step)
这是 d3.utcYear 的别名。range。
timeTicks(start, stop, count)
Source · 等效于 d3.utcTicks,但以本地时间表示。
timeTickInterval(start, stop, count)
Source · 返回 d3.timeTicks 在相同参数下使用的 时间间隔。
utcTicks(start, stop, count)
Source · 返回一个大约包含 count 个日期的数组,这些日期以规则的间隔分布在 start 和 stop(含)之间。如果 stop 在 start 之前,则日期将以逆时间顺序返回;否则日期将以时间顺序返回。以下 UTC 时间间隔将被考虑
- 1 秒
- 5 秒
- 15 秒
- 30 秒
- 1 分钟
- 5 分钟
- 15 分钟
- 30 分钟
- 1 小时
- 3 小时
- 6 小时
- 12 小时
- 1 天
- 2 天
- 1 周
- 1 个月
- 3 个月
- 1 年
根据 d3.ticks 的规则,还将考虑毫秒的倍数(对于小范围)和年的倍数(对于大范围)。使用最接近 count 的日期数量的间隔。例如
const start = new Date("1970-03-01");
const stop = new Date("1996-03-19");
const count = 4;
const ticks = d3.utcTicks(start, stop, count); // [1975-01-01, 1980-01-01, 1985-01-01, 1990-01-01, 1995-01-01]
如果 count 是一个时间间隔,则此函数的行为与 interval.range 相似,不同之处在于 start 和 stop 都包含在内,并且如果 stop 在 start 之前,则它可能会以逆时间顺序返回日期。
utcTickInterval(start, stop, count)
Source · 返回 d3.utcTicks 在相同参数下使用的 时间间隔。如果没有关联的间隔(例如,当 start 或 stop 无效时),则返回 null。
const start = new Date("1970-03-01");
const stop = new Date("1996-03-19");
const count = 4;
const interval = d3.utcTickInterval(start, stop, count); // d3.utcYear.every(5)