指南区域旨在帮助开发人员学习更好地处理日期和时间问题领域,以及 Moment.js 库。我们在这里解决了最常见的支持请求,因此这里是您查找任何问题解决方案的好地方。
指南部分是新的,仍在建设中。如果您有想要在此处看到的指南请求,或者想添加指南,请在 momentjs.com 存储库 中创建问题或提出拉取请求。
如果您刚开始使用,请查看此 scrimba moment 指南。
Moment.js 中的 moment 对象是可变的。这意味着像加、减或设置这样的操作会更改原始的 moment 对象。许多开发人员在第一次使用 Moment.js 时会被这样的场景所迷惑
var a = moment('2016-01-01');
var b = a.add(1, 'week');
a.format();
"2016-01-08T00:00:00-06:00"
如您所见,添加一周会改变 a
。为了避免这种情况,请在执行日期计算之前克隆 moment
var a = moment('2016-01-01');
var b = a.clone().add(1, 'week');
a.format();
"2016-01-01T00:00:00-06:00"
时间计算和日期计算之间存在逻辑差异。
在 Moment.js 中,时间计算假设时间刻度是线性的,只需根据提供的时间单位的数量增加或减少基于 UTC 的时间戳。
日期计算不使用线性时间刻度,而是增加或减少日历上的日期。这是因为一天、一个月或一年中的时间量是可变的。例如,由于夏令时转换,一天的长度可能在 23 到 25 小时之间。当然,月份的天数各不相同,而且由于闰年的原因,年的长度也不同。日期计算会导致一些有趣的情况。
由于夏令时,一天可能不等于 24 小时
//date math
moment('2016-03-12 13:00:00').add(1, 'day').format('LLL')
"March 13, 2016 1:00 PM"
//time math
moment('2016-03-12 13:00:00').add(24, 'hours').format('LLL')
"March 13, 2016 2:00 PM"
由于闰年,一年可能不等于 365 天
moment('2016-01-01').add(1, 'year').format('LL')
"January 1, 2017"
moment('2016-01-01').add(365, 'day').format('LL')
"December 31, 2016"
由于日期计算中持续时间的可变性,Moment 的 API 不正式支持对天数及更大单位进行加减小数运算。Moment.js 将接受小数值并尽力通过四舍五入到最接近的整数来处理它们。
从 2.12.0 开始,小数天和月值使用绝对值/四舍五入转换为整数。这意味着 1.5 四舍五入为 2,-1.5 四舍五入为 -2。
moment().add(1.5, 'days') == moment().add(2, 'days')
moment().add(-1.5, 'days') == moment().add(-2, 'days') == moment().subtract(1.5, 'days') == moment().subtract(2, 'days')
moment().add(2.3, 'months') == moment().add(2, 'months')
moment().add(-2.3, 'months') == moment().add(-2, 'months') == moment().subtract(2.3, 'months') == moment().subtract(2, 'months')
季度和年份转换为月份,然后取绝对值/四舍五入。
moment().add(1.5, 'years') == moment().add(18, 'months')
moment().add(.8, 'years') == moment().add(9.6, 'months') == moment().add(10, 'months')
moment().add(1.5, 'quarters') == moment().add(4.5, 'months') == moment().add(5, 'months')
人们经常混淆时区和 UTC 偏移量之间的区别。
UTC 偏移量是一个值,表示特定日期和时间与 UTC 的距离。它在大多数情况下以 HH:mm 格式表示。
时区是所有人都遵守法定标准时间的地理区域。
由于夏令时,一个时区通常与 UTC 有多个偏移量。一年中的某个时间点,多个时区可能具有相同的偏移量。例如,时区 America/Chicago、America/Denver 和 America/Belize 在不同时间都具有 -06:00 的偏移量。因此,不可能仅从偏移量值推断出时区。
Moment.js 核心库提供了根据偏移量值调整时间的功能。它不提供根据时区数据调整日期的支持 - 这是由 Moment TimeZone 库提供的。
Moment.js 为原生 JavaScript 日期对象提供了一个包装器。通过这样做,Moment.js 扩展了功能并弥补了对象中的几个缺陷。
使用原生日期进行解析尤其不可预测。例如,假设我正在使用美国的计算机,但我有一个日期格式为 DD/MM/YYYY。
var a = new Date('01/12/2016'); //December 1 2016 in DD/MM/YYYY format
//"Tue Jan 12 2016 00:00:00 GMT-0600 (Central Standard Time)"
使用原生 Date 对象无法很好地解决此行为。但是 Moment 的解析器可以很好地处理它
moment('01/12/2016', 'DD/MM/YYYY', true).format()
"2016-12-01T00:00:00-06:00"
此外,ECMA Script 5 规范对 ISO 8601 日期偏移量做出了一个不寻常的断言
缺少时区偏移量的值是“Z”
实际上,这意味着没有偏移量的 ISO 8601 日期将被视为 UTC 值,从而导致以下奇怪现象
//US local format
var a = new Date('1/1/2016');
//"Fri Jan 01 2016 00:00:00 GMT-0600 (Central Standard Time)"
//ISO 8601
var a = new Date('2016-01-01');
//"Thu Dec 31 2015 18:00:00 GMT-0600 (Central Standard Time)"
ES2015 规范修复了这个错误,使其与 ISO8601 规范保持一致,该规范指定了没有偏移量的本地时间。这本身就很糟糕,因为它会带来许多负面的向后兼容性影响。
使用 Moment,除非您另行指定,否则日期始终被解释为本地时间。这不是采用 ES2015 会改变的事情。
moment('2016-01-01')
//"2016-01-01T00:00:00-06:00"
算术是原生 Date 对象缺乏的另一个领域。Date 对象实际上没有为此提供 API。相反,它依赖于溢出的日期值。假设您想将 2016 年 4 月 30 日加 1 天。使用日期对象,您将执行以下操作
var a = new Date('4/30/2016');
a.setDate(a.getDate() + 1);
这可以解决问题,但有点不直观。Moment 提供了一个用于加/减的 API
moment('4/30/2016', 'MM/DD/YYYY').add(1, 'day')
//"2016-05-01T00:00:00-05:00"
Moment 对象有几个以 _
为前缀的内部属性。
最常查看的内部属性是 _d
属性,它保存 Moment 包装的 JavaScript Date。开发人员经常对 _d
值的控制台输出感到困惑。Moment 使用一种称为纪元偏移的技术,导致此属性有时与 Moment 反映的实际日期值不同。特别是如果正在使用 Moment TimeZone,则此属性几乎永远不会与 Moment 从其公共 .format()
函数输出的实际值相同。因此,_d
和任何其他以前缀 _
开头的属性的值不应被用于任何目的。
要打印 Moment 的值,请使用 .format()
、.toString()
或 .toISOString()
。
要从 Moment 检索原生 Date 对象,请使用 .toDate()
。此函数返回一个经过适当偏移的日期,用于与第三方 API 交互。
Moment.js 有一个非常灵活和高级的解析器,允许实现大量功能。
解析器的灵活性也使其成为 Moment.js 中最常被误用的工具之一。
本节介绍了一些关于如何在您的情况下正确使用解析器的指南。
Moment 提供了三个用于解析日期的函数:基本 moment 函数、moment.utc 和 moment.parseZone。
如果您希望在用户本地时间的上下文中与日期进行交互,请使用 moment 函数。
moment('2016-01-01T23:35:01');
这将生成一个日期,其 UTC 偏移量与本地计算机相同
“2016-01-01T23:35:01-06:00”
如果您希望将日期作为 UTC 日期进行交互,请使用 moment.utc
moment.utc('2016-01-01T23:35:01');
这将生成一个 UTC 偏移量为 +0:00 的日期
“2016-01-01T23:35:01+00:00”
如果您的日期格式具有固定的时区偏移量,请使用 moment.parseZone
moment.parseZone("2013-01-01T00:00:00-13:00");
这将生成一个具有固定偏移量的日期
“2013-01-01T00:00:00-13:00”
请注意,如果您使用 moment() 或 moment.utc() 解析具有指定偏移量的日期,则该日期将从该偏移量转换为本地时间或 UTC
此日期偏移了 8 个小时,从 +2 变为 -6(本地机器的偏移量)
moment('2016-01-01T00:00:00+02:00').format()
"2015-12-31T16:00:00-06:00"
此日期偏移了 2 个小时,从 +2 变为 UTC
moment.utc('2016-01-01T00:00:00+02:00').format()
"2015-12-31T22:00:00+00:00"
如果您知道要解析的日期字符串的格式,那么明确指定该格式始终是最佳选择。
示例
moment('01/01/2016', 'MM/DD/YYYY')
moment('2016-01-01 11:31:23 PM', 'YYYY-MM-DD hh:mm:ss a')
如果您的日期采用 ISO 8601 格式,则可以使用 moment 中内置的常量来指示这一点
moment('2016-01-01 12:25:32', moment.ISO_8601)
ISO 8601 格式包括但不限于
2013-02-08 # A calendar date part
2013-W06-5 # A week date part
2013-02-08T09 # An hour time part separated by a T
2013-02-08 09 # An hour time part separated by a space
2013-02-08 09:30:26 # An hour, minute, and second time part
2013-02-08 09+07:00 # +-HH:mm
严格模式是解析日期的推荐模式。如果您的代码库允许,您应该始终使用严格模式。在 GitHub 和 Stack Overflow 上看到的一半以上解析器问题都可以通过严格模式解决。
在以后的版本中,解析器将默认使用严格模式。
严格模式要求 moment 的输入与指定的格式完全匹配,包括分隔符。严格模式是通过将 true 作为第三个参数传递给 moment 函数来设置的。
moment('01/01/2016', 'MM/DD/YYYY', true).format()
"2016-01-01T00:00:00-06:00"
moment('01/01/2016 some text', 'MM/DD/YYYY', true).format()
"Invalid date"
分隔符匹配
//forgiving mode
moment('01-01-2016', 'MM/DD/YYYY', false).format()
"2016-01-01T00:00:00-06:00"
//strict mode
moment('01-01-2016', 'MM/DD/YYYY', true).format()
"Invalid date"
通过严格模式修复的场景
//UUID matches YYYYDDD because it starts with 7 digits
moment('5917238b-33ff-f849-cd63-80f4c9b37d0c', moment.ISO_8601).format()
"5917-08-26T00:00:00-05:00"
//strict mode fails because trailing data exists
moment('5917238b-33ff-f849-cd63-80f4c9b37d0c', moment.ISO_8601, true).format()
"Invalid date"
//date has out of range value but is parsed anyways
moment('100110/09/2015', 'MM/DD/YYYY').format()
"2015-10-09T00:00:00-05:00"
//strict mode catches out of range issue
moment('100110/09/2015', 'MM/DD/YYYY', true).format()
"Invalid date"
//wrong date is parsed because non-strict mode ignores data after format
moment('2016-12-31 11:32 PM').format('LT')
"11:32 AM"
//trailing data is noticed
moment('2016-12-31 11:32 PM', moment.ISO_8601, true).format('LT')
"Invalid date"
虽然严格模式在大多数情况下效果更好,但当传递给 moment 的字符串的格式可能会有所不同时,宽松模式非常有用。
宽松模式有用的一种常见情况是,第三方 API 提供日期,并且该 API 的日期格式可能会更改。假设一个 API 最初以“YYYY-MM-DD”格式发送日期,然后更改为“MM/DD/YYYY”格式。
在严格模式下,以下代码会导致显示“无效日期”
moment('01/12/2016', 'YYYY-MM-DD', true).format()
"Invalid date"
在使用格式字符串的宽松模式下,您会得到错误的日期
moment('01/12/2016', 'YYYY-MM-DD').format()
"2001-12-20T00:00:00-06:00"
宽松模式下的错误日期场景对用户来说当然不那么明显,但因此可能会长时间未被注意到。
在严格模式和宽松模式之间进行选择时,重要的是要考虑日期准确性更重要,还是日期永远不会显示为“无效日期”更重要。
Moment 的解析器支持为日期字符串指定多种可能的格式。这对于日期可能来自多个数据源的情况非常有用。只需将格式作为数组传递即可
moment('12 March, 2016', ['DDMMMMY', 'MMMMDDY']).format()
"2016-03-12T00:00:00-06:00"
moment('March 12, 2016', ['DDMMMMY', 'MMMMDDY']).format()
"2016-03-12T00:00:00-06:00"
为了使此功能正常工作,moment 必须解析提供的每种格式。因此,使用的格式越多,解析所需的时间就越长。Moment 用于确定使用哪种格式的启发式方法如下
在几个地方,Moment.js 会显示有关未来将删除的功能的弃用警告。此处概述了解决方法。
Moment construction falls back to js Date.
This is discouraged and will be removed in an upcoming major release.
当为传递给字符串构造函数的日期找不到已知格式时,会抛出此弃用警告。要解决此问题,请为传递给 moment()
的字符串指定格式。
Use moment.updateLocale(localeName, config) to change an existing locale.
moment.defineLocale(localeName, config) should only be used for creating a new locale
当您尝试使用 defineLocale 函数更改现有区域设置时,会抛出此弃用警告。这样做会导致与属性继承相关的意外行为。moment.updateLocale 将正确替换现有区域设置上的属性。
自2.16.0版本起已删除警告。
可以在定义或加载父级之前使用父级定义区域设置。如果在创建 moment 时父级不存在或无法延迟加载,则父级将默认为全局区域设置。
Locale <key> not found. Did you forget to load it?
当设置了全局区域设置但 Moment 找不到它时,会显示此警告。也许此区域设置未捆绑在您的副本中。
moment().add(period, number) is deprecated. Please use moment().add(number, period)
moment().subtract(period, number) is deprecated. Please use moment().subtract(number, period)
Moment 不赞成将 add 和 subtract 的参数排序为(周期,数字)。反转您的参数。
错误
moment().add('hours', 3);
正确
moment().add(3, 'hours');
moment().min is deprecated, use moment.max
moment().max is deprecated, use moment.min
此警告不是拼写错误,但令人困惑。
在 2.7.0 版本之前,moment 支持 moment().min 和 moment().max 函数。这些函数不直观。
Min 将返回所讨论的两个时刻中较大的一个,而 max 将返回较小的一个。
由于这种反转行为,弃用警告中提供的建议是正确的。
moment('2016-01-01').min('2016-02-01').format()
"2016-02-01T00:00:00-06:00"
//is equivalent to
moment.max(moment('2016-01-01'), moment('2016-02-01')).format()
"2016-02-01T00:00:00-06:00"
moment('2016-01-01').max('2016-02-01').format()
"2016-01-01T00:00:00-06:00"
//is equivalent to
moment.min(moment('2016-01-01'), moment('2016-02-01')).format()
"2016-01-01T00:00:00-06:00"
moment().zone is deprecated,
use moment().utcOffset instead.
进行此弃用是为了清楚起见。
moment().zone()
的结果是一个整数,表示给定时刻与 UTC 的偏移分钟数,符号反转(美国时刻导致正值)。
使用 moment().zone(number)
设置偏移量将在日期上设置偏移量,也使用反转符号。
因为时区与偏移量不同,所以名称更改为 utcOffset。当时,符号被更正以反映 UTC 偏移量的实际方向。
moment().zone()
360
//is replaced by
moment().utcOffset()
-360
moment().zone(420)
//is replaced by
moment().utcOffset(-420)
这些资源由日期/时间/时区社区的成员制作。
Stack Overflow DateTime 与 DateTimeOffset