一起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来讲考虑的地方仍有欠缺,这源于我对日期时间格式的理解不够完整,因此请大家多多见谅。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏coolblog.xyz技术专栏

Spring IOC 容器源码分析 - 创建原始 bean 对象

本篇文章是上一篇文章(创建单例 bean 的过程)的延续。在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程。本篇文章,我们就从战术的层...

22250
来自专栏java、Spring、技术分享

深入分析Spring Formatter

  在Web项目中,通常需要将数据转换为具有某种格式的字符串进行展示,因此Spring3引入了格式化转换器(Formatter SPI) 和格式化服务API(F...

16630
来自专栏ml

Guava------------Cache使用方法

    简单从这几个方面描述一下如何使用Cache,对Cache的各种原理介绍此处不涉及.     1.使用场景     2.如何使用Cache     3.创...

87080
来自专栏小灰灰

Java容器篇小结之Map自问自答

采用问答的方式对常见的问题进行整理小结 I. Map篇 0. 什么是Map 看到这个有点懵逼,一时还真不知道怎么解释,能让完全没有接触过的人都能听懂 想到生活...

209100
来自专栏软件开发 -- 分享 互助 成长

二分查找

二分查找又称折半查找,优点是比较次数少,查找速度快,平均性能好;其缺点是要求待查表为有序表,且插入删除困难。因此,折半查找方法适用于不经常变动而查找频繁的有序列...

22470
来自专栏iOS技术杂谈

iOS runtime探究(四): 从runtiem开始实践Category添加属性与黑魔法method swizzling你要知道的runtime都在这里

你要知道的runtime都在这里 转载请注明出处 https://cloud.tencent.com/developer/user/1605429 本文主要讲解...

34560
来自专栏从流域到海域

《笨办法学Python》 第39课手记

《笨办法学Python》 第39课手记 本节课讲列表的操作,用来做练习的代码中出现了之前用到过的几个函数,是一节复习课。你需要记住它们。 原代码如下: ten_...

21870
来自专栏java一日一条

关于Java集合的小抄

在尽可能短的篇幅里,将所有集合与并发集合的特征,实现方式,性能捋一遍。适合所有”精通Java”其实还不那么自信的人阅读。

7610
来自专栏WindCoder

Java漫谈-容器

除并发应用,Queue在Java SE5中仅有两个实现 LinkedList和PriorityQueue,差异在于排序行为,而不是性能。

9210
来自专栏飞扬的花生

js笔记

1.克隆对象 克隆数组: var country=['中国','美国']; var copyCountry=country.slice(0); 克隆对象: va...

24060

扫码关注云+社区

领取腾讯云代金券