datetime-local 日期时间输入
<input type="datetime-local"> 是 HTML5 引入的日期时间组合选择控件,允许用户在同一个输入框中选择日期和时间。该控件将 date 和 time 的功能合二为一,提交值格式为 YYYY-MM-DDTHH:MM,适用于需要同时选择日期和时间的场景。
前置知识
阅读本节前,建议先了解:time 时间输入
基础概念
什么是 datetime-local 输入
type="datetime-local" 输入框提供了日期和时间的组合选择界面。与分别使用 date 和 time 两个控件相比,datetime-local 的特点包括:
- 一体化控件:日期和时间在同一个控件中完成选择
- 紧凑界面:减少表单中的控件数量
- 格式统一:提交值为
YYYY-MM-DDTHH:MM或YYYY-MM-DDTHH:MM:SS - 不含时区信息:名称中的 "local" 表示使用本地时区,不包含 UTC 偏移量
- 移动端友好:在移动设备上提供原生的日期时间选择器
与旧版 datetime 的区别
HTML5 早期规范中曾定义 <input type="datetime">,但已被废弃。两者的关键区别:
| 特性 | datetime(已废弃) | datetime-local(当前标准) |
|---|---|---|
| 时区 | 包含 UTC 时区偏移 | 不含时区信息(本地时间) |
| 格式 | YYYY-MM-DDTHH:MMZ | YYYY-MM-DDTHH:MM |
| 浏览器支持 | 几乎无浏览器实现 | 现代浏览器均支持 |
| 状态 | 从规范中移除 | 活跃标准 |
浏览器支持
| 浏览器 | 支持情况 | 控件外观 |
|---|---|---|
| Chrome | ✅ 完整支持 | 日历 + 时间选择 |
| Firefox | ✅ 支持(93+) | 日历 + 时间输入 |
| Safari | ✅ 支持(14.1+) | 日历 + 时间滚轮 |
| Edge | ✅ 完整支持 | 日历 + 时间选择 |
| Opera | ✅ 完整支持 | 日历 + 时间选择 |
提交值格式
YYYY-MM-DDTHH:MM —— 日期和分钟
YYYY-MM-DDTHH:MM:SS —— 包含秒例如:2025-07-14T14:30 表示本地时间 2025 年 7 月 14 日 14:30。
语法
基本用法
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>datetime-local 基本示例</title>
</head>
<body>
<h2>选择日期和时间</h2>
<form>
<label for="event-time">活动时间:</label>
<input type="datetime-local" id="event-time" name="event_time">
<button type="submit">提交</button>
</form>
</body>
</html>设置默认值
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>datetime-local 默认值</title>
</head>
<body>
<h2>设置默认日期时间</h2>
<form>
<label for="deadline">截止时间:</label>
<!-- 设置默认值为 2025-08-01 18:00 -->
<input
type="datetime-local"
id="deadline"
name="deadline"
value="2025-08-01T18:00"
>
<button type="submit">保存</button>
</form>
</body>
</html>min 和 max 属性
限制可选日期时间范围:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>datetime-local 范围限制</title>
</head>
<body>
<h2>预约时间范围</h2>
<form>
<label for="appointment">预约时间:</label>
<input
type="datetime-local"
id="appointment"
name="appointment"
min="2025-07-15T09:00"
max="2025-12-31T18:00"
required
>
<button type="submit">提交预约</button>
</form>
</body>
</html>| 属性 | 说明 | 格式 |
|---|---|---|
min | 最小可选日期时间 | YYYY-MM-DDTHH:MM |
max | 最大可选日期时间 | YYYY-MM-DDTHH:MM |
详细说明
step 步进控制
step 属性的单位为秒,控制时间精度:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>datetime-local step 示例</title>
</head>
<body>
<h2>步进控制</h2>
<form>
<div>
<label for="dt-minute">分钟精度(默认):</label>
<input type="datetime-local" id="dt-minute" name="dt_minute" step="60">
</div>
<div>
<label for="dt-second">秒级精度:</label>
<input type="datetime-local" id="dt-second" name="dt_second" step="1">
</div>
<div>
<label for="dt-hour">整点精度(1小时=3600秒):</label>
<input type="datetime-local" id="dt-hour" name="dt_hour" step="3600">
</div>
</form>
</body>
</html>| step 值(秒) | 含义 |
|---|---|
1 | 1 秒精度 |
60(默认) | 1 分钟精度 |
900 | 15 分钟精度 |
3600 | 1 小时精度 |
86400 | 1 天精度 |
any | 不限制步进 |
valueAsDate 和 valueAsNumber
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>datetime-local 值属性</title>
</head>
<body>
<h2>获取日期时间值</h2>
<label for="my-dt">选择日期时间:</label>
<input type="datetime-local" id="my-dt" name="my_dt" value="2025-07-14T14:30">
<div id="output"></div>
<script>
const dtInput = document.getElementById('my-dt');
const output = document.getElementById('output');
dtInput.addEventListener('input', function () {
// value 字符串
const str = this.value; // "2025-07-14T14:30"
// valueAsDate — 返回本地时区的 Date 对象
const date = this.valueAsDate;
// valueAsNumber — 返回 Unix 时间戳(毫秒)
const timestamp = this.valueAsNumber;
if (date) {
const year = date.getFullYear();
const month = date.getMonth() + 1;
const day = date.getDate();
const hour = date.getHours();
const minute = date.getMinutes();
output.innerHTML = `
<p>value: ${str}</p>
<p>可读格式: ${year}年${month}月${day}日 ${hour}:${minute.toString().padStart(2, '0')}</p>
<p>Unix 时间戳: ${timestamp}</p>
`;
}
});
</script>
</body>
</html>JavaScript 动态设置当前时间
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>动态设置 datetime-local</title>
</head>
<body>
<h2>动态设置日期时间</h2>
<form>
<label for="now">当前日期时间:</label>
<input type="datetime-local" id="now" name="now">
<label for="tomorrow">明天此刻:</label>
<input type="datetime-local" id="tomorrow" name="tomorrow">
</form>
<script>
// 获取当前本地时间的 datetime-local 格式字符串
function toLocalISOString(date) {
const offset = date.getTimezoneOffset();
const local = new Date(date.getTime() - offset * 60000);
return local.toISOString().slice(0, 16);
}
// 设置当前时间
const nowInput = document.getElementById('now');
nowInput.value = toLocalISOString(new Date());
// 设置明天同一时间
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
document.getElementById('tomorrow').value = toLocalISOString(tomorrow);
</script>
</body>
</html>与 date + time 组合的对比
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>datetime-local vs date + time</title>
</head>
<body>
<h2>两种方式对比</h2>
<h3>方式一:datetime-local(一体化)</h3>
<form>
<label for="dt">出发时间:</label>
<input type="datetime-local" id="dt" name="departure_dt" required>
<button type="submit">提交</button>
</form>
<h3>方式二:date + time(分离)</h3>
<form>
<label for="d">出发日期:</label>
<input type="date" id="d" name="departure_date" required>
<br>
<label for="t">出发时间:</label>
<input type="time" id="t" name="departure_time" required>
<button type="submit">提交</button>
</form>
<table border="1" cellpadding="8" style="margin-top: 16px;">
<tr>
<th>对比维度</th>
<th>datetime-local</th>
<th>date + time</th>
</tr>
<tr>
<td>控件数量</td>
<td>1 个</td>
<td>2 个</td>
</tr>
<tr>
<td>提交数据</td>
<td>departure_dt=2025-07-14T14:30</td>
<td>departure_date=2025-07-14&departure_time=14:30</td>
</tr>
<tr>
<td>用户操作</td>
<td>一步完成</td>
<td>两步完成</td>
</tr>
<tr>
<td>灵活性</td>
<td>较低</td>
<td>较高(可分别设置 min/max)</td>
</tr>
<tr>
<td>适用场景</td>
<td>简单日期时间选择</td>
<td>需要独立控制日期和时间范围</td>
</tr>
</table>
</body>
</html>实战示例
活动发布表单
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>活动发布</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
max-width: 700px;
margin: 40px auto;
padding: 0 20px;
}
.form-group { margin-bottom: 16px; }
label { display: block; margin-bottom: 6px; font-weight: 600; }
input, textarea {
width: 100%;
max-width: 400px;
padding: 8px 12px;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 16px;
box-sizing: border-box;
}
input:focus, textarea:focus {
border-color: #e67e22;
outline: none;
}
.preview {
margin-top: 20px;
padding: 16px;
background: #fef5e7;
border-radius: 8px;
display: none;
}
button {
padding: 10px 24px;
background: #e67e22;
color: #fff;
border: none;
border-radius: 6px;
cursor: pointer;
}
button:hover { background: #d35400; }
</style>
</head>
<body>
<h2>发布活动</h2>
<form id="event-form">
<div class="form-group">
<label for="event-name">活动名称:</label>
<input type="text" id="event-name" name="event_name" required>
</div>
<div class="form-group">
<label for="start-dt">开始时间:</label>
<input type="datetime-local" id="start-dt" name="start_dt" required>
</div>
<div class="form-group">
<label for="end-dt">结束时间:</label>
<input type="datetime-local" id="end-dt" name="end_dt" required>
</div>
<div class="form-group">
<label for="event-desc">活动描述:</label>
<textarea id="event-desc" name="description" rows="3"></textarea>
</div>
<button type="button" id="preview-btn">预览</button>
<button type="submit">发布</button>
</form>
<div class="preview" id="preview">
<h3>活动预览</h3>
<p id="preview-content"></p>
</div>
<script>
const startDt = document.getElementById('start-dt');
const endDt = document.getElementById('end-dt');
const preview = document.getElementById('preview');
const previewContent = document.getElementById('preview-content');
// 设置开始时间最小值为当前
function toLocalISOString(date) {
const offset = date.getTimezoneOffset();
const local = new Date(date.getTime() - offset * 60000);
return local.toISOString().slice(0, 16);
}
startDt.min = toLocalISOString(new Date());
// 开始时间变化时,更新结束时间的最小值
startDt.addEventListener('change', function () {
const start = new Date(this.value);
// 结束时间至少比开始时间晚 30 分钟
start.setMinutes(start.getMinutes() + 30);
endDt.min = toLocalISOString(start);
if (endDt.value && new Date(endDt.value) <= new Date(this.value)) {
endDt.value = toLocalISOString(start);
}
});
// 预览功能
document.getElementById('preview-btn').addEventListener('click', function () {
const name = document.getElementById('event-name').value;
const desc = document.getElementById('event-desc').value;
if (startDt.value && endDt.value) {
preview.style.display = 'block';
previewContent.innerHTML = `
<strong>${name}</strong><br>
开始:${startDt.value.replace('T', ' ')}<br>
结束:${endDt.value.replace('T', ' ')}<br>
${desc ? '描述:' + desc : ''}
`;
}
});
</script>
</body>
</html>注意事项
时区问题
datetime-local 不包含时区信息,这既是优点也是潜在陷阱:
javascript
// 假设用户在北京时间(UTC+8)选择了 2025-07-14T14:30
const dtInput = document.getElementById('my-dt');
// value 字符串不含时区
console.log(dtInput.value); // "2025-07-14T14:30"
// valueAsDate 返回本地时区的 Date 对象
const date = dtInput.valueAsDate;
console.log(date.toISOString()); // "2025-07-14T06:30:00.000Z"(UTC 时间)
console.log(date.toString()); // 本地时间的完整字符串WARNING
如果需要在服务端处理时区,建议服务端约定时区规则,或配合 <select> 单独提供时区选择。
移动端体验
在 iOS 和 Android 上,datetime-local 触发原生日期时间选择器,体验流畅。但部分老旧 Android 设备可能降级为文本输入。
设置默认值为当前时间的技巧
JavaScript 的 Date.toISOString() 返回 UTC 时间,直接使用会导致时区偏差:
javascript
// 错误方式(会差 8 小时)
input.value = new Date().toISOString().slice(0, 16);
// 正确方式(转换为本地时间)
function toLocalISOString(date) {
const offset = date.getTimezoneOffset();
const local = new Date(date.getTime() - offset * 60000);
return local.toISOString().slice(0, 16);
}
input.value = toLocalISOString(new Date());readonly 和 disabled
html
<!-- 只读:显示但不能编辑,值会提交 -->
<input type="datetime-local" name="created_at" value="2025-07-14T10:00" readonly>
<!-- 禁用:灰色显示,值不会提交 -->
<input type="datetime-local" name="expired_at" value="2025-07-14T10:00" disabled>最佳实践
- 选择合适的控件:简单日期时间选择用
datetime-local,需要精细控制用date+time分离 - 动态设置 min:使用 JavaScript 设置最小值为当前时间,避免选择过去的时间
- 正确处理时区:获取/设置
valueAsDate时注意时区转换 - 提供预览:日期时间对用户来说不够直观,提供可读的预览信息
- 设置合理步进:根据场景选择精度,如日程安排用 30 分钟,航班时刻用 5 分钟
- 添加说明文字:在控件旁注明时间格式和时区信息
下一节
继续学习:month 月份输入