# 日期处理
[toc]
记录一些日期处理中的操作吧, 毕竟我也是好几次遇到同事写的代码,从来不考虑闰年2月份,不考虑周日的getDay
返回0的问题。举个例子, 比如获取从今天往前一年的时间范围,如果今天是 2020-02-29
, 有些人一顿操作就返回了 2019-02-29
或者2019-02-30
这样子的日期。这是非常不负责任的。
很多时候操作时间的加减,最好还是采用时间戳来操作。对于Date类和实例的基本方法就不说了,注意几个点吧
WARNING
Date.prototype.getMonth()
返回的是0-11, 0表示一月份Date.prototype.getDay()
返回表示一周中的第几天,(0-6), 0表示星期日
# 1、new Date()获取的是什么时间
new Date()
获取的是你js运行时那台机器的系统时间。比如你在中国, 我们一般以东八区的时间作为系统时间, 所以我们获取的就是东八区的时间。
对于日期的处理, 很多时候我们绕不过moment.js,但是很多时候, 日期处理,我们自己处理反而会显得更加轻量。比如日期格式化,虽然moment的这种模式的方式很强,普适性很好, 但是普适性好往往意味着需要更多的判断和时间。很多时候, 我们的项目可能往往只需要 yyyy-MM-dd
这样子的时间, 所以我们特殊自己处理即可。
下面分享一些常用的处理方式,不一定特别对, 毕竟测试用例写的不多。
# 2、判断是不是闰年
闰年是每隔4年出现一次,闰年的那一年是有366天。二月份存在29号。
非整百年:能被4整除的为闰年。 整百年:能被400整除的是闰年。
/**
* 判断是否是闰年
*
* @param {number} year
* @return {boolean}
*/
function isLeapYear(year) {
if (!year) return false;
if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
return true;
}
return false;
}
# 3、获取一个月的天数
大部分时候, 一个月有多少天是固定的, 比如1 3 5 7 8 10 12 就是31天, 4 6 9 11 就是30天,但是2月份是28还是29就看是不是闰年了。
不过js有更好的办法,比如你想获取3月份有几天, 我们知道有31天, 也就是3月31号, 其实我们给Date传入4月0号, 就可以获取到3月31号.
也就是说下个月的0号就是这个月的最后一天
/**
* 获取每个月有多少天
*
* @param {number} month [1-12]
* @return {number}
*/
function getDaysOfMonth(year, month) {
const date = new Date(year, month - 1 + 1, 0);
return date.getDate();
}
# 4、格式化日期
这个没有采取传入模式的方式, 我觉得自定义就好
function formateDate(dateIns) {
const year = dateIns.getFullYear();
let month = pad(dateIns.getMonth() + 1);
let date = pad(dateIns.getDate());
return `${year}-${month}-${date}`;
}
function pad(n, len = 2) {
n = n.toString();
while (n.length < len) {
n = '0' + n;
}
return n;
}
# 5、一天的毫秒数
这个可以定义一个常量, 毕竟经常需要使用到。
// 一天的毫秒数
const MillisecondsForOneDay = 24 * 60 * 60 * 1000;
# 6、获取所在周的周一
传入一个日期,获取这个日期所在星期的星期一
/**
* 获取日期所在周的周一
*
* @param {Date} date
* @return {Date}
*/
function weekFirstDay(date) {
let weekDay = date.getDay();
if (weekDay === 0) {
weekDay = 7;
}
const WeekFirstDay = new Date(date.getTime() - (weekDay - 1) * MillisecondsForOneDay);
return WeekFirstDay
}
# 7、获取所在周的周日
/**
* 获取日期所在周的周日
*
* @param {Date} date
* @return {Date}
*/
function weekLastDay(date) {
let weekDay = date.getDay();
if (weekDay === 0) {
weekDay = 7;
}
const WeekLastDay = new Date(date.getTime() + (7 - weekDay) * MillisecondsForOneDay);
return WeekLastDay;
}
# 8、获取日期所在月份的第一天和最后一天
/**
* 获取所在月份的第一天
*
* @param {Date} date
Date @return {*}
*/
function monthFirstDay(date) {
return new Date(date.getFullYear(), date.getMonth(), 1);
}
/**
* 获取所在月份的最后一天
*
* @param {Date} date
Date @return {*}
*/
function monthLastDay(date) {
return new Date(date.getFullYear(), date.getMonth() + 1, 0);
}
# 9、获取日期所在季度的第一天和最后一天
/**
* 获取日期所在季度的第一天
*
* @param {Date} date
* @return {Date}
*/
function quarterFirstDay(date) {
const year = date.getFullYear();
const month = date.getMonth();
const quarterFirstMonth = parseInt(month / 3) * 3 + 1; // 1 | 4 | 7 | 10
return new Date(year, quarterFirstMonth - 1, 1);
}
/**
* 获取日期所在季度的最后一天
*
* @param {Date} date
* @return {Date}
*/
function quarterLastDay(dateIns) {
const year = dateIns.getFullYear();
const month = dateIns.getMonth();
const quarterLastMonth = (parseInt(month / 3) + 1) * 3; // 3 | 6 | 9 | 12
// 写成 - 1 + 1可能更好理解, 毕竟这个月的最后一天就是下个月的0号
return new Date(year, quarterLastMonth - 1 + 1, 0);
}
# 10、获取一周前的一天
这个一般可以用于查询一些时间范围, 比如近一周的数据
/**
* 获取一周前的一天
*
* @param {Date} date
* @return {Date}
*/
function AWeekAgo(date) {
const timeStamp = date.getTime();
return new Date(timeStamp - 6 * MillisecondsForOneDay);
}
# 11、获取N个月前的时间
/**
* 获取N个月前的时间, 比如 3.13 获取到 2.14
*
* @param {Date} dateIns
* @param {number} [n=1]
* @return {Date}
*/
function getNMonthAgo(dateIns, n = 1) {
let year = dateIns.getFullYear();
const month = dateIns.getMonth() + 1; // [1-12]
let date = dateIns.getDate();
let targetMonth = month - n;
// 一月份的前一个就是上一年的12月
while (targetMonth <= 0) {
console.log(targetMonth);
targetMonth = 12 + targetMonth;
year--;
}
// 如果目标月份是2月, 且日期大于28, 判断闰年平年
if (targetMonth === 2 && date > 28) {
if (isLeapYear(year)) {
date = 29;
} else {
date = 28;
}
return new Date(year, targetMonth - 1, date);
}
const monthsAgoDate = new Date(year, targetMonth - 1, date).getTime();
return new Date(monthsAgoDate + MillisecondsForOneDay);
}
# 12、时区问题
有时候在处理一些时区问题时, 比如我们目前是东八区, new Date()
即可本地的时间, 但是这时候我想获取其他区在此刻的时间,比如东9区。
假设我们东八区现在是2022-01-01 08:00:00
, 那么东九区就是2022-01-01 09:00:00
, 我们怎么去获取此刻东九区的时间,看下面的函数
/**
* 获取其他时区的日期
*
* @param {number} offset 比如东八区就传入8 西8区传入-8 就可以获得对应时区的时间
* @return {Date}
*/
function getZoneTime(offset) {
const d = new Date();
const localTime = d.getTime();
// getTimezoneOffset()返回是以分钟为单位,需要转化成ms。
// 如果是东八区,getTimezoneOffset() 返回 -480 分钟
const localOffset = d.getTimezoneOffset() * 60 * 1000;
// 本地时间加上本地的时区偏移量, 即可拿到UTC时间
const utc = localTime + localOffset;
// UTC时间 加上你需要偏移的时区,即可拿到那个时区对应的时间。
return new Date(utc + 60 * 60 * 1000 * offset);
}
# 13、UTC时间
这个就是格林威治时间,0时区的时间。
Date.UTC()
方法接受的参数同日期构造函数接受最多参数时一样,返回从 1970-1-1 00:00:00 UTC 到指定日期的的毫秒数。
有时候我们有一个服务于全球各个地方的服务, 想在某一个时刻彻底不提供某一个服务, 这个时候我们可以使用Date.UTC
, 比如
Date.UTC(2022, 7 - 1, 1, 0, 0, 0, 0); // 1656633600000
就是返回UTC时间的2022年7月1日距离1970年1月1日的时间戳, 值为1656633600000。 无论你在哪个时区调用都是返回这个时间。可以做一些全球统一的操作。
# 14、set 函数
还有很多比如 Date.prototype.setFullYear()
, Date.prototype.setMonth()
, Date.prototype.setDate()
这样子的Api可供选择使用
# 15、获取昨天和明天
使用加减日期的办法比时间戳的方法更好。
export const prevDate = function(date, amount = 1) {
return new Date(date.getFullYear(), date.getMonth(), date.getDate() - amount);
};
export const nextDate = function(date, amount = 1) {
return new Date(date.getFullYear(), date.getMonth(), date.getDate() + amount);
};
# 16、获取每个月的日历,对应星期
/**
*
*
* @param {Number} year
* @param {Number<0-11>} month
* @return Array<Date>
*/
function getMonthDate(year, month) {
const firstDate = new Date(year, month, 1, 0, 0, 0, 0);
let dayOfWeek = firstDate.getDay();
if (dayOfWeek === 0) {
dayOfWeek = 7;
}
const preDateList = [];
for (let i = 1, len = dayOfWeek; i < len; i++) {
const d = new Date(year, month, 1 - i, 0, 0, 0, 0);
preDateList.unshift({
dateIns: d
});
}
const nextDateList = [];
for (let i = 0, len = 42 - preDateList.length; i < len; i++) {
const d = new Date(year, month, 1 + i, 0, 0, 0, 0);
nextDateList.push({
dateIns: d,
format: formateDate(d)
});
}
console.log(nextDateList);
return preDateList.concat(nextDateList);
}
function pad(n, len = 2) {
n = n.toString();
while (n.length < len) {
n = '0' + n;
}
return n;
}
function formateDate(dateIns) {
const year = dateIns.getFullYear();
let month = pad(dateIns.getMonth() + 1, 2);
let date = pad(dateIns.getDate(), 2);
return `${year}-${month}-${date}`;
}
# 16.1 如果把周日放在第一个
可以使用下面的逻辑
function getMonthDate(year, month) {
const firstDate = new Date(year, month, 1, 0, 0, 0, 0);
let dayOfWeek = firstDate.getDay();
// if (dayOfWeek === 0) {
// dayOfWeek = 7;
// }
const preDateList = [];
for (let i = 1, len = dayOfWeek; i <= len; i++) {
const d = new Date(year, month, 1 - i, 0, 0, 0, 0);
preDateList.unshift({
dateIns: d
});
}
const nextDateList = [];
for (let i = 0, len = 42 - preDateList.length; i < len; i++) {
const d = new Date(year, month, 1 + i, 0, 0, 0, 0);
nextDateList.push({
dateIns: d,
format: formateDate(d)
});
}
// console.log(nextDateList);
return preDateList.concat(nextDateList);
}
function pad(n, len = 2) {
n = n.toString();
while (n.length < len) {
n = '0' + n;
}
return n;
}
function formateDate(dateIns) {
const year = dateIns.getFullYear();
let month = pad(dateIns.getMonth() + 1, 2);
let date = pad(dateIns.getDate(), 2);
return `${year}-${month}-${date}`;
}
# 17、判断8位数字是否是一个合格的日期格式
/**
* 判断8位数字是否是一个合格的日期格式
*
* @param {string} [dateStr='']
* @return {*}
*/
function validDataFormat(dateStr = '') {
if (!dateStr) return false;
dateStr = String(dateStr);
const dateReg = /^\d{8}$/; // 日期格式为8位数字Ï
if (!dateReg.test(dateStr)) return false;
const formatDateStr = dateStr.replace(/^(\d{4})(\d{2})(\d{2})$/, '$1/$2/$3');
// console.debug(formatDateStr);
const parsedDate = Date.parse(formatDateStr);
if (Number.isNaN(parsedDate)) return false;
// console.debug('----', new Date(parsedDate).toLocaleDateString());
return true;
}
← 类型判断 Date格式化和解析 →