ydr-util
Version:
611 lines (548 loc) • 15.9 kB
JavaScript
/*!
* 日期
* @author ydr.me
* @create 2014-11-19 10:29
*/
/**
* @module util/date
* @requires util/dato
* @requires util/typeis
*/
;
var dato = require('./dato.js');
var typeis = require('./typeis.js');
var weeks = '日一二三四五六';
var monthDates = [31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
var REG_RANGE = /^(this|in)\s+?(?:(\d+)\s+?)?(day|week|month|year)s?$/i;
/**
* 格式化日期<br>
* 主要参考ECMA规范定义:YYYY-MM-DDTHH:mm:ss.sssZ<br>
* 其他参数参考自moment<br>
*
* @param {String} format 格式化字符串<br>
* 假设当前时间为:2014年1月1日 19点9分9秒9毫秒 周三<br>
* <strong>日期</strong><br>
* 至少4位年份<code>YYYY</code> 2014<br>
* 至少2位年份<code>YY</code> 14<br>
* 至少2位月份<code>MM</code> 01<br>
* 至少1位月份<code>M</code> 1<br>
* 至少2位日期<code>DD</code> 01<br>
* 至少1位日期<code>D</code> 1<br>
*
* <strong>时间</strong><br>
* 至少2位24小时制小时<code>HH</code> 19<br>
* 至少1位24小时制小时<code>H</code> 19<br>
* 至少2位12小时制小时<code>hh</code> 07<br>
* 至少1位12小时制小时<code>h</code> 7<br>
* 至少2位分钟数<code>mm</code> 09<br>
* 至少1位分钟数<code>m</code> 9<br>
* 至少2位秒数<code>ss</code> 09<br>
* 至少1位秒数<code>s</code> 9<br>
* 至少3位毫秒数<code>SSS</code> 009<br>
* 至少2位毫秒数<code>SS</code> 09<br>
* 至少1位毫秒数<code>S</code> 9<br>
*
* <strong>时段</strong><br>
* 星期<code>e</code> 三<br>
* 上下午<code>a</code> 下午<br>
*
* @param {Date|Object|Number|String} [date] 日期
* @param {Object} [config] 格式配置
* @returns {null|string}
*
* @example
* // 默认的格式化
* date.format('YYYY年MM月DD日 HH:mm:ss.SSS 星期e a');
* // => "2014 年"
* date.format('YYYY 年');
* // => "2014 年"
*
* // 自定义格式化
* var month = {
* "01": "January",
* "02": "February",
* "03": "March",
* "04": "April",
* "05": "May",
* "06": "June",
* "07": "July",
* "08": "August",
* "09": "September",
* "10": "October",
* "11": "November",
* "12": "December",
* };
* date.format('YYYY年MM月DD日 HH:mm:ss.SSS 星期e a', {
* // 要替换的字段,以及要替换的值
* 'MM': month
* });
* // => "2014年October月04日 17:28:06.363 星期六 下午"
*/
exports.format = function (format, date, config) {
if (typeis(format) !== 'string') {
throw new Error('date format must be a string');
}
if (typeis(arguments[1]) === 'object') {
config = arguments[1];
date = new Date();
}
format = format || 'YYYY-MM-DD HH:mm:ss www';
date = exports.parse(date);
config = config || {};
var Y = String(date.getFullYear());
var M = String(date.getMonth() + 1);
var D = String(date.getDate());
var H = String(date.getHours());
var h = H > 12 ? H - 12 : H;
var a = H > 12 ? 0 : 1;
var m = String(date.getMinutes());
var s = String(date.getSeconds());
var S = String(date.getMilliseconds());
var e = String(date.getDay());
var formater = [
{
key: 'YYYY',
val: Y,
is: 'Y'
},
{
key: 'YY',
val: Y.slice(-2),
is: 'Y'
},
{
key: 'MM',
val: dato.fillNumber(M, 2),
is: 'M'
},
{
key: 'M',
val: M,
is: 'M'
},
{
key: 'DD',
val: dato.fillNumber(D, 2),
is: 'D'
},
{
key: 'D',
val: D,
is: 'D'
},
{
key: 'HH',
val: dato.fillNumber(H, 2),
is: 'H'
},
{
key: 'H',
val: H,
is: 'H'
},
{
key: 'hh',
val: dato.fillNumber(h, 2),
is: 'h'
},
{
key: 'h',
val: h,
is: 'h'
},
{
key: 'mm',
val: dato.fillNumber(m, 2),
is: 'm'
},
{
key: 'm',
val: m,
is: 'm'
},
{
key: 'ss',
val: dato.fillNumber(s, 2),
is: 's'
},
{
key: 's',
val: s,
is: 's'
},
{
key: 'SSS',
val: dato.fillNumber(S, 3),
is: 'S'
},
{
key: 'SS',
val: dato.fillNumber(S, 2),
is: 'S'
},
{
key: 'S',
val: S,
is: 'S'
},
{
key: 'e',
val: weeks[e],
is: 'e'
},
{
key: 'a',
val: a ? '上午' : '下午',
is: 'a'
}
];
var hasFormat = {};
var configFormater = [];
// 年、月、日、时、分、秒、毫秒、星期、上下午
// 只保证每个字段只被格式化一次,防止误操作
dato.each(formater, function (index, fmt) {
var reg = new RegExp(fmt.key, 'mg');
if (!hasFormat[fmt.is]) {
if (config[fmt.key]) {
hasFormat[fmt.is] = !0;
configFormater.push({
key: fmt.key,
val: config[fmt.key][fmt.val],
is: fmt.is
});
} else {
if (reg.test(format)) {
hasFormat[fmt.is] = !0;
format = format.replace(reg, fmt.val);
}
}
}
});
// 自定义格式化
dato.each(configFormater, function (index, fmt) {
var reg = new RegExp(fmt.key, 'mg');
format = format.replace(reg, fmt.val);
});
return format;
};
/**
* 解析时间
* @param {String|Date} string 时间字符串
* @param {Date} [dftDate] 如果前置时间不合法,默认时间,默认为现在
* @returns {Date}
*
* @example
* date.parse('12/21/2014 12:21:22');
* // => Sun Dec 21 2014 12:21:22 GMT+0800 (CST)
*/
exports.parse = function (string, dftDate) {
var date;
dftDate = dftDate || new Date();
switch (typeis(string)) {
case 'string':
case 'number':
date = string;
break;
case 'date':
return string;
default :
return dftDate;
}
return typeis.validDate(date) ? new Date(date) : dftDate;
};
/**
* 是否为闰年
* @param {Number} year 年份
* @returns {boolean}
*
* @example
* date.isLeapYear(2014);
* // => false
*/
exports.isLeapYear = function (year) {
return (year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0));
};
/**
* 获得某年某月的天数
* @param {Number} year 年
* @param {Number} month 月份,默认序列月,即1月为第0月
* @param {Boolean} [isNatualMonth=false] 是否为自然月,默认为 false
* @returns {Number} 天数
*
* @example
* // 获得10月份的天数
* date.getDaysInMonth(2014, 9);
* // => 31
*/
exports.getDaysInMonth = function (year, month, isNatualMonth) {
month = isNatualMonth ? month - 1 : month;
month = new Date(year, month).getMonth();
return month === 1 ? (exports.isLeapYear(year) ? 29 : 28) : monthDates[month];
};
/**
* 获得某年某月某日在当年的第几天
* @param {Number} year 年份
* @param {Number} month 默认序列月
* @param {Number} date 日期
* @param {Boolean} [isNatualMonth=false] 是否自然月,默认 false
* @returns {Number}
*/
exports.getDaysInYear = function (year, month, date, isNatualMonth) {
month = isNatualMonth ? month - 1 : month;
var days = date;
while (month--) {
days += exports.getDaysInMonth(year, month);
}
return days;
};
/**
* 计算某年某月某日是当年的第几周
* @param {Number} year 年
* @param {Number} month 月
* @param {Number} date 日
* @param {Boolean} [isNatualMonth] 是否为自然月
* @returns {number}
*
* @example
* // 判断2014年10月24日是今年的第几周
* date.getWeeksInYear(2014, 9, 24);
* // => 43
*/
exports.getWeeksInYear = function (year, month, date, isNatualMonth) {
month = isNatualMonth ? month - 1 : month;
var pastDate = date + new Date(year, 0, 1).getDay();
var i = 0;
for (; i < month; i++) {
pastDate += exports.getDaysInMonth(year, i);
}
return Math.ceil(pastDate / 7);
};
/**
* 计算某年某月某日是当月的第几周
* @param {Number} year 年
* @param {Number} month 月
* @param {Number} date 日
* @param {Boolean} [isNatualMonth=false] 是否为自然月
* @returns {number}
*
* @example
* // 判断2014年10月24日是当月的第几周
* date.getWeeksInMonth(2014, 9, 24);
* // => 4
*/
exports.getWeeksInMonth = function (year, month, date, isNatualMonth) {
month = isNatualMonth ? month - 1 : month;
var pastDate = date + new Date(year, month, 1).getDay();
return Math.ceil(pastDate / 7);
};
/**
* 人性化比较时间时间
* @param {String|Number|Date} date 比较时间
* @param {String|Number|Date} [date] 被比较时间,默认为当前时间
* @returns {string}
*
* @example
* // 过去时间
* date.from(Date.now() - 1);
* // => "刚刚"
* date.from(Date.now() - 10*1000);
* // => "10秒前"
* date.from(Date.now() - 61*1000);
* // => "1分钟前"
* date.from(Date.now() - 60*60*1000);
* // => "1小时前"
* date.from(Date.now() - 24*60*60*1000);
* // => "1天前"
* date.from(Date.now() - 30*24*60*60*1000);
* // => "1个月前"
* date.from(Date.now() - 12*30*24*60*60*1000);
* // => "1年前"
* date.from(Date.now() - 20*12*30*24*60*60*1000);
* // => "19年前"
* date.from(Date.now() - 100*12*30*24*60*60*1000);
* // => "很久之前"
*
* // 将来时间
* date.from(Date.now() + 1);
* // => "即将"
* date.from(Date.now() + 10*1000);
* // => "10秒后"
* date.from(Date.now() + 61*1000);
* // => "1分钟后"
* date.from(Date.now() + 60*60*1000);
* // => "1小时后"
* date.from(Date.now() + 24*60*60*1000);
* // => "1天后"
* date.from(Date.now() + 30*24*60*60*1000);
* // => "1个月后"
* date.from(Date.now() + 12*30*24*60*60*1000);
* // => "11个月后"
* date.from(Date.now() + 20*12*30*24*60*60*1000);
* // => "19年后"
* date.from(Date.now() + 100*12*30*24*60*60*1000);
* // => "98年后"
* date.from(Date.now() + 200*12*30*24*60*60*1000);
* // => "很久之后"
*/
exports.from = function (date, compareDate) {
compareDate = compareDate || new Date();
compareDate = exports.parse(compareDate);
var old = exports.parse(date);
var oldTime;
var diff;
var seconds;
var minutes;
var hours;
var days;
var months;
var years;
var isInFeature;
if (!old || !compareDate) {
return '未知';
}
oldTime = old.getTime();
// 小于 1970年1月1日 08:00:00
if (oldTime <= 0) {
return '很久之前';
} else if (oldTime >= Number.MAX_VALUE) {
return '很久之后';
}
diff = compareDate.getTime() - oldTime;
isInFeature = diff < 0;
diff = isInFeature ? -diff : diff;
seconds = Math.floor(diff / 1000);
minutes = Math.floor(diff / (1000 * 60));
hours = Math.floor(diff / (1000 * 60 * 60));
days = Math.floor(diff / (1000 * 60 * 60 * 24));
years = Math.abs(compareDate.getFullYear() - old.getFullYear());
months = isInFeature ?
years * 12 - compareDate.getMonth() + old.getMonth() :
years * 12 + compareDate.getMonth() - old.getMonth();
years -= (isInFeature ? 1 : 0);
// < 10s
if (seconds < 10) {
return isInFeature ? '即将' : '刚刚';
}
// < 60s
else if (minutes < 1) {
return seconds + '秒' + (isInFeature ? '后' : '前');
}
// < 1h
else if (hours < 1) {
return minutes + '分钟' + (isInFeature ? '后' : '前');
}
// < 1d
else if (days < 1) {
return hours + '小时' + (minutes % 60 ? minutes % 60 + '分钟' : '') + (isInFeature ? '后' : '前');
}
// < 1M
else if (months < 1) {
return days + '天' + (isInFeature ? '后' : '前');
}
// < 1Y
else if (years < 1) {
return months + '个月' + (isInFeature ? '后' : '前');
}
// < 100Y
else if (years < 100) {
return years + '年' + (isInFeature ? '后' : '前');
}
return '很久之' + (isInFeature ? '后' : '前');
};
/**
* 计算日期范围
* @param type {String} 可选
* `this N days`/`this N weeks`/`this N months`/`this N years` 当日/周/月/年
* `in Ndays`/`in N weeks`/`in N months`/`in N years` 几日/周/月/年内
* @param [ref] {Date} 起点时间,默认为当前时间
* @returns {{from: Date, to: Date}}
*
* @example
* // this
* date.range('this 1 day');
* // 本周日 - 本周一
* date.range('this 1 week');
* // 本月一 - 本月末
* date.range('this 1 month');
* // 本年头 - 本年尾
* date.range('this 1 year');
*
* // in
* date.range('in 1 day');
* // 今天 - 一周内
* date.range('in 1 week');
* // 今天 - 下个月今天
* date.range('in 1 month');
* // 今天 - 明年今天
* date.range('in 1 year');
*/
exports.range = function (range, ref) {
range = String(range).toLowerCase();
var temp = range.match(REG_RANGE);
if (!temp) {
throw new Error('date range error');
}
var type = temp[1];
var value = dato.parseInt(temp[2] || 1);
var scope = temp[3];
var oneDayMilliseconds = 1 * 24 * 60 * 60 * 1000;
var from = exports.parse(ref);
var thisYear = from.getFullYear();
var thisMonth = from.getMonth();
var thisDate = from.getDate();
var thisDay = from.getDay();
// 今日 00:00:00:000
from.setHours(0);
from.setMinutes(0);
from.setSeconds(0);
from.setMilliseconds(0);
// 昨日 23:59:59:999
var to = new Date(from.getTime() - 1);
switch (type) {
case 'this':
switch (scope) {
case 'day':
to.setDate(thisDate + value - 1);
break;
case 'week':
from.setDate(thisDate + (0 - thisDay));
to.setDate(thisDate + (6 - thisDay) + (value - 1) * 7);
break;
case 'month':
from.setDate(1);
to.setMonth(thisMonth + value, 1);
to = new Date(to.getTime() - oneDayMilliseconds);
break;
case 'year':
from.setMonth(0);
from.setDate(1);
to.setFullYear(thisYear + value - 1);
to.setMonth(12, 1);
to = new Date(to.getTime() - oneDayMilliseconds);
break;
}
break;
case 'in':
switch (scope) {
case 'day':
to.setDate(thisDate + value - 1);
break;
case 'week':
to.setDate(thisDate + value * 7);
break;
case 'month':
to.setMonth(thisMonth + value, thisDate);
to = new Date(to.getTime() - oneDayMilliseconds);
break;
case 'year':
to.setFullYear(thisYear + value, thisMonth, thisDate);
to = new Date(to.getTime() - oneDayMilliseconds);
break;
}
break;
}
return {
from: from,
to: to
};
};