datetime-local 型の <input> 要素は、ユーザーが簡単に日付と時刻、つまり年、月、日と時、分を入力することができる入力コントロールを作成します。ユーザーのローカルタイムゾーンが使用されます。コントロールのユーザーインターフェイスは、一般にブラウザ―によってまちまちです。現時点で対応は不安定で、 Chrome/Opera とデスクトップ版の Edge、それに最新版のモバイルブラウザーが有用な実装をしています。他のブラウザーでは、ブラウザーでは、このコントロールは単純な <input type="text"> に格下げされます。

秒には対応していないことに注意してください。

メモ: datetime-local は限られたブラウザーしか対応しておらず、入力欄の動作が様々であるため、現在はこれを表示するためにフレームワークやライブラリを使用するか、独自のカスタム入力欄をした方が良いかもしれません。また、 datetime の入力欄を別々に使用すると、 datetime-local よりも対応が広くなります。

一部のブラウザーでは、テキストのみの入力要素を表示し、結果をサーバーに送信する前に妥当な日付/時刻値であることを検証するものもありますが、予期しない動作をすることがあるので、この動作に頼るべきではありません。

対応していないブラウザーを使っている人向けに、 Chrome や Opera の datetime-local コントロールは以下のスクリーンショットのようになります。右端の下向きの矢印をクリックすると、日付を選択するための日付選択画面が現れます。時刻は手入力する必要があります。

Edge の datetime-local コントロールは以下のような外見です。日付及び時刻のぶぶんの値をクリックすると、2つの別々な選択画面が現れますので、簡単に日付と時刻を設定できます。これは datetime のウィジェットを両方作成して、一つにまとめたようなものです。

Value (ローカルタイムゾーンでの) 日付と時刻を表す DOMString、又は空欄。
イベント change 及び input
対応する共通属性 autocomplete, list, readonly, step
IDL 属性 list, value, valueAsNumber
メソッド select(), stepDown(), stepUp()

入力欄に入力された日付を表す DOMString です。次のように value 属性の中に日付と時刻を含めることで、既定値を設定することができます。

<label for="party">パーティーを予約する日時を入力してください。</label>
<input id="party" type="datetime-local" name="partydate" value="2017-06-01T08:30">

一点気を付けなければならないことは、表示される日付と時刻の書式は実際の value とは異なることです。表示される日付と時刻は、オペレーティングシステムからの報告に従ってユーザーのロケールによって書式化されますが、日付や時刻の value は常に yyyy-MM-ddThh:mm の書式です。例えば、上記の値をサーバーに送信すると、 "partydate=2017-06-01T08:30" のようになります。

メモ: このようなデータが HTTP の GET を通じて送信されると、コロン文字を URL 引数に含めるために、 partydate=2017-06-01T08%3A30 のようにエスケープする必要があることもお忘れなく。これを行う方法の一つとして encodeURI() を参照してください。

次のように、 JavaScript で HTMLInputElement.value プロパティを使用して日付の値を取得したり設定したりすることもできます。

var dateControl = document.querySelector('input[type="datetime-local"]');
dateControl.value = '2017-06-01T08:30';

JavaScript の Date には、数値の日時情報を正しく整形された文字列に変換したり、手動で行ったりするメソッドがいくつかあります。例えば、この用途では Date.toISOString() メソッドが利用できます。

datetime-local 入力欄の使用

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

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

datetime-local の基本的な使用

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

<form>
    <label for="party">パーティーを予約する日時を入力してください。</label>
    <input id="party" type="datetime-local" name="partydate">
</form>

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

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

  <form>
    <label for="party">パーティーを予約する日時を入力してください。</label>
    <input id="party" type="datetime-local" name="partydate" min="2017-06-01T08:30" max="2017-06-30T16:30">
  </form>

結果は次のようになります。

  • この結果は、2017年の6月の日付のみが選択できるようになります。文字列値の「日」の部分のみが編集可能となり、日付選択ウィジェットで6月以外にスクロールすることはできなくなります。
  • どのブラウザーを使用するかによって、指定された値の範囲外の時刻が時刻選択画面で選択できなかったり (Edge など)、無効な値 (Validation を参照) であっても設定できたりすることがあります (Chrome など)。

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

入力欄の寸法の制御

<input type="datetime-local"> は、 size のようなコントロールの寸法に関する属性には対応していません。寸法を変更する必要がある場合は、 CSS を使用する必要があります。

タイムゾーンの設定

datetime-local 入力型はコントロールのタイムゾーンやロケールを設定する方法がありません。これは datetime 入力型では利用できましたが、この入力型は廃止され、仕様書から削除されました。削除された主な理由はブラウザーの対応がなく、ユーザーインターフェイスや使い勝手が決まっていなかったからです。単に日付と時刻を設定するコントロールを用意して、別なコントロールで地域を設定したほうが簡単です。

例えば、ユーザーがログインしている場所によって地域を設定するようなシステムを開発する場合、タイムゾーンを hidden 入力型で次のように提供することができます。

<input type="hidden" id="timezone" name="timezone" value="-08:00">

一方、ユーザーに日時入力と共にタイムゾーンを入力できるようにする必要がある場合、 <select> 要素などでタイムゾーンを入力する手段を提供することができます。

<select name="timezone_offset" id="timezone-offset" class="span5">
    <option value="-12:00">(GMT -12:00) Eniwetok, Kwajalein</option>
    <option value="-11:00">(GMT -11:00) Midway Island, Samoa</option>
    <option value="-10:00">(GMT -10:00) Hawaii</option>
    <option value="-09:50">(GMT -9:30) Taiohae</option>
    <option value="-09:00">(GMT -9:00) Alaska</option>
    <option value="-08:00">(GMT -8:00) Pacific Time (US &amp; Canada)</option>

  ...
 
</select>

どちらの場合も、日時の値とタイムゾーンの値はサーバーに別々のデータポイントとして送信されるため、サーバー側のデータベースに適切に格納する必要があります。

メモ: 上記のコードの断片は、HTML select 要素の全世界のタイムゾーンから取得しました。

検証

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

min 及び max を使用して有効な日付を制限したり (日付の最大値と最小値の設定を参照)、 required 属性を使用して日時の入力を必須にしたりすることができます。その結果、対応しているブラウザーでは、範囲を外れた日付や空の日付欄を送信しようとするとエラーが表示されるでしょう。

例を見てみましょう。ここで日付の最小値と最大値を設定し、入力欄を必須にしました。

<form>
    <div>
        <label for="party">希望するパーティーの日時を選択して下さい (必須、6月1日午前8:30~6月30日午後4:30) </label>
        <input id="party" type="datetime-local" name="partydate" min="2017-06-01T08:30" max="2017-06-30T16:30" required>
        <span class="validity"></span>
    </div>
    <div>
        <input type="submit" value="予約する!">
    </div>
</form>

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

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

上記の例の CSS です。 CSS の :valid 及び :invalid プロパティを使用して、現在の値が有効かどうかに基づいてスタイルを設定しています。アイコンは入力欄そのものではなく、入力欄の隣の <span> に置くようにしないと、 Chrome ではコントロールの内側にコンテンツを生成するので、正しく整形したり表示したりすることができません。

div {
    margin-bottom: 10px;
    display: flex;
    align-items: center;
}

label {
  display: inline-block;
  width: 300px;
}

input:invalid+span:after {
    content: '✖';
    padding-left: 5px;
}

input:valid+span:after {
    content: '✓';
    padding-left: 5px;
}

重要: HTML のフォーム検証は、入力されたデータが正しい形式であることを保証するスクリプトの代用にはなりません。 HTML を調整して検証をくぐり抜けたり、完全に削除したりすることはとても簡単にできます。 HTML を完全にバイパスし、サーバーに直接データを送信することも可能です。サーバー側のコードが受信したデータの検証に失敗した場合、不適切な形式のデータ (または大きすぎるデータ、間違った種類のデータなど) が送信された場合に災害が発生するおそれがあります。

ブラウザーの対応の扱い

前述のように、現時点で日付入力を書く上で一番の問題はブラウザーの対応です。デスクトップでは Chrome/Opera と Edge のみが対応しており、モバイルでは多くの最新のブラウザーが対応しています。例えば、 Android 版 Firefox の datetime-local の選択画面はこの例のように表示されます。

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

2番目の問題はより深刻です。すでに述べたように、 datetime-local 入力欄では、実際の値が常に yyyy-mm-ddThh:mm の書式で正規化されます。一方、文字列入力欄では、既定でブラウザーは日付がどの書式で入力されるかの認識がなく、以下のように人間が日付と時刻を書く様々な方法で入力される可能性があります。

  • ddmmyyyy
  • dd/mm/yyyy
  • mm/dd/yyyy
  • dd-mm-yyyy
  • mm-dd-yyyy
  • mm-dd-yyyy hh:mm (12時制)
  • mm-dd-yyyy hh:mm (24時制)
  • など

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

<form>
  <div>
    <label for="party">希望するパーティーの日時を選択して下さい (必須、6月1日午前8:30~6月30日午後4:30) </label>
    <input id="party" type="datetime-local" name="partydate"
           min="2017-06-01T08:30" max="2017-06-30T16:30"
           pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}" required>
    <span class="validity"></span>
  </div>
  <div>
    <input type="submit" value="予約する!">
  </div>
  <input type="hidden" id="timezone" name="timezone" value="-08:00">
</form>

nnnn-nn-nnTnn:nn のパターン (n は数字の0から9) に一致しない文字列を入力して送信しようとすると、エラーメッセージが表示される (そして入力欄が無効として強調表示される) のが分かるでしょう。もちろん、これではユーザーが無効な日付を入力したり、誤った形式で日付や時刻を入力したりすることを止めることはできません。

また、日付や時刻を入力するパターンをどうやってユーザーに理解させればよいのでしょうか?

まだ問題があります。

ブラウザーに依存しない方法によってフォームで日付を扱う最善の方法は、現時点では年月日を別々な (<select> 要素のコントロールにする一般的です。以下の実装を見てください) にするか、 jQuery date pickerjQuery timepicker plugin のような JavaScript ライブラリを使用することです。

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

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

<form>
  <div class="nativeDateTimePicker">
    <label for="party">希望するパーティーの日時を選択して下さい。</label>
    <input type="datetime-local" id="party" name="bday">
    <span class="validity"></span>
  </div>
  <p class="fallbackLabel">希望するパーティーの日時を選択して下さい。</p>
  <div class="fallbackDateTimePicker">
    <div>
      <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>
    <div>
      <span>
        <select id="hour" name="hour">
        <label for="hour">時</label>
        </select>
      </span>
      <span>
        <select id="minute" name="minute">
        <label for="minute">分</label>
        </select>
      </span>
    </div>
  </div>
</form>

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

もう一つの面白い部分は、機能の検出コードです。ブラウザーが <input type="datetime-local"> に対応しているかどうかを検出するために、新たな <input> 要素を生成し、その typedatetime-local に設定して、すぐに type に何が設定されたかをチェックします。 datetime-local 型に対応していないブラウザーでは、 datetime-local 型が text 型へフォールバックされます。 <input type="datetime-local"> に対応していない場合は、ネイティブの日時入力欄を非表示にしてフォールバック用の (<select>) による選択ユーザーインターフェイスを表示します。

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

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

// 最初はフォールバックを非表示にする
fallbackPicker.style.display = 'none';
fallbackLabel.style.display = 'none';

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

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

function populateDays(month) {
  // 日の <select> から現在の一連の <option> 要素を削除し、
  // 挿入のための準備をする
  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;
    (year - 2016) % 4 === 0 ? 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);
  }
}

function populateHours() {
  // populate the hours <select> with the 24 hours of the day
  for(var i = 0; i <= 23; i++) {
    var option = document.createElement('option');
    option.textContent = (i < 10) ? ("0" + i) : i;
    hourSelect.appendChild(option);
  }
}

function populateMinutes() {
  // populate the minutes <select> with the 60 hours of each minute
  for(var i = 0; i <= 59; i++) {
    var option = document.createElement('option');
    option.textContent = (i < 10) ? ("0" + i) : i;
    minuteSelect.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週ある年もあることを忘れないでください (年あたりの週数を参照)。商品のアプリを開発するときはこれを念頭に置いておく必要があります。

仕様書

仕様書 状態 備考
HTML Living Standard
<input type="datetime-local"> の定義
現行の標準  
HTML5
<input type="datetime-local"> の定義
勧告  

ブラウザーの対応

機能ChromeEdgeFirefoxInternet ExplorerOperaSafari
基本対応2012 なし1 なし11 なし2
機能Android webviewChrome for AndroidEdge mobileFirefox for AndroidOpera AndroidiOS SafariSamsung Internet
基本対応 あり あり ? あり11 あり ?

1. See bug 888320 and TPE DOM/Date time input types.

2. The input type is recognized, but there is no date-specific control.

関連情報

ドキュメントのタグと貢献者

このページの貢献者: mfuji09
最終更新者: mfuji09,