ISO WEEK NUM 实现

项目要求实现 ISOWEEKNUM 函数, 网上查找资料了解到是一种周数计算方式, 称为 ISO标准周. Excel里面就有直接提供此函数.

在一些博客和GitHub上面找到的几个实现, 经过测试似乎都并不正确. 跟Excel计算出来的结果并不一样.

无奈之下只好放弃了复制粘贴大法, 自己去了解了一下ISOWEEKNUM的定义, 并做出了以下实现:

/**
 * 计算某日期在全年中的周数, 结果要符合ISO标准周的定义
 *
 * @param {Date} date 要计算的日期
 * @returns {number} 周数
 */
var ISOWEEKNUM = function (date) {
  var currentDate = new Date(date);
  var firstOfCurrentYear = setMonthAndDate(date, 0, 1);
  var isoWeekNumber = 0;

  // 判断当前年的第一天与date对象所指的日期的关系
  if ((firstOfCurrentYear.getDay() > 4)
    // 这里是两个 `与` 符号, 不知道为什么被转成了这样
    && (currentDate.getMonth() === 0)
    && (currentDate.getDate() - firstOfCurrentYear.getDate()) < 3) {
    firstOfCurrentYear = new Date(firstOfCurrentYear
      .setFullYear(firstOfCurrentYear.getFullYear() - 1));
  }
  // 循环叠加, 计算周数
  while (firstOfCurrentYear.getTime() <= currentDate.getTime()) {
    firstOfCurrentYear = addDate(firstOfCurrentYear, 7);
    ++isoWeekNumber;
  }
  return isoWeekNumber;
};

/**
 * 设置月份&日期
 *
 * @functional
 * @param {Date} originDate 原日期
 * @param {number} monthNumber 设置的月份
 * @param {number} dateNumber 设置的日期(月中的日期)
 * @returns {Date} 新的日期
 */
var setMonthAndDate = function (originDate, monthNumber, dateNumber) {
  var _ = new Date(new Date(originDate).setMonth(monthNumber));
  return new Date(_.setDate(dateNumber));
};

/**
 * 基于 originDate 添加天数并返回新的 Date
 *
 * @functional
 * @param {Date} originDate 原日期
 * @param {number} incNumber 添加的日期数
 * @returns {Date} 新的日期
 */
var addDate = function (originDate, incNumber) {
  return new Date(new Date(originDate).setDate(originDate.getDate() + incNumber));
};

你可以直接粘贴这段代码到浏览器的控制台执行(注意第15和16行的那两个&&实体符号), 下面是一些日期的测试结果:

// 2016年1月1日
ISOWEEKNUM(new Date(2016, 0, 1))
// <· 53

// 2016年1月3日
ISOWEEKNUM(new Date(2016, 0, 3))
// <· 53

// 2016年1月4日
ISOWEEKNUM(new Date(2016, 0, 4))
// <· 1

// 2016年6月1日
ISOWEEKNUM(new Date(2016, 5, 1))
// <· 22

// 2018年1月1日
ISOWEEKNUM(new Date(2018, 0, 1))
// <· 1

// 2018年3月7日
ISOWEEKNUM(new Date(2016, 2, 7))
// <· 10