最近公司發放了2025年行事曆(以下為示意圖非實際行事曆):
看起來很正常的行事曆,但問題就出現在了週別計算。
歸納後某公司十年來的行事曆的規則如下:
a. 1月1日那一週如果屬於今年的日數大於等於4日,則為第一週W1。
b. 1月1日那一週如果屬於今年的日數小於4日,則為去年最後一週W52或W53。
c. 每一週以星期一為開始日。
d. ....
以下為 javascript 程式碼:
/// 行事曆 週別計算
function getWeekNumber(dateInput) {
console.log('輸入的日期是:' + dateInput);
const date = new Date(dateInput); // 將輸入的字串轉為日期
const year = date.getFullYear(); // 取得輸入日期的年份
const firstDayOfYear = new Date(year, 0, 1); // 取得該年份1月1日
const dayOfWeek_firstDayOfYear = firstDayOfYear.getDay(); // 取得1月1日是星期幾
// 尋找該年份的第一個星期一
const firstMonday = new Date(firstDayOfYear);
if (dayOfWeek_firstDayOfYear !== 1) {
const daysToAdd = (8 - dayOfWeek_firstDayOfYear) % 7; // 計算距離下個星期一的天數
firstMonday.setDate(firstMonday.getDate() + daysToAdd);
}
// 計算從第一個星期一到指定日期的差距,以天數計算
const differenceInDays = Math.floor((date - firstMonday) / (1000 * 60 * 60 * 24));
// 週數計算
let weekNumber = Math.floor(differenceInDays / 7) + 1; // 週數從1開始
if (dayOfWeek_firstDayOfYear >= 2 && dayOfWeek_firstDayOfYear <= 4) {
weekNumber += 1; // 如果1月1日屬於星期二到星期四,則週數加1
}
// 若週數為0,計算去年的最後一個星期
if (weekNumber === 0) {
const lastYear = year - 1;
const lastDayOfLastYear = new Date(lastYear, 11, 31);
const firstDayOfLastYear = new Date(lastYear, 0, 1);
// 尋找去年第一個星期一
const firstMondayOfLastYear = new Date(firstDayOfLastYear);
if (firstDayOfLastYear.getDay() !== 1) {
// 計算距離下個星期一的天數
const daysToAdd_lastyear = (8 - firstMondayOfLastYear.getDay()) % 7;
firstMondayOfLastYear.setDate(firstMondayOfLastYear.getDate() + daysToAdd_lastyear);
}
const differenceInDays_lastyear = Math.floor((lastDayOfLastYear - firstMondayOfLastYear) / (1000 * 60 * 60 * 24));
weekNumber = Math.floor(differenceInDays_lastyear / 7) + 1; // 加 1 因為週數是從 1 開始
if (firstDayOfLastYear.getDay() >= 2 && firstDayOfLastYear.getDay() <= 4) {
weekNumber += 1; // 同樣的條件,若是週二到週四,週數加1
}
return `${lastYear}-W${weekNumber}`; // 返回結果
}
// 若週數為53,檢查今年的最後一天
else if (weekNumber === 53) {
const lastDayOfYear = new Date(year, 11, 31);
const dayOfWeek_lastDayOfYear = lastDayOfYear.getDay(); // 取得年底的星期幾
if (dayOfWeek_lastDayOfYear > 0 && dayOfWeek_lastDayOfYear < 4) {
return `${year + 1}-W1`; // 如果最後一天屬於週一到週四,則為下一年的第一週
} else {
return `${year}-W${weekNumber}`; // 否則就是今年的53週
}
} else {
return `${year}-W${weekNumber}`; // 返回結果
}
}
function getDateRange(weekInput) {
console.log('輸入的週別是:' + weekInput);
// 解析輸入的週數格式,例如 '2024-W1' 或 '2026-W20'
const [year, week] = weekInput.split('-W').map(Number);
// 計算每週的第一天,即從每年的第一天開始
const firstDayOfYear = new Date(year, 0, 1); // 取得該年份1月1日
const dayOfWeek_firstDay = firstDayOfYear.getDay(); // 取得該年份1月1日是星期幾?
//console.log('該年份第一天是星期幾?'+dayOfWeek_firstDay);
// 計算該年份第一週的啟始日期
const firstWeekOfYearStartDate = new Date(firstDayOfYear)
// 每年第一週需 >= 4天,只有1月1日在星期一~星期四,該年份第一週才會包含1月1日
if(dayOfWeek_firstDay >=1 && dayOfWeek_firstDay <= 4){
firstWeekOfYearStartDate.setDate(firstDayOfYear.getDate() - (dayOfWeek_firstDay-1));
} else if(dayOfWeek_firstDay === 0){
firstWeekOfYearStartDate.setDate(firstDayOfYear.getDate() + 1);
} else {
firstWeekOfYearStartDate.setDate(firstDayOfYear.getDate() + (7-dayOfWeek_firstDay+1));
}
//console.log('該年份第一週的啟始日期:'+firstWeekOfYearStartDate);
// 計算該週的開始日期
const startDate = new Date(firstWeekOfYearStartDate);
startDate.setDate(startDate.getDate() + (week - 1) * 7); // 計算開始日期
// 計算該週的結束日期
const endDate = new Date(startDate);
endDate.setDate(endDate.getDate() + 6); // 結束日期是開始日期的6天後
// 格式化日期為 YYYY-MM-DD 格式
const formatDate = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份從0開始,需加1
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
// 輸出結果範圍
return `${formatDate(startDate)}~${formatDate(endDate)}`;
}
接下來我們驗證一下算法:
2016/01/02 一般都會認為是2016年第一週,依但某公司規則,硬生生變成了2015W53
2025/12/31 依但某公司規則為2026年第一週