# 日期处理

[toc]

记录一些日期处理中的操作吧, 毕竟我也是好几次遇到同事写的代码,从来不考虑闰年2月份,不考虑周日的getDay返回0的问题。举个例子, 比如获取从今天往前一年的时间范围,如果今天是 2020-02-29, 有些人一顿操作就返回了 2019-02-29或者2019-02-30这样子的日期。这是非常不负责任的。

很多时候操作时间的加减,最好还是采用时间戳来操作。对于Date类和实例的基本方法就不说了,注意几个点吧

WARNING

  1. Date.prototype.getMonth() 返回的是0-11, 0表示一月份
  2. 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;
}
上次更新: 1/22/2025, 9:39:13 AM