<input type="date">

<input> 要素の type="date" 入力型は、検証付きのテキストボックスまたは特殊な日付選択インターフェイスのどちらかで、ユーザーに日付を入力させる入力欄を生成します。

結果の値には年、月、日が含まれますが、時刻は含まれませんtime および datetime-local 入力型は時刻や日付と時刻の入力に対応しています。

入力欄のユーザーインターフェイスは、一般にブラウザーによってまちまちです。詳細はブラウザーの互換性を参照してください。対応していないブラウザーでは、このコントロールは単純な <input type="text"> に格下げされます。

日付を選択する専用のインターフェイスを持つブラウザーの中で、 Chrome や Opera のデータコントロールは次のように表示されます。

“dd/mm/yyyy” と、増加/減少ボタンと、カレンダーコントロールを表示するための下向きの矢印があるテキストボックス。

Edge の日付コントロールは次のように表示されます。

“mm/dd/yyyy” があるが、操作をすると、3列の日付選択が表示される。

Firefox の日付コントロールは次のように表示されます。

違う形の “dd/mm/yyyy” テキストボックスで、選択可能なカレンダーコントロールを開いている。

DOMString で、 YYYY-MM-DD 形式の日付、または空欄を表す
イベント change および input
対応している共通属性 autocomplete, list, readonly, step
IDL 属性 list, value, valueAsDate, valueAsNumber
メソッド select(), stepDown(), stepUp()

入力欄に入力された日付を表す DOMString です。日付は ISO8601 に従って書式化されており、これは HTML で使われる日付や時刻の形式日付の文字列で説明されています。

次のように、日付の入力欄の既定値を value 属性に設定することができます。

<input type="date" value="2017-06-01">

表示される日付の書式は実際の value とは異なります。 — 表示される日付はユーザーのブラウザーに設定されたロケールに基づいて書式になりますが、解釈された value は常に yyyy-mm-dd の書式です。

日付の値は JavaScript で入力要素の value および valueAsNumber プロパティを用いて、次のように取得したり設定したりすることができます。

var dateControl = document.querySelector('input[type="date"]');
dateControl.value = '2017-06-01';
console.log(dateControl.value); // "2017-06-01" と表示
console.log(dateControl.valueAsNumber); // 1496275200000 と UNIX タイムスタンプを表示

このコードは <input> 要素のうち typedate である最初のものを探し、値を 2017-06-01 (2017年6月1日) に設定します。それからその値を文字列と数字の書式で読み直しています。

追加の属性

すべての <input> 要素で共通する属性と共に、 date 型の入力欄は以下の属性にも対応しています。

属性 説明
max 受け付けられる最も遅い日付
min 受け付けられる最も早い日付
step 上下スピナーボタンで値を調整する時や、日付の検証の時の刻み間隔

max

受け付ける最も遅い日付です。要素に入力された value がこれ以降になった場合、要素は制約検証に失敗します。 max 属性の値が yyyy-mm-dd の書式のありうる日付ではない場合、要素は最大の日付値を持ちません。

maxmin の両方の属性が設定された場合、この値は min 属性にあるものより遅いか、同じ日付文字列でなければなりません。

min

受け付ける最も早い日付です。要素に入力された value がこれ以前になった場合、要素は制約検証に失敗します。 min 属性の値が yyyy-mm-dd の書式のありうる日付でない場合、要素は最小の日付値を持ちません。

maxmin の両方の属性が設定された場合、この値は max 属性で指定されたものより早いか、同じ日付を指定する必要があります。

step

step 属性は値が吸着する粒度を指定する数値、または後述する特殊な値 any です。刻みの基準値に等しい値 (指定されていれば min、そうでなければ value、どちらも設定されていなければ適切な既定値) のみが妥当となります。

文字列値の any は刻みがなく、どの値でも許可されることを意味します (minmax など、他の制約に制限されます)。

注: ユーザーがデータを入力したときには刻みの設定には吸着せず、ユーザーエージェントは直近の妥当な値、同じ距離の値の選択肢が二つあった場合は、正の方向の推奨値に丸められます。

date 入力欄では、 step の値は日単位で指定します。また、 86,400,000 に step を乗じた値に等しいミリ秒数として扱われます (数値はミリ秒単位であるため)。 step の既定値は 1 であり、1日を表します。

anystep 属性の値として指定すると、 1date 入力欄に設定したのと同じ効果になります。

date 入力欄の使用

日付入力は便利に見えます。 — 日付の選択に簡単なインターフェイスを提供し、サーバーに送信するデータの書式をユーザーのロケールに関係なく正規化してくれます。しかし、今のところブラウザーの対応が限定されているため、 <input type="date"> には問題があります。

この節では、 <input type="date"> の基本的な使い方と少し複雑な使い方を見てみてから、その後でブラウザーの互換性の問題を緩和するアドバイスを提供しましょう (ブラウザーの互換性の扱い を参照してください)。

今後ブラウザーの互換性がもっと普遍的になり、この問題が消滅することを願います。

日付入力の基本的な使用

もっとも単純な <input type="date"> の使用方法は、次のように基本的な <input><label> 要素の組み合わせです。

<form action="https://example.com">
  <label>
    誕生日を入力してください。
    <input type="date" name="bday">
  </label>

  <p><button>Submit</button></p>
</form>

この HTML は入力された日付を bday キーに入れて https://example.com に送ります。 — 結果の URL は https://example.com/?bday=1955-06-08 のようになります。

日付の最大値と最小値の設定

min および max 属性を使用して、ユーザーが選択できる日付を制限することができます。次の例では、日付の最小値を 2017-04-01 に、日付の最大値を 2017-04-30 に設定しています。

<form>
  <label for="party">希望するパーティーの日を選んでください。
    <input type="date" name="party" min="2017-04-01" max="2017-04-30">
  </label>
</form>

この結果は、2017年の4月の日付のみが選択できるようになります。 — テキストボックスの年と月の部分が編集できなくなり、日付選択ウィジェットで4月以外にスクロールすることができなくなります。

: step 属性を使用すると、日付が加算するたびに飛ばす日数を設定できるはずです(例えば、土曜日のみを選択できるようにしたい場合など)。しかし、執筆時点でどの実装も正しく動作していないようです。

入力欄の寸法の制御

<input type="date">size のような寸法に関する属性には対応していません。寸法を変更するには CSS を推奨します。

検証

既定で、 <input type="date"> は入力された値をその書式を超えて検証しません。インターフェイスは一般的に、日付でないものの入力をさせないからです。 — これは便利です。 — しかし、入力欄を空のままにしたり、 text 型にフォールバックするブラウザーにおいて、無効な日付を入力したりすることが可能です (例えば4月32日など)。

min および max を使用して有効な日付を制限すると (日付の最大値と最小値の設定を参照)、対応しているブラウザーは、範囲を外れたの日付を送信しようとしたときにエラーを表示するでしょう。しかし、ユーザーの端末が日付選択に完全に対応していない場合、値がこれらの日付の中にあることを保証するためには、送信された結果を再度チェックしなければならないでしょう。

required 属性を使用して、日付の入力を必須にすることもできます。 — 空の日付欄を送信しようとするとエラーが表示されるでしょう。これは、テキスト入力欄で代替されたとしても、ほとんどのブラウザーで動作するはずです。

日付の最小値と最大値の例と、入力欄を必須にする例を見てみましょう。

<form>
  <label>
    推奨したいパーティーの日を選択してください (必須、4月1日~20日):
    <input type="date" name="party" min="2017-04-01" max="2017-04-20" required>
    <span class="validity"></span>
  </label>

  <p>
    <button>Submit</button>
  </p>
</form>

不完全な日付 (または設定した範囲を外れた日付) を送信しようとすると、ブラウザーはエラーを表示します。例を実行してみましょう。

対応しているブラウザーで入力しなかった場合のスクリーンショットです。

入力欄に吹き出しが重なっており、その中に橙色のビックリマークのアイコンと “Please fill in this field.” というメッセージがある。

上記の例で使用されている CSS です。 :valid および :invalid 擬似要素を使用して、現在の値が妥当かどうかで入力欄の隣にアイコンを追加しています。アイコンは入力欄そのものではなく、入力欄の隣の <span> に置くようにしないと、 Chrome ではコントロールの内側にコンテンツを生成するので、正しく整形したり表示したりすることができません。

label {
  display: flex;
  align-items: center;
}

span::after {
  padding-left: 5px;
}

input:invalid + span::after {
  content: '✖';
}

input:valid+span::after {
  content: '✓';
}

重要: クライアント側のフォーム検証は、サーバー上の検証の代用にはなりません。誰かが HTML を改変したり、 HTML を完全にバイパスしてサーバーに直接データを送信したいrすることは簡単です。サーバーが受信したデータの検証に失敗した場合、不適切な形式のデータ、大きすぎるデータ、誤った種類のデータなどに遭遇すると、障害が発生するおそれがあります。

ブラウザーの対応の扱い

前述のように、現時点で日付入力を書く上で一番の問題はブラウザーの対応です。 Android 版 Firefox の日付選択はこの例のように表示されます。

ウェブページとブラウザーの UI の上にモーダルで浮動しているポップアップのカレンダー選択

対応していないブラウザーでは、文字列入力欄に安全に格下げされますが、これはユーザーインターフェイスの一貫性 (表示されるコントロールが異なること) とデータの扱い方の両方で問題を生みます。

2番目の問題はより深刻です。日付入力に対応していると、値が yyyy-mm-dd の書式で正規化されます。しかし、文字列入力欄では、ブラウザーには日付がどの書式で入力されるかの認識がなく、また、以下のように人間が日付を書く書式は様々なものがあります。

  • ddmmyyyy
  • dd/mm/yyyy
  • mm/dd/yyyy
  • dd-mm-yyyy
  • mm-dd-yyyy
  • Month dd, yyyy

これを回避する方法の一つは、日付入力欄に pattern 属性を付けることです。日付選択ではこれを使用しませんが、文字列入力にフォールバックされたときには使用されます。例えば、次の例を未対応のブラウザーで見てみてください。

<form>
  <label for="bday">誕生日を入力してください:
    <input type="date" name="bday" required pattern="\d{4}-\d{2}-\d{2}">
    <span class="validity"></span>
  </label>
  <p>
    <button>送信</button>
  </p>
</form>

送信ボタンを押すと、入力が ####-##-## のパターン (# は数字の0から9) に一致しない場合はエラーメッセージが表示され、入力欄が無効として強調表示されるのが分かるでしょう。もちろん、これでユーザーが無効な日付や不正な書式を入力するのを止めることはできません。ですから、まだ問題があります。

現時点では、ブラウザーに依存しない方法でフォームで日付を扱う最善の方法は、年月日を別々なコントロールに入力するようにするか、 jQuery date picker のような JavaScript ライブラリを使用することです。

この例では、日付を選択するユーザーインターフェイスの要素を2組作成します。ネイティブの <input type="date"> による選択と、ネイティブの日付入力に対応していない古いブラウザー向けの、3つの <select> 要素です。

HTML

HTML は次のようになります。

<form>
    <div class="nativeDatePicker">
      <label for="bday">誕生日を入力してください:</label>
      <input type="date" id="bday" name="bday">
      <span class="validity"></span>
    </div>
    <p class="fallbackLabel">誕生日を入力してください:</p>
    <div class="fallbackDatePicker">
      <span>
        <select id="year" name="year">
        </select>
        <label for="year"></label>
      </span>
      <span>
        <select id="month" name="month">
          <option selected>1</option>
          <option>2</option>
          <option>3</option>
          <option>4</option>
          <option>5</option>
          <option>6</option>
          <option>7</option>
          <option>8</option>
          <option>9</option>
          <option>10</option>
          <option>11</option>
          <option>12</option>
        </select>
        <label for="month"></label>
      </span>
      <span>
        <select id="day" name="day">
        </select>
        <label for="day"></label>
      </span>
    </div>
</form>

月は (常に同じなので) ハードコーディングされていますが、年と日の値は、現在の年、および現在選択されている年と月によってそれぞれ動的に生成されます (どのように動作するかについての詳細な説明は、以下のコードのコメントを参照してください)。

JavaScript

もう一つの面白い部分は、機能の検出コードです。 — ブラウザーが <input type="date"> に対応しているかどうかを検出するためのものです。

新たな <input> 要素を生成し、その typedate に設定して、すぐにどの型になったのかをチェックします。対応していないブラウザーでは、 text が返されます。これは、 date 型が text 型へフォールバックされるためです。 <input type="date"> に対応していない場合は、ネイティブの日付選択を非表示にして、フォールバック (<select>) 表示します。

// 変数を定義
var nativePicker = document.querySelector('.nativeDatePicker');
var fallbackPicker = document.querySelector('.fallbackDatePicker');
var fallbackLabel = document.querySelector('.fallbackLabel');

var yearSelect = document.querySelector('#year');
var monthSelect = document.querySelector('#month');
var daySelect = document.querySelector('#day');

// 最初はフォールバックを隠す
fallbackPicker.style.display = 'none';
fallbackLabel.style.display = 'none';

// 新しい日付入力が文字列入力にフォールバックされるかどうか
var test = document.createElement('input');

try {
  test.type = 'date';
} catch (e) {
  console.log(e.description);
}

// もし文字列入力になるならば、 if() {} ブロックの中のコードを実行する
if(test.type === 'text') {
  // ネイティブの日付選択を隠してフォールバック版を表示
  nativePicker.style.display = 'none';
  fallbackPicker.style.display = 'block';
  fallbackLabel.style.display = 'block';

  // 年と日を動的に生成する
  // (月は常に同じなのでハードコーディング)
  populateDays(monthSelect.value);
  populateYears();
}

function populateDays(month) {
  // delete the current set of <option> elements out of the
  // day <select>, ready for the next set to be injected
  while(daySelect.firstChild){
    daySelect.removeChild(daySelect.firstChild);
  }

  // 挿入する日数を保持する変数を作成
  var dayNum;

  // 31日か30日か?
  if(month === '1' | month === '3' | month === '5' | month === '7' | month === '8' | month === '10' | month === '12') {
    dayNum = 31;
  } else if(month === '4' | month === '6' | month === '9' | month === '11') {
    dayNum = 30;
  } else {
  // 2月の場合は、閏年かどうかを計算する
  var year = yearSelect.value;
  var isLeap = new Date(year, 1, 29).getMonth() == 1;
  isLeap ? dayNum = 29 : dayNum = 28;
  }

  // <select> に正しい数の新しい <option> 要素を挿入
  for(i = 1; i <= dayNum; i++) {
    var option = document.createElement('option');
    option.textContent = i;
    daySelect.appendChild(option);
  }

  // 前回の日が既に設定されていたら、 daySelect の値を
  // 日に設定し、年を変えたときに1に戻ることを
  // 避ける
  if(previousDay) {
    daySelect.value = previousDay;

    // 前回設定されていた日が大きい数字、例えば31であり、
    // その後で日数が少ない月 (例えば2月) を選択した場合、
    // このコードで空欄の日選択を表示するのではなく、
    // 利用可能な最大の日を選択するように保証する
    if(daySelect.value === "") {
      daySelect.value = previousDay - 1;
    }

    if(daySelect.value === "") {
      daySelect.value = previousDay - 2;
    }

    if(daySelect.value === "") {
      daySelect.value = previousDay - 3;
    }
  }
}

function populateYears() {
  // 今年を数値として取得
  var date = new Date();
  var year = date.getFullYear();

  // 今年から100年前までの年が <select> で選択できるようにする
  for(var i = 0; i <= 100; i++) {
    var option = document.createElement('option');
    option.textContent = year-i;
    yearSelect.appendChild(option);
  }
}

// 年や月の <select> 値が変更されたら、 populateDays() を
// 再実行して日数を調整する
yearSelect.onchange = function() {
  populateDays(monthSelect.value);
}

monthSelect.onchange = function() {
  populateDays(monthSelect.value);
}

// 日の選択を保存
var previousDay;

// 以前どの日が設定されていたかを保存する
// 使い方は populateDays() を参照
daySelect.onchange = function() {
  previousDay = daySelect.value;
}

: 53週ある年もあることを忘れないでください (年あたりの週数を参照)。商品のアプリを開発するときはこれを念頭に置いておく必要があります。

仕様書

ブラウザーの互換性

BCD tables only load in the browser

関連情報