[JavaScript] 檢驗輸入日期是否真實存在

這是 JavaScript 的一個作業的一部份,我把他拆成三個部份寫,第一個是要檢驗從 <input type="text" /> 標籤獲得使用者輸入字串,必須符合 yyyy/mm/dd 的格式,例如 2022/02/10 、2022/04/31 或 2022/02/29:

而且要檢查符合格式的字串所代表的日期,是否真實存在,若輸入不符合格式或不存在的日期就提示並阻擋。大部份網路上找到的這方面問題的解法,都是針對月份是1、3、5、7、8、10、12月則有 31 日,剩餘月份為 30 日,最後檢查閏年平年判斷 2 月是 28 日還是 29 日。這裡我想用不同的解法,並做成了筆記。

格式檢查

首先 HTML 部份

<label for="date">日期:</label>
<input type="text" id="date" placeholder="yyyy/mm/dd" onblur="checkDate()" />
<div id="dateWarning"></div>

<input> 標籤有 id="date",在失去焦點時註冊事件 checkDate(),下面置放一個<div> 標籤 id="dateWarning" 做為提示的區域。先對輸入字串檢查是否符合 yyyy/mm/dd 的形式,這裡用正規表示式 Regular Expression:

function checkDate() {
  let dateText = document.getElementById('date').value;       // 取輸入的值的字串
  let dateWarning = document.getElementById('dateWarning');   // 取錯誤提示的元素

  let regexp = new RegExp(/^[0-9]{1,4}\/((0[1-9])|(1[0-2]))\/((0[1-9])|([12][0-9])(3[01]))$/);      // RegularExpression
  if (!regexp.test(dateText)) {                     // 匹配測試不符合此格式時
    dateWarning.innerHTML = '格式必須符合「年 / 月 / 日」';
    return;
    .
    .
    .
  }

正規表示式雖然長但蠻簡單的,分隔年月日的 / 是特殊字元,要用跳脫字元 \/

然後年的部份 [0-9]{1,4} 匹配了 4 個數字、3 個數字、2 個數字或 1 個數字;

而月的部份因為有01、02、....09、10、11、12月,依十位數字為 0 或 1 分成兩種,十位數字若為 0,則個位數字可為 1 到 9,所以正規表示式為 0[1-9] ,若十位數字是 1,則個位數字可為0、1、2,所以是 1[0-2] ,這兩種都可以就用或符號 | 相連,所以月的部份為 ((0[1-9])|(1[0-2]))

最後日的部份有01、02、....09、10、11、12、...、19、20、21、...、29、30、31,依十位數字為 0 或 1 或 2 或 3 分成四種,當十位數字為 0 時,個位數字可為 1 到 9,得 0[1-9] ,若十位數字為 1 或 2 時,個位數字可為 0 到 9,得 [12][0-9] ,若十位數字為 3 時,個位數字可為0、1,得 3[01] ,全部用 | 連接,所以日的部份為 ((0[1-9])|([12][0-9])(3[01]))

JavaScript 的 Date 物件

檢查是否為存在的日期,我利用了 JavaScript 的 Date 物件,所以先介紹一些相關函式方法,因為我的主題在年月日,所以時分秒相關方法就省略:

建構函式 new Date(Year, Month, Day) :用年月日可建構代表該日期的物件,要注意 Month 是0 到 11,一月的 Month = 0,二月的 Month = 1,.... ,十二月的 Month = 11,所以 new Date( 2022, 1, 10 ) 會得到 2022 年 2 月 10 日的物件。

建構函式 new Date(string) :可用如「2022/02/10」、「2015-04-17」、「1995.11.23」的字串建構,所以 new Date( '2022/02/10' ) 會得到 2022 年 2 月 10 日的物件。

.getFullYear():回傳 Date 物件的年份(以 4 位數字表示)

.getMonth():回傳 Date 物件的月份,一月 = 0,二月 = 1,.... ,十二月 = 11

.getDate():回傳 Date 物件的日期,1日 = 1,2日 = 2,.... ,31日 = 31

.getDay():回傳當週的星期,星期日 = 0,星期一 = 1, ... ,星期六 = 6

這裡比較有趣的點是即使是不存在的日期,也可以建構 Date 物件,而日期會被推移,例如 5 月有 31 天:

  • new Date( 2015, 4, 31 ) 得 2015 年 5 月 31 日的物件
  • new Date( 2015, 4, 32 ) 得 2015 年 6 月 1 日的物件
  • new Date( 2015, 4, 33 ) 得 2015 年 6 月 2 日的物件

超過則會往下個月推移,同樣的,不足的會往前推:

  • new Date( 2015, 4, 1 ) 得 2015 年 5 月 1 日的物件
  • new Date( 2015, 4, 0 ) 得 2015 年 4 月 30 日的物件
  • new Date( 2015, 4, -1 ) 得 2015 年 4 月 29 日的物件

檢查日期是否合法

由於錯誤的日期會被推移,所以檢查輸入的日期 day ,和建構成 Date 物件後的日期 day 是否一致,就可以判斷是否合法日期了,前者取得<input> 的 value 字串,取其尾巴兩個數字的子字串,後者取建構出來的的 Date 物件呼叫 .getDate() ,比對兩者即可:

function checkDate() {
  .
  .
  .
  let dateByText = dateText.slice(-2);            // 取出輸入的結尾2字,即日期

  let dateByObj = new Date(dateText).getDate();   // 將輸入字串轉為物件,取出日期

  if (dateByText != dateByObj) {                  // 比對
    dateWarning.innerHTML = '不存在的日期';   
    return;
  }
  dateWarning.innerHTML = ' ';                   // 日期沒問題,清空錯誤的提示
  return;
}

留言