幻想编程

JavaScript 的 Date 最详细解读

时间:16-05-17 21:59:12点击:473

如何获得某个月的天数?

不知道大家遇到过这个问题吗?我想如果你们写过日期组件一定有这个问题,我当时的解决方案是这样的:

以下的三个方法,month 参数我都根据 JS 本身对于 Date 的月份定义,采用0为1月

最老实的办法

const EVERY_MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];

function getDays(year, month) {

if (month === 1 && isLeap(year)) return 29;

return EVERY_MONTH_DAYS[month];

}

手动做了每个月天数的映射,如果是2月份并闰年,那天数+1

那没有更好的方法呢?手动 map 和闰年判断的逻辑没有就好了。

稍微 diao 一点的办法

function getDays(year, month) {

if (month === 1) return new Date(year, month, 29).getMonth() === 1 ? 29 : 28;

return new Date(year, month, 31).getMonth() === month ? 31 : 30;

}

我们发现,new Date()的第三个参数是可以大于我们所知的每个月的最后一天的的,比如:

new Date(2016, 0, 200) //Mon Jul 18 2016 00:00:00 GMT+0800 (CST)

这样,我们就利用这个 JS 的特性,用29和31这两个关键点,去判断除了那个月的最后一天+1还是那个月吗?(其实28和30是关键点)。

再稍微 diao 一点的方法

function getDays(year, month) {

return new Date(year, month + 1, 0).getDate();

}

new Date()的第三个参数传小于1的值会怎么样了,比如传0,我们就获得了上个月的最后一天,当然传负数也没问题:

new Date(2016, 0, -200) //Sun Jun 14 2015 00:00:00 GMT+0800 (CST)

Date.prototype.各种String

这里主要和大家普及以下知识:

GMT(格林尼治平时)

格林尼治平时(又称格林尼治平均时间或格林尼治标准时间,旧译格林威治标准时间;英语:Greenwich Mean Time,GMT)是指位于英国伦敦郊区的皇家格林尼治天文台的标准时间,因为本初子午线被定义在通过那里的经线。

自1924年2月5日开始,格林尼治天文台每隔一小时会向全世界发放调时信息。

理论上来说,格林尼治标准时间的正午是指当太阳横穿格林尼治子午线时(也就是在格林尼治上空最高点时)的时间。由于地球在它的椭圆轨道里的运动速度不均匀,这个时刻可能与实际的太阳时有误差,最大误差达16分钟。

由于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治时间已经不再被作为标准时间使用。现在的标准时间,是由原子钟报时的协调世界时(UTC)。

所以我们也从 MDN 上的文档看到对于toGMTString()的解释是:

Returns a string representing the Date based on the GMT (UT) time zone. Use toUTCString() instead.

UTC(世界标准时间)

协调世界时,又称世界标准时间或世界协调时间,简称UTC(从英文「Coordinated Universal Time」/法文「Temps Universel Cordonn 」而来),是最主要的世界时间标准,其以原子时秒长为基础,在时刻上尽量接近于格林尼治平时

CST(北京时间)

北京时间,China Standard Time,中国标准时间。在时区划分上,属东八区,比协调世界时早8小时,记为UTC+8。

不过这个CST这个缩写比较纠结的是它可以同时代表四个不同的时间:

Central Standard Time (USA) UT-6:00

Central Standard Time (Australia) UT+9:30

China Standard Time UT+8:00

Cuba Standard Time UT-4:00

总结就是,前后端去传时间的时候,尽量都用 UTC 时间。

ISO 日期和时间的表示方法

if ( !Date.prototype.toISOString ) {

( function() {

function pad(number) {

if ( number < 10 ) {

return '0' + number;

}

return number;

}

Date.prototype.toISOString = function() {

return this.getUTCFullYear() +

'-' + pad( this.getUTCMonth() + 1 ) +

'-' + pad( this.getUTCDate() ) +

'T' + pad( this.getUTCHours() ) +

':' + pad( this.getUTCMinutes() ) +

':' + pad( this.getUTCSeconds() ) +

'.' + (this.getUTCMilliseconds() / 1000).toFixed(3).slice(2, 5) +

'Z';

};

}() );

}

通过 Polyfill 我们就能知道 ISO 是怎么表示时间的,最主要的特征是最后一位是 Z ,然后表示的总是 UTC 时间。

额外的补充

.valueOf() 和 .getTime()

.valueOf()的功能和.getTime()一样。

该方法通常在 JavaScript 内部被调用,而不是在代码中显式调用。什么意思?没有 valueOf,那么Date的实例是不能进行运算的。

var obj = Object.create(null);

obj + 1; // Uncaught TypeError: Cannot convert object to primitive value( )

.toJSON

直接看这个 API 的名字的时候,我以为会返回一个 JSON 格式的字符串,但其实是这么一个东西

new Date().toJSON() // "2016-05-05T06:03:28.130Z"

其实是这么回事

JSON.stringify(new Date()) // ""2016-05-05T06:06:02.615Z""

那结果能够被 parse 吗?

JSON.parse(JSON.stringify(new Date())) // "2016-05-05T06:19:24.766Z"

JSON.parse('"' + new Date().toJSON() + '"') // "2016-05-05T06:19:24.766Z"

但是结果只是字符串而已。需要再讲这个字符串交给 new Date() 才行。

.toLocaleFormat()

不属于任何标准。在JavaScript 1.6中被实现。似乎也只有 Firefox 自持这个 API,其实正确姿势是用.toLocaleDateString()

.toLocale各种String()

.toLcale各种String(locales [, options]])

妈的这个 API 有点烦,看 MDN 的文档你就知道。这个 API 是用来本地化时间的。

这里稍微说下我对这些参数的理解:

locales

var date = new Date(Date.UTC(2012, 11, 20, 3, 0, 0));

// formats below assume the local time zone of the locale;

// America/Los_Angeles for the US

// US English uses month-day-year order

alert(date.toLocaleString("en-US"));

// "12/19/2012, 7:00:00 PM"

// British English uses day-month-year order

alert(date.toLocaleString("en-GB"));

// "20/12/2012 03:00:00"

// Korean uses year-month-day order

alert(date.toLocaleString("ko-KR"));

// "2012. 12. 20. 오후 12:00:00"

// Arabic in most Arabic speaking countries uses real Arabic digits

alert(date.toLocaleString("ar-EG"));

// "٢٠ /١٢ /٢٠١٢ ٥:٠٠:٠٠ ص"

// for Japanese, applications may want to use the Japanese calendar,

// where 2012 was the year 24 of the Heisei era

alert(date.toLocaleString("ja-JP-u-ca-japanese"));

// "24/12/20 12:00:00"

// when requesting a language that may not be supported, such as

// Balinese, include a fallback language, in this case Indonesian

alert(date.toLocaleString(["ban", "id"]));

// "20/12/2012 11.00.00"

以locales所指的地区的时区和语言输出。

options

localeMatcher 选择本地匹配的什么算法,似乎没什么大用

timeZone 再设置下 UTC 时区

hour12 是否12小时制

formatMatcher 各日期时间单元的格式化

weekday Possible values are "narrow", "short", "long".

era Possible values are "narrow", "short", "long".

year Possible values are "numeric", "2-digit".

month Possible values are "numeric", "2-digit", "narrow", "short", "long".

day Possible values are "numeric", "2-digit".

hour Possible values are "numeric", "2-digit".

minute Possible values are "numeric", "2-digit".

second Possible values are "numeric", "2-digit".

timeZoneName Possible values are "short", "long".

栗子:

var date = new Date(Date.UTC(2012, 11, 20, 3, 0, 0));

date.toLocaleString("en-US", {hour12: false}); // "12/19/2012, 19:00:00"

var options = {timeZoneName:'long',weekday: "long", year: "2-digit", month: "narrow", day: "numeric"};

date.toLocaleString("en-US", options); // "Thursday, D 20, 12, China Standard Time"

老实的方法

let date = new Date();

let result = [

[

date.getFullYear(),

date.getMonth() + 1,

date.getDate()

].join('-'),

[

date.getHours(),

date.getMinutes(),

date.getSeconds()

].join(':')

].join(' ').replace(/\b\d\b/g, '0$$&');

diao 一点的方法

var date = new Date();

var result = date.toLocaleString('zh-CN', { hour12: false })

.replace(/\//g, '-').replace(/\b\d\b/g, '0$$&');