2021.04.28 更新:今天有一位 Andrew 大大留言,內容是用 day.js 取得上個月第一日、最後一日的更簡單的方法,更新在 最後一段 中。
本篇要解決的問題
最近收到一個設計,是一個跑報表的頁面,設計上放了一個像 GA 選日期的方式,可以讓使用者選擇看上週、上個月、最近 7 天、最近 30 天,因為是用 API 在取資料,因此必須要能在前端就先算出這些日期丟給 API。
之前在 Moment.js 上看見不再更新的公告,裡面有推薦可以改用 Day.js,進到 Day.js 的頁面第一句話就寫著:
Fast 2kB alternative to Moment.js with the same modern API
看到只有 2KB,用就對了!
不過,Day.js 的文件蠻簡潔的,而且沒有一個直接可以算出上個月的功能,本篇就是整理了一下算出前 x 天、上週、上個月的程式碼,也有在專案中實際使用。
以下因為是以「今天」當作基準,這邊先列出今天的日期:2021/03/15。
計算前 7 天
文件:https://day.js.org/docs/en/manipulate/subtract
要計算前幾天的日期方式是一樣的:
var day = dayjs().subtract(7, 'day').format(); console.log(day); // 2021-03-08T21:55:52+08:00
subtract 是指今天開始減去幾天的意思,因此數字寫幾天,就是抓幾天前的日期,1 就是昨天、7 就是前 7 天、30 就是前 30 天。
day 也可以替換成 month, year。
var month = dayjs().subtract(1, 'month').format(); console.log(month); // 2021-02-15T21:56:28+08:00 var year = dayjs().subtract(1, 'year').format(); console.log(year); // 2020-03-15T21:56:28+08:00
計算上週
文件:https://day.js.org/docs/en/plugin/weekday、https://day.js.org/docs/en/plugin/loading-into-browser
計算上週,指的是計算出上個禮拜的周一到周日。
如果要使用 weekday 的功能,要另外裝上外掛,這邊用引用 CDN 的方式做示範:
<script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.10.4/dayjs.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.10.4/plugin/weekday.min.js"></script> <script> dayjs.extend(window.dayjs_plugin_weekday); // 以下是上一個周一 ~ 周日 var mon = dayjs().weekday(-6).format(); // 2021-03-08T21:57:37+08:00 var tue = dayjs().weekday(-5).format(); // 2021-03-09T21:57:37+08:00 var wen = dayjs().weekday(-4).format(); // 2021-03-10T21:57:37+08:00 var thu = dayjs().weekday(-3).format(); // 2021-03-11T21:57:37+08:00 var fri = dayjs().weekday(-2).format(); // 2021-03-12T21:57:37+08:00 var sat = dayjs().weekday(-1).format(); // 2021-03-13T21:57:37+08:00 var sun = dayjs().weekday(0).format(); // 2021-03-14T21:57:37+08:00 </script>
除了計算上週,也可以拿來計算本週、下週,數字就繼續往下算:
本週
var mon = dayjs().weekday(1).format(); var tue = dayjs().weekday(2).format(); var wen = dayjs().weekday(3).format(); var thu = dayjs().weekday(4).format(); var fri = dayjs().weekday(5).format(); var sat = dayjs().weekday(6).format(); var sun = dayjs().weekday(7).format();
下週
var mon = dayjs().weekday(8).format(); var tue = dayjs().weekday(9).format(); var wen = dayjs().weekday(10).format(); var thu = dayjs().weekday(11).format(); var fri = dayjs().weekday(12).format(); var sat = dayjs().weekday(13).format(); var sun = dayjs().weekday(14).format();
計算上個月
文件:https://day.js.org/docs/en/get-set/date
本月是 3 月,上個月指的就是 2/1 – 2/28。
Day.js 有提供抓出一個月中第幾天的日期:
dayjs().date(1);
上面這行會吐出本月的第一天,但有個問題,4 月只有 30 天,沒有 31 天,如果我們寫了 31,那多出來的一天就會給 5/1,而不會自動停在那個月份的最大天數。
另外計算月份這件事也有天然的麻煩:
- 2 月有閏年(28 天或 29 天)
- 如果本月是 1 月,那上個月就會是「去年」的 12 月
- 小月有 30 天,大月是 31 天
第 2 個跨年度的問題好解決,就判斷這個月是 1 月的話把今年的年份 -1。
第 3 個問題也好解決,就判斷是哪些月份,遇到那些月份就設成 31 天,其它不是 2 月的話就設 30 天。
麻煩的是閏年。
處理閏年
有句話說 Google 是父,維基是母,或是直接說維基就是轉機。
如果直接找「閏年」的維基百科,會發現上面有寫出判斷閏年的方式:
被 4 整除且不被 100 整除 或 被 400 整除
所以在判斷上我們只需要寫:
var currentYear = dayjs().year(); // 本年 let monthMaxDay; // 放 28 天或 29 天 if(currentYear %% 4 === 0 && currentYear %% 100 !== 0 || currentYear %% 400 === 0) { monthMaxDay = 29; } else { monthMaxDay = 28; }
30 天、31 天
最快的判斷方式,就是把所有 31 天的月份存在陣列裡,如果上個月的月份是在這陣列中,就給 31 天,不在陣列中就給 30 天,當然,在這之前記得先判斷完 2 月。
// 本月,dayjs 的月份值是「該月 - 1」,因此要 +1 回來 var currentMonth = dayjs().month() + 1; // 本月如果是 1 月,上個月就要是 12 月,並且年份減 1 var lastMonth; if(currentMonth === 1) { lastMonth = 12; currentYear -= 1; } else { lastMonth = currentMonth - 1; } // 1, 3, 5, 7, 8, 10, 12 為大月 var bigMonth = [1, 3, 5, 7, 8, 10, 12]; // 不是 31 天,就是 30 天 let monthMaxDay; // 放 30 天或 31 天 // 上一個月如果有在陣列中,就是 31 天,否則就是 30 天 if(bigMonth.includes(lastMonth)) { monthMaxDay = 31; } else { monthMaxDay = 30; }
完整判斷「上個月」程式碼
Andrew 大大提供的方法
以上是 August 研究出的方法,如果各位大大們有更簡單的方式還請留言告知~
取得上個月的起訖日期,可以試試這樣寫法 (但我沒做過完整測試…)
使用 day.js
「不」使用依賴的話
是 D 大!
上個月的起訖 , 我會用 startOf 跟 endOf
如果需要 timezone +0 的上個月起迄 , 我會使用 utc
哇噻!真的更簡單耶!請讓我更新到文章中,謝謝 Andrew 大大。