首页 > 技术文章 > 用JavaScript校验日期的合法性

gymmer 2017-05-25 13:43 原文

校验表单时可能会遇到校验日期是否正确。可以利用JS的内置对象Date帮助我们完成日期校验。

思路是首先用被校验日期(假设为A,可能为字符串或数字)创建一个Date对象(假设为B)。
然后判断A和B的年、月、日是否全部相等。如果是,说明A是合法的;否则,A的范围有误。

用代码表示为:

// 被校验日期A:2008年3月15日
var year = 2008,
    month = 3,
    date = 15;

// 创建Date对象B
var B = new Date(year, month-1, date);

// 判断A和B的年月日是否全部相等
if (year === B.getFullYear() && month === B.getMonth()+1 && date === B.getDate()) {
    // 全部相等,被校验日期合法
} else {
    // 不全部相等,被校验日期的年、月、日至少有一个错误。
}

这里容易踩坑的地方是,Date对象月份从零开始:0表示一月,1表示二月,...,11表示十二月。

为什么要构建一个Date对象?这就要涉及到Date构造函数的语法了。

官网上上给出了四种调用构造函数的形式:

  • 第一种:new Date()
  • 第二种:new Date(value)
  • 第三种:new Date(dateString)
  • 第四种:new Date(new Date(year, month[, date[, hours[, minutes[, seconds[, milliseconds]]]]]);

解释如下:

  1. 第一种:根据系统设置,返回当前日期时间。
  2. 第二种:value为整数型,表示从1970年1月1日 00:00:00 距现在所过去的毫秒数。
  3. 第三种:dateString为字符串型,其格式需满足指定格式。
  4. 第四种:各参数都是整数型。

当拿到了被校验日期的年、月、日之后,计算毫秒数或构造日期格式字符串都比较麻烦。所以这里使用第四种。

这时,调用Date构造函数的另一条规则就起作用了:如果参数值超过了合理范围,则它会被调整到临近值。我理解的就是,构造函数会为不合理的参数做进位运算。

举个例子,new Date(2013,13,1)相当于new Date(2014,1,1),也就是 2013年14月1日被进位成 2014年2月1日

再回到之前的校验代码:

// 被校验日期A:2013年14月1日
var year = 2013,
    month = 14,
    date = 1;

// 创建Date对象B
var B = new Date(2013, 13, 1);  
// B --> new Date(2014, 1, 1);
// B.getFullYear() === 2014
// B.getMonth() === 1
// B.getDate() === 1

// 可以看到,year !== B.getFullYear() 并且 month !== B.getMonth()
// 所以判断出被校验日期是非法的。

再增强一下我们的程序,使之可以判断多种格式的被校验日期:

  • 年、月、日,例如(2008, 3, 15)、('2008', '3', '15')
  • 特殊字符分割的字符串,例如('2008-03-15', '-')、('2008/3/15', '/')
  • 连续8位的数字格式,例如'20080315'、20080315

我写了一段简易的代码,仅供参考:

function isValidDate() {
    var year = 0,
        month = 0,
        date = 0;

    if (arguments.length === 3) {
        // 2008 3 15
        // '2008' '3' '15'
        year = Number(arguments[0]);
        month = Number(arguments[1]);
        date = Number(arguments[2]);
    } else if (arguments.length === 2) {
        // '2008-03-15' '-'
        // '2008/3/15' '/'
        // 参数必须为字符串类型,分隔符不能为空字符串或数字
        var str = arguments[0],
            seperator = arguments[1],
            dateArray = str.split(seperator);
        year = Number(dateArray[0]);
        month = Number(dateArray[1]);
        date = Number(dateArray[2]);
    } else if (arguments.length === 1) {
        // '20080315'
        // 20080315
        // 参数必须为8位,且只能为数字
        var str = String(arguments[0]);
        year = Number(str.slice(0, 4));
        month = Number(str.slice(4, 6));
        date = Number(str.slice(6));
    }
    var dateObj = new Date(year, month-1, date),
        nYear = dateObj.getFullYear(),
        nMonth = dateObj.getMonth() + 1,
        nDate = dateObj.getDate();
    
    if (year === nYear && month === nMonth && date === nDate) {
        return true;
    } else {
        return false;
    }
}

// 测试
isValidDate.assert = function(value) {
    if (value) {
        console.log('pass');
    } else {
        console.log('failed');
    }
}

isValidDate.test = function() {
    var assert = isValidDate.assert;
    
    assert(isValidDate(2008, 2, 29)===true);
    assert(isValidDate(2008, 3, 33)===false);
    assert(isValidDate(2017, 13, 01)===false);
    assert(isValidDate(2018, 5, 12)===true);
    assert(isValidDate('2008-2-29','-')===true);
    assert(isValidDate('2008/02/29','/')===true);
    assert(isValidDate('2008 03 17',' ')===true);
    assert(isValidDate('2008,13,01',',')===false);
    assert(isValidDate('2008,12,01',',')===true);
    assert(isValidDate('20080229')===true);
    assert(isValidDate('20081329')===false);
    assert(isValidDate('20081131')===false);
    assert(isValidDate(20080229)===true);
    assert(isValidDate(20081329)===false);
    assert(isValidDate(20081131)===false);
}

isValidDate.test();

推荐阅读