一起Polyfill系列:让Date识别ISO 8601日期时间格式
一、什么是ISO 8601日期时间格式
ISO 8601是国际标准化组织制定的日期时间表示规范,全称是《数据存储和交换形式·信息交换·日期和时间的表示方法》。
示例:
1. 2014-12-12T00:00:00.000Z
2. 2014-12-12T00:00:00.000+08
3. 2014-12-12T00:00:00.000+0800
4. 2014-12-12T00:00:00.000+08:00
5. 2004-W17-3
6. 0001-165
详细说明请参考度娘:http://baike.baidu.com/link?url=Qr7NLClAyUHihOCl1DK6DQL_gMw5rk3euXdiz3zt6M9ORGFS2XBy7LHmcO2ID-iz
二、Javascript中实现的ISO 8601日期时间格式
度娘后大家应该对ISO 8061有一定的了解了吧。ISO 8601的内容十分丰富,可惜Javascript仅仅实现一小部分而已,不过这一部分就够我们用了。
javascript支持的ISO 8061格式如下:
1. 2014-12-12T00:00:00.000Z
2. 2014-12-12T00:00:00.000+0800
3. 2014-12-12T00:00:00.000+08:00
二、ES5中涉及ISO 8061日期时间格式的方法
1. Date.parse({String} datetime)
:接收ISO 8061和GMT的日期时间格式字符串(根据格式内容被识别为0时区或其他时区的日期时间),返回入参所表示的0时区日期时间距离1970年1月1日的毫秒数。
2. Date.prototype.toISOString()
:返回当前Date类型对象0时区的ISO 8061日期时间格式字符串。形如:2014-12-12T00:00:00.000Z
3. new Date({String} datetime)
:构造函数的入参在ES5中新增接收ISO 8061格式字符串,其实内部就是调用 Date.parse({String} datetime) 进行转换。
4. Date.prototype.toJSON()
:返回当前Date类型对象0时区的ISO 8061日期时间格式字符串。形如:2014-12-12T00:00:00.000Z。
三、认识ES3下的Date类型
1. 作为构造函数使用
/**
* 第一种入参模式:无入参,实例化当前日期时间的Date对象
*/
var date1 = new Date();
/**
* 第二种入参模式:短日期格式字符串入参,实例化当前时区日期时间的Date对象
*/
var date2 = new Date('2014/12/3');
/**
* 第三种入参模式:长日期格式字符串入参,实例化当前时区日期时间的Date对象
*/
var date3 = new Date('Aug 3, 2014');
/**
* 第四种入参模式:GMT日期格式字符串入参,实例化指定时区日期时间的Date对象
*/
var date4 = new Date('Tue May 25 2014 00:00:00 GMT +0800');
/**
* 第五种入参模式:GMT日期格式字符串入参,实例化0时区日期时间的Date对象
*/
var date5 = new Date('Tue May 25 2014 00:00:00 GMT');
/**
* 第六种入参模式:入参依次为年、月、日、时、分、秒和毫秒的数值(其中仅年和月为必填项,日默认值为1,其他默认值为0),实例化当前时区日期时间的Date对象
*/
var date6 = new Date(2014,12,2,1,1,1,1);
2. 作为函数使用
// 无论入参是什么,总返回当前时区的GMT日期时间格式的字符串
var dateStr = Date();
3. 类成员
3.1. Date.parse({String} datetime)
:接收GMT的日期时间格式字符串(根据GMT格式内容被识别为0时区或其他时区的日期时间),返回入参所表示的0时区日期时间距离1970年1月1日的毫秒数
3.2. Date.UTC(Y,M,d,H,m,s,ms)
:设置0时区的日期时间,返回入参所表示的0时区日期时间距离1970年1月1日的毫秒数
4. 部分实例成员
4.1. Date.prototype.toGMTString()
:返回当前Date对象的GMT日期时间格式字符串(仅为了向后兼容而已)
4.2. Date.prototype.toUTCString()
:返回当前Date对象的GMT日期时间格式字符串(建议使用该方法)
四、一起Polyfill
if (!Date.prototype.toISOString){
var isLeapYear = function(year){
return (year % 400 === 0) || (year % 4 === 0 && year % 100 !== 0);
};
var operHoursAndMinutes = {};
operHoursAndMinutes['+'] = function(minusHours, minusMinutes, year, month, date, hours, minutes, seconds, milliseconds){
var ret = {};
minutes -= minusMinutes;
hours -= minusHours;
if (minutes < 0){
hours -= 1;
minutes += 60;
}
if (hours < 0 ){
--date;
hours += 24;
if (date < 0){
--month;
if (month < 0){
--year;
month = 11;
}
if (month % 2 === 0){
date += 31;
}
else if (month === 1)
{
date += isLeapYear(year) ? 29 : 28;
}
else{
date += 30;
}
if (month < 0){
--year;
month += 12;
}
}
}
ret.year = year;
ret.month = month;
ret.date = date;
ret.hours = hours;
ret.minutes = minutes;
ret.seconds = seconds;
ret.milliseconds = milliseconds;
return ret;
};
operHoursAndMinutes['-'] = function(addHours, addMinutes, year, month, date, hours, minutes, seconds, milliseconds){
var ret = {};
minutes += addMinutes;
hours += addHours;
if (minutes >= 60){
hours += 1;
minutes -= 60;
}
if (hours >=24){
++date;
hours -= 24;
var dateOfCurrMonth = month % 2 === 0 ? 31 : (month === 1 ? (isLeapYear(year) ? 29 : 28) : 30);
if (date >= dateOfCurrMonth){
++month;
date -= dateOfCurrMonth;
if (month >= 12){
++year;
month -= 12;
}
}
}
ret.year = year;
ret.month = month;
ret.date = date;
ret.hours = hours;
ret.minutes = minutes;
ret.seconds = seconds;
ret.milliseconds = milliseconds;
return ret;
};
var regExp = new RegExp('^(\d{4,4})'
+ '-((?:0[123456789]|1[012]))'
+ '-((?:0[123456789]|[12]\d|3[01]))'
+ 'T'
+ '((?:[01]\d|2[0123]))'
+ ':([012345]\d)'
+ ':([012345]\d)'
+ '(?:.(\d{3}))?'
+ '(Z|[+-](?:[01]\d|2[0123]):?[012345]\d)$');
var parseISOString2UTC = function(ISOString){
var ret = {};
var year = Number(RegExp.$1)
, month = Number(RegExp.$2) - 1
, date = Number(RegExp.$3)
, hours = Number(RegExp.$4)
, minutes = Number(RegExp.$5)
, seconds = Number(RegExp.$6)
, offset = RegExp.$8
, milliseconds;
milliseconds = (milliseconds = Number(RegExp.$7), !isNaN(milliseconds) && milliseconds || 0);
if (offset === 'Z'){
ret.year = year;
ret.month = month;
ret.date = date;
ret.hours = hours;
ret.minutes = minutes;
ret.seconds = seconds;
ret.milliseconds = milliseconds;
}
else if (typeof offset !== 'undefined'){
var symbol = offset.charAt(0);
var offsetHours = Number(offset.substring(1,3));
var offsetMinutes = Number(offset.substring(offset.length > 5 ? 4 : 3));
ret = operHoursAndMinutes[symbol](offsetHours, offsetMinutes, year, month, date, hours, minutes, seconds, milliseconds);
}
return ret;
};
var _nativeDate = Date;
Date = function(Y,M,D,H,m,s,ms){
var ret, len = arguments.length;
if (!(this instanceof Date)){
ret = _nativeDate.apply(null, arguments);
}
else if (len === 1 && typeof arguments[0] === 'string' && regExp.test(arguments[0])){
var tmpRet;
try{
tmpRet = parseISOString2UTC();
}
catch(e){
console && console.log('Invalid Date');
return void 0;
}
ret = new _nativeDate(_nativeDate.UTC(tmpRet.year, tmpRet.month, tmpRet.date, tmpRet.hours, tmpRet.minutes, tmpRet.seconds, tmpRet.milliseconds));
}
else if (typeof arguments[0] === 'string'){
ret = new _nativeDate(arguments[0]);
}
else{
ret = len >= 7 ? new _nativeDate(Y, M, D, H, m, s, ms)
: len >= 6 ? new _nativeDate(Y, M, D, H, m, s)
: len >= 5 ? new _nativeDate(Y, M, D, H, m)
: len >= 4 ? new _nativeDate(Y, M, D, H)
: len >= 3 ? new _nativeDate(Y, M, D)
: len >= 2 ? new _nativeDate(Y, M)
: len >= 1 ? new _nativeDate(Y)
: new _nativeDate();
}
return ret;
};
Date.prototype = _nativeDate.prototype;
Date.prototype.constructor = Date;
var _pad = function(num){
if (num < 10){
return '0' + num;
}
return num;
};
var _padMillisecond = function(num){
if (num < 10){
return '00' + num;
}
else if (num < 100){
return '0' + num;
}
return num;
};
Date.prototype.toISOString = function(){
return [this.getUTCFullYear(), '-', _pad(this.getUTCMonth() + 1), '-', _pad(this.getUTCDate()), 'T'
, _pad(this.getUTCHours()), ':', _pad(this.getUTCMinutes()), ':', _pad(this.getUTCSeconds()), '.', _padMillisecond(this.getUTCMilliseconds()), 'Z'].join('');
};
// 复制可枚举的类成员
for (var clsProp in _nativeDate){
if (_nativeDate.hasOwnProperty(clsProp)){
Date[clsProp] = _nativeDate[clsProp];
}
}
// 复制不可枚举的类成员
var innumerableMems = ['UTC'];
for (var i = 0, clsProp; clsProp = innumerableMems[i++];){
Date[clsProp] = _nativeDate[clsProp];
}
Date.parse = function(str){
if (['string', 'number'].indexOf(typeof str) === -1) return NaN;
var isMatch = regExp.test(str), milliseconds = 0;
if (!isMatch) return _nativeDate.parse(str);
var tmpRet = parseISOString2UTC();
return _nativeDate.UTC(tmpRet.year, tmpRet.month, tmpRet.date, tmpRet.hours, tmpRet.minutes, tmpRet.seconds, tmpRet.milliseconds);
};
Date.now = Date.now
|| function(){
return +new this();
};
}
五、总结
上述实现相对es5-shim来讲考虑的地方仍有欠缺,这源于我对日期时间格式的理解不够完整,因此请大家多多见谅。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 什么是Python Wheels?为什么要关心它?
- Nginx fastcgi_cache权威指南
- 玩转dockerfile
- redis实战第六篇 手动创建redis cluster
- docker容器入门最佳教程
- redis实战第五篇 jedis 连接 redis sentinel详解
- redis实战第四篇 手动容灾故障转移记录
- 10条很棒的Python一行代码
- 如何在一个Docker中同时运行多个程序进程?
- 如何在CentOS / RHEL 7上启用IPv6
- Golang中的RegExp正则表达式用法指南
- Golang glog使用详解
- kubernetes使用securityContext和sysctl
- 浅谈分词算法基于字的分词方法(HMM)
- 优雅的重启服务