# d3-time

在可视化时间序列数据、分析时间模式或处理一般时间时,常规时间单位的不规则性很快就变得明显起来。在 Gregorian calendar(公历) (opens new window) 中,大多数月份有 31 天但是有些月份只有 28 或者 2930 天。大多数年份有 365 天但是 leap years(闰年) (opens new window)366 天。在 daylight saving(夏令时) (opens new window) 中一天可能有 23 25 小时。更复杂的是世界各地的夏时制不同。

由于这些时间特性,执行看似微不足道的任务可能会很困难。例如,如果你想计算两个日期之间相隔的天数,你可能会简单的以 24 小时为一天去计算:

var start = new Date(2015, 02, 01), // Sun Mar 01 2015 00:00:00 GMT-0800 (PST)
    end = new Date(2015, 03, 01); // Wed Apr 01 2015 00:00:00 GMT-0700 (PDT)
(end - start) / 864e5; // 30.958333333333332, oops!

但是使用 d3.timeDay.count 会更容易:

d3.timeDay.count(start, end); // 31

day intervald3-time 提供的方法之一. 常用的时间单位比如 hours, weeks, months, etc. 都有对应的计算边界日期的方法。例如 d3.timeDay 通常会以当地时间的午夜作为一天的开始。为了 roundingcounting,间隔可以用来生成一组边界日期。比如计算当前月的每一个周日:

var now = new Date;
d3.timeWeek.range(d3.timeMonth.floor(now), d3.timeMonth.ceil(now));
// [Sun Jun 07 2015 00:00:00 GMT-0700 (PDT),
//  Sun Jun 14 2015 00:00:00 GMT-0700 (PDT),
//  Sun Jun 21 2015 00:00:00 GMT-0700 (PDT),
//  Sun Jun 28 2015 00:00:00 GMT-0700 (PDT)]

d3-time 模块不会实现自己的日历系统。它仅仅是基于 ECMAScriptDate (opens new window) 实现了一些方便的数学计算 API. 因此,它忽略了闰秒,只能与当地时区和 Coordinated Universal Time(世界协调时) (opens new window) (UTC)一起工作。

这个模块可以被 D3 的时间比例尺用来生成合理的刻度,通过 D3 的时间格式化可以直接被用来做类似于 calendar layouts (opens new window) 之类的事情。

# Installing

NPM 安装: npm install d3-time. 此外还可以下载 latest release (opens new window). 可以直接从 d3js.org (opens new window)standalone library (opens new window) 或作为 D3 4.0 (opens new window) 的一部分引入. 支持 AMD, CommonJS, 以及基本的标签形式,如果使用标签引入会暴露全局 d3 变量:

<script src="https://d3js.org/d3-time.v1.min.js"></script>
<script>

var day = d3.timeDay(new Date);

</script>

在浏览器中测试 d3-time. (opens new window)

# API Reference

# interval(date) <> (opens new window)

interval.floor 的别名. 例如 d3.timeYear(date) 和 d3.timeYear.floor(date) 是等价的.

# interval.floor(date) <> (opens new window)

对日期的向下取整,返回一个新日期,返回的日期不超过当前 date。例如 d3.timeDay.floor(date) 通常返回给定 date 的当天 12:00 AM.

这个方法是幂等的:如果指定的 date 在当前时间间隔内,则返回相同时间的新日期。此外返回的日期是当前区间内的最小值,比如 interval.floor(interval.floor(date) - 1) 会返回前一个间隔的最小时间值。

注意 == and === 操作不能对比 Date (opens new window) 对象,因此不能使用这个操作来判读指定的 date 是否已经完成向下取整。作为替代,你可以将其转换为数值然后对比:

// Returns true if the specified date is a day boundary.
function isDay(date) {
  return +d3.timeDay.floor(date) === +date;
}

这比测试时间是否为 12:00 AM 更可靠,因为在某些时区,由于夏令时可能不存在午夜。

# interval.round(date) <> (opens new window)

日期取整,返回指定 date 的取整时刻。例如 d3.timeDay.round(date),如果当前 date12:00 AM 之前会返回当天的 12:00 AM,否则会返回下一天的 12:00 AM.

这个方法也是幂等的:对于一定时间区间内的 date 都会返回相同的新时刻。

# interval.ceil(date) <> (opens new window)

日期向上取整,返回不晚于指定 date 的最早的时刻。比如,d3.timeDay.ceil(date) 通常会返回指定 date 下一天的 12:00 AM.

这个方法也是幂等的:对于一定时间区间内的 date 都会返回相同的新时刻。因此 interval.ceil(interval.ceil(date) + 1) 会返回当前日期的下下一天的 12:00 AM.

# interval.offset(date[, step]) <> (opens new window)

返回一个新的日期,新的日期等于 datestep 间隔。如果没有指定 step 则默认为 1。如果 step 为负,则返回的新日期将比指定的 date 早。如果 step0 则返回指定 date 的拷贝;如果 step 不是整数,则会使用 floored (opens new window) 进行取整。这个方法不会对指定的 date 进行四舍五入。例如如果 date 为 今天的 5:34 PMd3.timeDay.offset(date, 1) 会返回明天的 5:34 PM(即使夏令时改变了也能正确返回)。

# interval.range(start, stop[, step]) <> (opens new window)

返回介于 start (包含) 和 stop (不包含) 之间的日期间隔数组。如果 step 没有指定则默认返回每个日期间隔边界值;例如 d3.timeDay 间隔步长设置为 2 时表示每隔一天返回取一个值。如果 step 不是整数,则会使用 floored (opens new window) 进行取整。

返回的数组的第一个元素的日期值表示所取的日期范围的最早值,不早于 start;随后的所有的值之间的间隔都为 step 表示的日期间隔。例如只取奇数天:

d3.timeDay.range(new Date(2015, 0, 1), new Date(2015, 0, 7), 2);
// [Thu Jan 01 2015 00:00:00 GMT-0800 (PST),
//  Sat Jan 03 2015 00:00:00 GMT-0800 (PST),
//  Mon Jan 05 2015 00:00:00 GMT-0800 (PST)]

只取偶数天:

d3.timeDay.range(new Date(2015, 0, 2), new Date(2015, 0, 8), 2);
// [Fri Jan 02 2015 00:00:00 GMT-0800 (PST),
//  Sun Jan 04 2015 00:00:00 GMT-0800 (PST),
//  Tue Jan 06 2015 00:00:00 GMT-0800 (PST)]

如果想要每个间隔的步长一致,可以使用 interval.every 代替。

# interval.filter(test) <> (opens new window)

返回一个新的间隔,这个间隔是使用 test 函数对指定间隔进行过滤之后的子集。test 函数传递日期并且应该返回真或假以表示当前日期是否被被考虑在内。例如创建一个返回每个月 1st, 11th, 21th and 31th(如果存在) 的间隔:

var i = d3.timeDay.filter(function(d) { return (d.getDate() - 1) % 10 === 0; });

返回的新的间隔不支持 interval.count. 同时参考 interval.every.

# interval.every(step) <> (opens new window)

返回当前间隔的经过过滤后的日期列表。step 依赖于当前间隔的类型,例如 d3.timeMinute.every(15) 会返回一个表示每隔 15 分钟取一个值的间隔。需要注意,对于一些间隔返回的日期可能不是均匀的。如果 step 不可用则返回 null。如果 step1 则返回当前间隔。

这个方法可以和 interval.range 结合使用以保障生成固定的日期列表:

d3.timeDay.every(2).range(new Date(2015, 0, 1), new Date(2015, 0, 7));
// [Thu Jan 01 2015 00:00:00 GMT-0800 (PST),
//  Sat Jan 03 2015 00:00:00 GMT-0800 (PST),
//  Mon Jan 05 2015 00:00:00 GMT-0800 (PST)]

同上:

d3.timeDay.every(2).range(new Date(2015, 0, 2), new Date(2015, 0, 8));
// [Sat Jan 03 2015 00:00:00 GMT-0800 (PST),
//  Mon Jan 05 2015 00:00:00 GMT-0800 (PST),
//  Wed Jan 07 2015 00:00:00 GMT-0800 (PST)]

返回的过滤间隔不支持 interval.count,同时参考 interval.filter.

# interval.count(start, end) <> (opens new window)

返回以当前间隔为单位,在 start(不包含) 之后和 end(包含) 之前的个数。注意这个行为与 interval.range 有所不同,因为它的目标是返回 end 相对于 start 之间的统计数。例如计算当前日期在当年内的基于 0 的天数统计:

var now = new Date;
d3.timeDay.count(d3.timeYear(now), now); // 177

同样的,计算当前基于 0 的对周个数统计(过了多少周):

d3.timeSunday.count(d3.timeYear(now), now); // 25

# d3.timeInterval(floor, offset[, count[, field]]) <> (opens new window)

构造给定指定的 flooroffset 函数以及可选的 count 函数自定义间隔。

floor 函数接受单个日期作为参数,并将其四舍五入到最近的区间边界。

floor 函数以日期和整数步长作为参数,将指定的日期向前推进指定的边界数; 步长可以是正的,负的或零。

可选的 count 函数接收一个起始日期和结束日期,向下取整到当前间隔,并返回开始(独占)和结束(包含)之间的边界数。如果没有指定 count 方法则返回的间隔不会暴露 interval.count or interval.every 方法。注意,由于内部优化,指定的 count 函数不能调用其他的 interval.count 方法。

可选字段 field 函数接受一个日期,该日期已向下取整到当前间隔,并返回指定日期的字段值,该值对应于此日期(排他的)和上一个最新父边界之间的边界数。例如,对于 d3.timeDay,会返回从当月初开始到指定日期的天数。如果没有指定 field 函数,则默认返回 1970年1月1日UTC的UNIX纪元 以来的间隔边界数。field 函数决定了 interval.every 的行为。

# Intervals

提供以下间隔类型:

# d3.timeMillisecond <> (opens new window)
# d3.utcMillisecond

毫秒

# d3.timeSecond <> (opens new window)
# d3.utcSecond

秒 (e.g., 01:23:45.0000 AM); 1,000 毫秒.

# d3.timeMinute <> (opens new window)
# d3.utcMinute <> (opens new window)

分钟 (e.g., 01:02:00 AM); 60 秒. 注意 ECMAScript ignores leap seconds(忽略闰秒) (opens new window).

# d3.timeHour <> (opens new window)
# d3.utcHour <> (opens new window)

小时 (e.g., 01:00 AM); 60 分钟. 注意夏令时.

# d3.timeDay <> (opens new window)
# d3.utcDay <> (opens new window)

天 (e.g., February 7, 2012 at 12:00 AM); 通常 24 小时. 注意夏令时.

# d3.timeWeek <> (opens new window)
# d3.utcWeek <> (opens new window)

d3.timeSunday 的别名; 通常 7168 小时. 注意夏令时.

# d3.timeSunday <> (opens new window)
# d3.utcSunday <> (opens new window)

基于周日的周 (e.g., February 5, 2012 at 12:00 AM).

# d3.timeMonday <> (opens new window)
# d3.utcMonday <> (opens new window)

基于周一的周 (e.g., February 6, 2012 at 12:00 AM).

# d3.timeTuesday <> (opens new window)
# d3.utcTuesday <> (opens new window)

基于周二的周 (e.g., February 7, 2012 at 12:00 AM).

# d3.timeWednesday <> (opens new window)
# d3.utcWednesday <> (opens new window)

基于周三的周 (e.g., February 8, 2012 at 12:00 AM).

# d3.timeThursday <> (opens new window)
# d3.utcThursday <> (opens new window)

基于周四的周 (e.g., February 9, 2012 at 12:00 AM).

# d3.timeFriday <> (opens new window)
# d3.utcFriday <> (opens new window)

基于周五的周 (e.g., February 10, 2012 at 12:00 AM).

# d3.timeSaturday <> (opens new window)
# d3.utcSaturday <> (opens new window)

基于周六的周 (e.g., February 11, 2012 at 12:00 AM).

# d3.timeMonth <> (opens new window)
# d3.utcMonth <> (opens new window)

月 (e.g., February 1, 2012 at 12:00 AM); 2831 天.

# d3.timeYear <> (opens new window)
# d3.utcYear <> (opens new window)

年 (e.g., January 1, 2012 at 12:00 AM); 365366 天.

# Ranges

为方便起见,interval.range 的别名也以相应区间的复数形式提供。

# d3.timeMilliseconds(start, stop[, step]) <> (opens new window)
# d3.utcMilliseconds(start, stop[, step])

别名 d3.timeMillisecond.ranged3.utcMillisecond.range.

# d3.timeSeconds(start, stop[, step]) <> (opens new window)
# d3.utcSeconds(start, stop[, step])

别名 d3.timeSecond.ranged3.utcSecond.range.

# d3.timeMinutes(start, stop[, step]) <> (opens new window)
# d3.utcMinutes(start, stop[, step]) <> (opens new window)

别名 d3.timeMinute.ranged3.utcMinute.range.

# d3.timeHours(start, stop[, step]) <> (opens new window)
# d3.utcHours(start, stop[, step]) <> (opens new window)

别名 d3.timeHour.ranged3.utcHour.range.

# d3.timeDays(start, stop[, step]) <> (opens new window)
# d3.utcDays(start, stop[, step]) <> (opens new window)

别名 d3.timeDay.ranged3.utcDay.range.

# d3.timeWeeks(start, stop[, step])
# d3.utcWeeks(start, stop[, step])

别名 d3.timeWeek.ranged3.utcWeek.range.

# d3.timeSundays(start, stop[, step]) <> (opens new window)
# d3.utcSundays(start, stop[, step]) <> (opens new window)

别名 d3.timeSunday.ranged3.utcSunday.range.

# d3.timeMondays(start, stop[, step]) <> (opens new window)
# d3.utcMondays(start, stop[, step]) <> (opens new window)

别名 d3.timeMonday.ranged3.utcMonday.range.

# d3.timeTuesdays(start, stop[, step]) <> (opens new window)
# d3.utcTuesdays(start, stop[, step]) <> (opens new window)

别名 d3.timeTuesday.ranged3.utcTuesday.range.

# d3.timeWednesdays(start, stop[, step]) <> (opens new window)
# d3.utcWednesdays(start, stop[, step]) <> (opens new window)

别名 d3.timeWednesday.ranged3.utcWednesday.range.

# d3.timeThursdays(start, stop[, step]) <> (opens new window)
# d3.utcThursdays(start, stop[, step]) <> (opens new window)

别名 d3.timeThursday.ranged3.utcThursday.range.

# d3.timeFridays(start, stop[, step]) <> (opens new window)
# d3.utcFridays(start, stop[, step]) <> (opens new window)

别名 d3.timeFriday.ranged3.utcFriday.range.

# d3.timeSaturdays(start, stop[, step]) <> (opens new window)
# d3.utcSaturdays(start, stop[, step]) <> (opens new window)

别名 d3.timeSaturday.ranged3.utcSaturday.range.

# d3.timeMonths(start, stop[, step]) <> (opens new window)
# d3.utcMonths(start, stop[, step]) <> (opens new window)

别名 d3.timeMonth.ranged3.utcMonth.range.

# d3.timeYears(start, stop[, step]) <> (opens new window)
# d3.utcYears(start, stop[, step]) <> (opens new window)

别名 d3.timeYear.ranged3.utcYear.range.

最后更新: 2019/5/18 下午4:11:02