input type="datetime"-local

<input type="datetime"-local>

<input>datetime-local创建输入字段的元素允许轻松输入日期和时间 - 这包括年,月,日,小时和分钟。

控件的用户界面从浏览器到浏览器有所不同,目前的支持是不完整的,只有桌面上的Chrome / Opera和Edge以及大多数现代的移动浏览器版本具有可用的实现。在其他浏览器中,控件会优雅地降到简单<input type="text">。

<input id="datetime" type="datetime-local">

对于那些不使用支持浏览器的用户,Chrome / Opera datetime-local控件看起来像下面的屏幕截图。点击右侧的向下箭头会弹出一个日期选择器,让您选择一个日期; 你必须手动输入时间。

边缘日期时间-本地控件看起来是这样的但是, 单击该值的日期和时间部分将为您提供两个单独的选取器, 以便您进行选择:

表示日期和时间的DOMString,或者为空。
活动改变和输入。
支持的通用属性自动完成,列表,只读和步骤。
IDL属性列表,值,值asAsumber。
方法select(),stepDown(),stepUp()。

A DOMString表示输入到输入中的日期的值。您可以通过在value属性中包含日期来为输入设置默认值,如下所示:

<label for="party">Enter a date and time for your party booking:</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

您还可以使用该HTMLInputElement.value属性在JavaScript中获取和设置日期值,例如:

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

使用日期时间本地输入

日期/时间输入听起来很方便乍一看 - 他们提供了一个简单的用户界面来选择日期和时间,他们规范化的数据格式发送到服务器,无论用户的地区。但是,<input type="datetime-local">由于浏览器支持有限,因此存在问题。

我们将考虑基本的和更复杂的用途<input type="datetime-local">,然后就减轻浏览器支持问题提供建议(请参阅处理浏览器支持)。

datetime-local的基本用法

最简单的使用<input type="datetime-local">涉及基本<input>和<label>元素组合,如下所示:

<form>     <label for="party">Enter a date and time for your party booking:</label>     <input id="party" type="datetime-local" name="partydate"> </form>

设置最大和最小日期和时间

您可以使用minmax属性来限制用户可以选择的日期/时间。在下面的例子中,我们设置最小日期时间2017-06-01T08:30和最大日期时间为2017-06-30T16:30

  <form>     <label for="party">Enter a date and time for your party booking:</label>     <input id="party" type="datetime-local" name="partydate" min="2017-06-01T08:30" max="2017-06-30T16:30">   </form>

其结果是:

  • 只能选择2017年6月的日期 - 只有日期值的“天”部分可以编辑,6月以外的日期不能滚动到日期选择器窗口小部件中。

  • 根据您使用的浏览器,您可能会发现超出指定值的时间可能无法在时间选择器(如边缘)中选择,或者无效(请参阅验证),但仍可用(例如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>

在这两种情况下,timedate和timezone值将作为单独的数据点提交给服务器,然后您需要将它们适当地存储在服务器端的数据库中。

注意:上面的代码片段取自HTML选择元素中的所有世界时区。

验证

默认情况下,<input type="datetime-local">不对输入的值应用任何验证。UI实现通常不会让你输入任何不是日期时间的东西 - 这是有帮助的 - 但是你仍然可以不填写任何值并提交,或者输入无效的日期时间(例如4月32日)。

您可以使用minmax限制可用的日期(请参阅anch(“设置最大值和最小值日期”)),并使用该required属性在日期中强制填入。因此,如果您尝试提交超出设定范围的日期或空的日期字段,支持的浏览器将显示错误。

我们来看一个例子 - 在这里我们设置了最小和最大的日期时间,并且还要求这个字段:

<form>     <div>         <label for="party">Choose your preferred party date and time (required, June 1st 8.30am to June 30th 4.30pm):</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="Book party!">     </div> </form>

如果您尝试使用不完整的日期提交表单(或使用设定范围外的日期),则浏览器显示错误。现在尝试玩这个例子:

这里是不使用支持浏览器的人的截图:

这是上面例子中使用的CSS。这里我们利用:valid和:invalidCSS属性来根据当前值是否有效来设置输入的样式。我们必须将图标放在<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支持它在桌面上,以及大多数现代浏览器在手机上。作为一个例子,datetime-localAndroid版Firefox 的选择器如下所示:

非支持的浏览器会降级为文本输入, 但这会在用户界面的一致性 (所呈现的控件不同) 和数据处理方面产生问题。

第二个问题是最严重的 - 就像我们之前提到的那样,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 hour clock)

  • mm-dd-yyyy hh:mm (24 hour clock)

  • etc.

One way around this is to put a pattern attribute on your datetime-local input. Even though the datetime-local input doesn't use it, the text input fallback will. For example, try viewing the following demo in a non-supporting browser:

<form>   <div>     <label for="party">Choose your preferred party date and time (required, June 1st 8.30am to June 30th 4.30pm):</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="Book party!">   </div>   <input type="hidden" id="timezone" name="timezone" value="-08:00"> </form>

如果你尝试提交它,你会看到,现在浏览器显示错误信息(并突出了输入为无效)如果您的条目不匹配的模式nnnn-nn-nnTnn:nn,在那里n是一个数字从0到9。当然,这并未不要阻止人们输入无效日期,或者错误地格式化日期和时间。

用户将要了解他们需要输入时间和日期的模式?

我们仍然有一个问题。

目前以跨浏览器的方式处理表单中日期的最好方法是让用户在单独的控件中输入日期,月份,年份和时间(<select>元素正在流行 - 请参阅下面的实现),或者使用JavaScript库,如jQuery日期选择器,和jQuery timepicker插件。

例子

在这个例子中,我们创建了两组用于选择日期时间的UI元素 - 一个本机<input type="datetime-local">选取器,以及一组五个<select>元素,用于在旧版浏览器中选择不支持本机输入的日期和时间。

HTML看起来像这样:

<form>   <div class="nativeDateTimePicker">     <label for="party">Choose a date and time for your party:</label>     <input type="datetime-local" id="party" name="bday">     <span class="validity"></span>   </div>   <p class="fallbackLabel">Choose a date and time for your party:</p>   <div class="fallbackDateTimePicker">     <div>       <span>         <label for="day">Day:</label>         <select id="day" name="day">         </select>       </span>       <span>         <label for="month">Month:</label>         <select id="month" name="month">           <option selected>January</option>           <option>February</option>           <option>March</option>           <option>April</option>           <option>May</option>           <option>June</option>           <option>July</option>           <option>August</option>           <option>September</option>           <option>October</option>           <option>November</option>           <option>December</option>         </select>       </span>       <span>         <label for="year">Year:</label>         <select id="year" name="year">         </select>       </span>     </div>     <div>       <span>         <label for="hour">Hour:</label>         <select id="hour" name="hour">         </select>       </span>       <span>         <label for="minute">Minute:</label>         <select id="minute" name="minute">         </select>       </span>     </div>   </div> </form>

月份是硬编码的(因为它们总是相同的),而日和年的值是根据当前选择的月份和年份以及当年分别动态生成的(请参阅下面的代码注释,了解这些函数的详细解释)。我们也决定动态地生成小时和月份,因为有这么多!

该代码可能会感兴趣的另一部分是特征检测码-来检测浏览器是否支持<input type="date">,我们创建了一个新的<input>元素,设置其type到date,然后立即检查什么的类型设置为-不支持的浏览器将返回text,因为这个date类型会回到类型text。如果<input type="date">不支持,我们隐藏本地选择器并显示后备选取器UI(<select>)。

// define variables 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' // hide fallback initially fallbackPicker.style.display = 'none'; fallbackLabel.style.display = 'none'; // test whether a new date input falls back to a text input or not var test = document.createElement('input' test.type = 'date'; // if it does, run the code inside the if() {} block if(test.type === 'text') { // hide the native picker and show the fallback nativePicker.style.display = 'none'; fallbackPicker.style.display = 'block'; fallbackLabel.style.display = 'block'; // populate the days and years dynamically // (the months are always the same, therefore hardcoded) populateDays(monthSelect.value populateYears( populateHours( populateMinutes( } 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 } // Create variable to hold new number of days to inject var dayNum; // 31 or 30 days? if(month === 'January' | month === 'March' | month === 'May' | month === 'July' | month === 'August' | month === 'October' | month === 'December') { dayNum = 31; } else if(month === 'April' | month === 'June' | month === 'September' | month === 'November') { dayNum = 30; } else { // If month is February, calculate whether it is a leap year or not var year = yearSelect.value; (year - 2016) % 4 === 0 ? dayNum = 29 : dayNum = 28; } // inject the right number of new <option> elements into the day <select> for(i = 1; i <= dayNum; i++) { var option = document.createElement('option' option.textContent = i; daySelect.appendChild(option } // if previous day has already been set, set daySelect's value // to that day, to avoid the day jumping back to 1 when you // change the year if(previousDay) { daySelect.value = previousDay; // If the previous day was set to a high number, say 31, and then // you chose a month with less total days in it (e.g. February), // this part of the code ensures that the highest day available // is selected, rather than showing a blank daySelect if(daySelect.value === "") { daySelect.value = previousDay - 1; } if(daySelect.value === "") { daySelect.value = previousDay - 2; } if(daySelect.value === "") { daySelect.value = previousDay - 3; } } } function populateYears() { // get this year as a number var date = new Date( var year = date.getFullYear( // Make this year, and the 100 years before it available in the year <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 } } // when the month or year <select> values are changed, rerun populateDays() // in case the change affected the number of available days yearSelect.onchange = function() { populateDays(monthSelect.value } monthSelect.onchange = function() { populateDays(monthSelect.value } //preserve day selection var previousDay; // update what day has been set to previously // see end of populateDays() for usage daySelect.onchange = function() { previousDay = daySelect.value; }

规范

SpecificationStatusComments
HTML Living StandardThe definition of '<input type="datetime-local">' in that specification.Living Standard
HTML5The definition of '<input type="datetime-local">' in that specification.Recommendation

浏览器兼容性

FeatureChromeEdgeFirefox (Gecko)Internet ExplorerOperaSafari
Basic support2012No support1No support10.62No support2

FeatureAndroidChrome for AndroidEdgeFirefox Mobile (Gecko)IE MobileOpera MobileSafari Mobile
Basic support(Yes)(Yes)(Yes)(Yes)?(Yes)(Yes)