背景

在前端Moment.js可以说是家喻户晓,目前在日期和时间的处理上仍然是使用最广泛的库。从2011年到现在Moment已经完成了它的使命,在技术发展到今天,JavaScript生态已经足够健全,有很多优秀的库可以替代moment.js。所以除非有重大问题会维护一下,小毛病就不管了,也不再发布大迭代版本创新了。不建议大家使用moment.js作为新项目的时间处理包,可以选择Luxon,Day.js,date-fns,js-Joda作为替代品。

Moment的继承者

  • day.js官网
  • Day.js 是一个轻量的处理时间和日期的 JavaScript 库,和 Moment.js 的 API 设计保持完全一样

对比

moment

dayjs

体积

67.8k左右

2KB左右

沙箱

Moment 对象是可变对象(mutable)

所有更改Day.js对象的API操作都将返回一个新的实例。

多语言支持



插件拓展

×


  • 插件可以给 Day.js 增加新功能和扩展已有功能:
  • AdvancedFormat 扩展了 dayjs().format API 以支持更多模版
  • RelativeTime 增加了 .from .to .fromNow .toNow 4个 API 来展示相对的时间 (e.g. 3 小时以前).
  • IsLeapYear 增加了 .isLeapYear API 返回一个 boolean 来展示一个 Dayjs’s 的年份是不是闰年.
  • WeekOfYear 增加了 .week() API 返回一个 number 来表示 Dayjs 的日期是年中第几周.
  • IsSameOrAfter 增加了 .isSameOrAfter() API 返回一个 boolean 来展示一个时间是否和一个时间相同或在一个时间之后.
  • IsSameOrBefore 增加了 .isSameOrBefore() API 返回一个 boolean 来展示一个时间是否和一个时间相同或在一个时间之前.

小结

简单来说day.js 是Moment的完美继承者,不仅解决了mutable的问题,同样解决了包体积过大的问题,同时保持了与moment相同的api,几乎不需要任何代价的进行切换使用。Luxon,date-fns,js-Joda感兴趣的同学可以自行了解。

JS 的新一代日期/时间 API Temporal

  • Temporal 博客 众所周知,JS的Date是出了名的难用,一直以来我们都在使用momentjs,dayjs等第三方库来处理日期和时间格式,于是 TC39 组织开始了对 Date 的升级改造,他们找到了 moment.js 库的作者,Maggie ,由她来担任新特性 Temporal的主力设计。
  • 特性
  • 所有的对象都是不可改变的。改变它们会产生新的值,类似于JavaScript中字符串的工作方式。
  • 它支持时区和非格雷戈尔式的日历。
  • 有几个专门针对时间值的类(带时区的日期时间值,不带时区的日期时间值,不带时区的日期值,等等)。这有几个好处。
  • 一个值的上下文(是否有时区,等等)更容易理解。
  • 如何实现一个给定的任务往往更加明显。
  • .toString() ,使用时可以少考虑很多。
  • 1月是第1个月。
  • Temporal 支持的日历基于标准的Unicode Unicode Common Locale Data Repository (CLDR) 也就是说支持农历、民国历等等
npm install @js-temporal/polyfill

import { Temporal} from '@js-temporal/polyfill';

Temporal是一个全局对象,像 Math 、Promise 一样位于顶级命名空间中,为 Javascript 语言带来了现代化的日期、时间接口。

对比Date和Temporal的使用

  • Temporal被设计为三部分
  • ISO 8601 格式的日期和时间;具体时间
  • 时区(中国北京);哪个时区的
  • 日历(中国农历);使用什么历法
  • 对比 Date
new Date()
//Mon May 16 2022 10:11:27 GMT+0800 (中国标准时间)

Date 采用 GMT格式(旧的时间表示格式) 的时间,使用方面不如 ISO 8601 通用,同时不包含 时区和历法。

使用方法(更多API)

  • 如何获取本地时区的当前日期和时间?
    请注意,如果您只需要日期而不是时间,则应使用Temporal.PlainDate. 如果两者都需要,请使用Temporal.PlainDateTime.
const date = Temporal.Now.plainDateISO();
date.toString();

Temporal.Now.plainDateTimeISO().toString();
  • 如何获得 Unix 时间戳?
const timeStamp = Temporal.Now.instant();

// Timestamp in Milliseconds
timeStamp.epochMilliseconds;
// Timestamp in Seconds
timeStamp.epochSeconds;
  • Temporal在类型和遗留之间转换Date
  • 旧版Date=>Temporal.Instant和/或Temporal.ZonedDateTime
const legacyDate = new Date('1970-01-01T00:00:01Z');
const instant = legacyDate.toTemporalInstant();  // 转换为 temporal 格式

assert.equal(instant.epochMilliseconds, legacyDate.getTime()); // 和 Date 对比
assert.equal(instant.toString(), '1970-01-01T00:00:01Z');

const zoned = instant.toZonedDateTimeISO(Temporal.Now.timeZone());

assert.equal(zoned.epochMilliseconds, legacyDate.getTime());

const zoned2 = instant.toZonedDateTimeISO('Asia/Shanghai');

assert.equal(zoned2.epochMilliseconds, legacyDate.getTime());
assert.equal(zoned2.timeZone.id, 'Asia/Shanghai');
  • 仅日期值:legacy Date=>Temporal.PlainDate
    要正确地将 date-only 转换Date为 aTemporal.PlainDate而不会受到偏离一天错误的影响,您必须确定使用哪个时区的午夜来构造Date,然后在从 转换为 时使用相同的时Temporal.Instant区Temporal.PlainDate。
let date = new Date(2000, 0, 1);
	let plainDate = date
	  .toTemporalInstant()                         // => 2000-01-01T08:00:00Z
	  .toZonedDateTimeISO(Temporal.Now.timeZone()) // => 2000-01-01T00:00:00-08:00[America/Los_Angeles]
	  .toPlainDate();                              // => 2000-01-01
	  
	assert.equal(plainDate.toString(), '2000-01-01');
	
	date = new Date(Date.UTC(2000, 0, 1)); // => Fri Dec 31 1999 16:00:00 GMT-0800 (Pacific Standard Time)
	date = new Date('2000-01-01T00:00Z');  // => Fri Dec 31 1999 16:00:00 GMT-0800 (Pacific Standard Time)
	plainDate = date
	  .toTemporalInstant()       // => 2000-01-01T00:00:00Z
	  .toZonedDateTimeISO('UTC') // => 2000-01-01T00:00:00+00:00[UTC]
	  .toPlainDate();            // => 2000-01-01
	
	assert.equal(plainDate.toString(), '2000-01-01');
  • Temporal类型 => 遗留Date
// To convert Instant to legacy Date, use the epochMilliseconds property.

const instant = Temporal.Instant.from('2020-01-01T00:00:01.000999Z');
const result = new Date(instant.epochMilliseconds);

assert.equal(result.getTime(), 1577836801000); // ms since Unix epoch
assert.equal(result.toISOString(), '2020-01-01T00:00:01.000Z');

// Same thing for ZonedDateTime.
// Note that legacy Date will not preserve the ZonedDateTime's time zone.

const zoned = Temporal.ZonedDateTime.from('2020-01-01T00:00:01.001[Asia/Tokyo]');
const result2 = new Date(zoned.epochMilliseconds);

assert.equal(result2.getTime(), 1577804401001); // note, different time
assert.equal(result2.toISOString(), '2019-12-31T15:00:01.001Z');

// For most use cases, new Date(x.epochMilliseconds) is fine.
// You may need to add an extra round() step if you want other
// rounding behaviour than truncation. For example, here the 999
// microseconds is rounded to 1 millisecond.

const result3 = new Date(instant.round({ smallestUnit: 'millisecond' }).epochMilliseconds);

assert.equal(result3.getTime(), 1577836801001);
assert.equal(result3.toISOString(), '2020-01-01T00:00:01.001Z');
  • 类型之间的转换
  • 将日历日期 ( Temporal.PlainDate) 和挂钟时间 ( Temporal.PlainTime) 组合成Temporal.PlainDateTime. 就是将日期加上时间组合起来
const date = Temporal.PlainDate.from('2020-05-14');

const noonOnDate = date.toPlainDateTime(Temporal.PlainTime.from({ hour: 12 }));

assert(noonOnDate instanceof Temporal.PlainDateTime);

assert.equal(noonOnDate.toString(), '2020-05-14T12:00:00');
  • 将日历上的一天 ( Temporal.PlainMonthDay) 和一年组合成Temporal.PlainDate 就是将天和年组合起来
const birthday = Temporal.PlainMonthDay.from('12-15');

const birthdayIn2030 = birthday.toPlainDate({ year: 2030 });

birthdayIn2030.dayOfWeek; // => 7

assert(birthdayIn2030 instanceof Temporal.PlainDate);

assert.equal(birthdayIn2030.toString(), '2030-12-15');
  • 序列化
  • 要将精确时间序列Temporal.Instant化为字符串,请使用toString(). 没有任何参数,这会给你一个 UTC 时间的字符串。
  • 如果您需要您的字符串包含 UTC 偏移量,则使用该timeZone选项Temporal.Instant.prototype.toString()将返回该时区中与确切时间相对应的挂钟时间的字符串序列化。
  • 这会丢失有关字符串所在时区的信息,因为它只保留该特定确切时间与时区的 UTC 偏移量。如果您需要您的字符串包含时区名称,请改用Temporal.ZonedDateTime它保留此信息。
const instant = Temporal.Instant.from('2022-05-16T10:41:51Z');
// 使用 toString 获得字符串
const result = instant.toString(); // '2022-05-16T10:41:51Z'
// 使用 toString 获得某时区字符串
const result2 = instant.toString({ timeZone: 'America/Yellowknife' });

// 使用 toZonedDateTimeISO 转换时区
const zoned = instant.toZonedDateTimeISO('Asia/Seoul');
const result3 = zoned.toString();

// ZonedDateTime
assert(zoned.equals(Temporal.ZonedDateTime.from(result3)));
  • 同时Temporal 支持排序, 四舍五入, 时区转换, 时间的计算等等功能
  • 具体API查看上面链接的Doc文档

Temporal 小结

1.Date不支持除用户本地时间以外的时区。Temparal 支持开发人员通过 TimeZone 来设置本地时间以外的时区。
2.计算 API 缺失。除了时区和日历类型外,其他类型都可以进行 算术运算,即时间的比较,增加,减少等。
3.不支持非公历,Calendar 类型支持 Temparal 选择日历。
4.解析器行为不可靠以至于无法使用,在 Temporal 里,new 构造函数() 或者From 方法,对参数的要求都更加规范,同时From 方法支持 日期溢出 后的逻辑处理,可以防止系统崩溃。

js archiver版本_前端

左侧绿色区域的 Instant 类型,用来表达某个瞬间的时间,不包含时区和日历的信息。右侧黄色区域的 PlainXX系列(5个),用来表达日历日期或者钟表时间,包含日历信息,而中间的 ZonedDateTime 则横跨左右两个区域,包含时区和日历信息,可以作为一个通道,连接左侧的 Instant 和右侧的 Plain系列,负责类型之间转换的桥梁,同时中间的 Timezone 时区类型 Calendar 日历类型,不单独使用,配合上方的 ZonedDateTime 类型来辅助转换。最下面的 Duration 与所有类型没有直接关系,不参与类型转换,表示一段持续时间,并且这段时间可以用来进行算术。