HTML

A tag for creating a time selection input field.

<input type="time">

はじめに

HTML5で導入された<input type="time">は、ユーザーに時刻(例:14:30など)を簡単かつ正確に入力してもらうためのフォーム部品です。この要素を使うことで、従来のテキスト入力と比べて入力ミス(例:形式の間違いなど)を防ぎやすくなります。また、スマートフォンなどのモバイル端末では、画面上に専用の時刻ピッカーUI(スピナーやホイール型の選択UI)が自動的に表示され、指先で直感的に時刻を選べるようになります。一方、デスクトップ環境では、スピンボックス(数値の上下ボタン)やドロップダウン形式など、プラットフォームごとに最適化されたUIが提供されるため、JavaScriptを使って一からインターフェースを構築するよりも圧倒的に簡単に導入できます。

ただし、この<input type="time">「日付の情報を含まない時刻のみ(ローカル時刻)」を扱うため、日付やタイムゾーンの指定はできません。さらに、「秒(SS)」の入力や表示にも一部のブラウザでは非対応であるなど、あまり知られていない制限が存在します。こうした仕様は、初心者にとっては気づきにくい一方で、上級者でも設計段階で見落としやすく、思わぬ不具合の原因となることがあります。

そのため、この要素を使用する際には、ブラウザごとの対応状況や仕様の制限を正しく理解したうえで、用途に応じた適切な使い方を心がけることが重要です。

最小構成 ―― “まず動かす”

HTML

<label for="alarm">アラーム時刻:</label>
<input id="alarm" type="time" name="alarm" required>

ここまでで バリデーション(時刻が空なら送信ブロック)と キーボード操作 が既に利用可能です。ブラウザは自動で 24 時間 or 12 時間表示を決めるため、ユーザー側は locale を意識せずに済みます。

主要属性と振る舞い

value

min / max

HTML

<input type="time" min="09:30" max="17:00">

step

HTML

<input type="time" step="900"> <!-- 15 分刻み -->

required, disabled, readonly, autofocus, autocomplete, list

注意 : placeholder はレンダリングされません。空欄時でもピッカー UI が前面に出るため、視覚的ヒントは <label> や外部テキストで補う必要があります。

JavaScript API

JavaScript

const el = document.querySelector('#alarm');

// 文字列として取得 / 設定
console.log(el.value);        // "07:45" など
el.value = '08:00';

// 数値 (ミリ秒) で扱いたい場合
console.log(el.valueAsNumber); // 27000000 = 7.5h * 3600 s * 1000ms
el.valueAsNumber += 15 * 60 * 1000; // 15 分進める

// Date オブジェクト化(UTC 1970-01-01 基準)
const d = el.valueAsDate;       // 1970-01-01T07:45:00.000Z

ブラウザ間差異がやや大きいため、数値演算を伴う場合は valueAsNumber を基準にして丸め誤差を防ぎます。

フォーム送信とサーバーサイド処理

PHP

PHP

$time = $_POST['alarm'] ?? '';      // "07:45"
if (preg_match('/\A([01]\d|2[0-3]):[0-5]\d\z/', $time)) {
    [$h, $m] = explode(':', $time);
    $seconds = $h * 3600 + $m * 60;
}

Node.js (Express)

JavaScript

app.post('/reserve', (req, res) => {
	const { start } = req.body;           // "18:30"
	if (!/^\d{2}:\d{2}$/.test(start)) return res.status(400).end();
	const [h, m] = start.split(':').map(Number);
	const seconds = h * 3600 + m * 60;
});

重要ポイント : <input type="time"> 自体には タイムゾーン情報が含まれません。ユーザーのローカル時刻として解釈し、サーバー到着後にアプリケーション固有の TZ(例: Asia/Tokyo)へマッピングする必要があります。

アクセシビリティ & UX

ラベル
for 属性で結びつける。スクリーンリーダーはラベルを読み上げ、操作モードを告知
エラーメッセージ
aria-invalid="true"aria-describedby でカスタムメッセージを紐付け
フォーカスリング
ブラウザ既定の outline を尊重しつつ、CSS でコントラストを高める
キーボード
ArrowUp/Down で分・時間を増減、Home/End で最小/最大にジャンプ(ブラウザ依存あり)

スタイリングのコツ

CSS

/* アイコンを非表示にして独自 UI だけを見せる例 (Chrome / Edge) */
input[type="time"]::-webkit-calendar-picker-indicator {
	opacity: 0;
	pointer-events: none;
}

/* フォーカス時に時計アイコンをスライドインさせるイメージ */
input[type="time"]:focus-visible + .clock-icon {
	transform: translateX(0); /* 初期は -100% で隠す */
}

Firefox は ::-moz-focus-innerappearance で、Safari は現状カスタムしづらい点に注意。完全なクロスブラウザデザイン を目指すなら、隠さずネイティブ UI を活かす プログレッシブエンハンスメント戦略が推奨されます。

スマートフォン実装で押さえるポイント

デフォルト UI の違いを理解する

iOS は回転式ホイール、Android はダイヤル/キーパッド型など OS ごとにピッカー UI が異なります。これを前提に「完璧に同じ UI」を強要せず、OS ネイティブの操作感を尊重する方がアクセシビリティと保守性を高めます。

ネイティブのピッカーが不都合なケース(秒まで入力したい・カスタムテーマを当てたい等)は、<input type="time"> を隠し、アクセシブルなカスタムピッカー(例:dialogrole="dialog"aria-modal="true"+ボタン操作)を提供しつつ、値は隠れた input に同期させる方式が安全です。

step・min・max 属性でタップ数を最小化

モバイルでは「数タップ増えるだけ」で離脱率が跳ね上がります。

15 分単位で良いなら step="900"(= 15 min)を指定し、ピッカー側で不要な時刻をスキップさせて入力コストを削減します。

営業時間など範囲が決まっている場合は min="09:00" / max="18:00" を設定し、範囲外の候補をそもそも表示させないことで誤入力を防ぎます。

24 h/12 h 表記の自動切替にまかせる

時刻表記は ブラウザのロケールと端末の時刻設定に従って自動で 24 h / 12 h が切り替わります。

アプリ側で強制したい場合はカスタムピッカーに切り替えるしかありませんが、ユーザーの慣れた表記を壊すリスクが高いので「表示はローカライズ任せ、内部ロジックは常に HH:MM 24 h で扱う」設計が推奨されます。

バリデーションは「入力直後」+「送信前」の二段階

タッチ UI ではキーボードを閉じた瞬間に即時フィードバックを返さないと再入力が面倒になります。input イベントで簡易チェック → invalid / submit で確定チェックの二層に分け、エラーは フィールド直下に短文で表示。

フォーカス移動時にスクロール位置がずれやすいので、エラー要素へ scrollIntoView({block:"center"}) を添えると UX が安定します。

タッチ目標サイズとスペース確保

ガイドライン目安は 幅・高さともに 48 px(9 mm)以上、間隔 8 px 以上。

インラインで <input type="time"> を置く場合でも、ピッカー呼び出しアイコン・ラベル含めてタップ領域を確保し、意図しない隣接要素タップを避けます。

フォールバック戦略を持つ

今も一部の 旧 Android WebView/組み込みブラウザ は type="time" をサポートしません。非対応環境では type="text" として振る舞うため、プレースホルダーにフォーマット例(例: 09:30)を必ず入れるか、先頭で pattern="^([01]\d|2[0-3]):[0-5]\d$" を与えて手動入力を保護します。

さらに JavaScript でサポート検出を行い、未サポートなら軽量なポリフィル(inputmask・tiny-date-picker など)を動的読み込みすると UX が保たれます。

仮想キーボードのレイアウト崩れ対策

<input> が画面下部にある場合、キーボード出現でピッカーが隠れることがあります。

重要フォームでは scroll-padding-bottomenv(safe-area-inset-bottom)+80 px などを指定し、ピッカー or キーボード分だけ自動でスクロール余白を確保するとレイアウトジャンプを防げます。

ダークモード/高コントラストモード対応

モバイルは OS ダークモード比率が高いので、カスタムピッカーを実装する場合は prefers-color-scheme で配色を分岐。

システム UI をそのまま使う場合でも、隣接する入力枠・ラベルが背景と同化しないよう color-scheme: light dark;background-color: Canvas 相当の変数を併用すると一貫性が高まります。

フォームの自動ズーム抑止

iOS Safari はフォントサイズ 16 px 未満の入力要素をタップすると自動ズームします。ズーム→ピッカー表示→戻る、の流れはユーザーが迷子になりやすいので、入力要素のフォントは必ず 16 px 以上に設定し、ズーム発生を防ぎましょう。

高速化とパフォーマンス

モバイルでは JavaScript 実行コストが直にバッテリーと熱に跳ね返ります。

カスタムピッカーの場合でも Shadow DOM+CSS Animation で極力 DOM 操作を減らし、スクロール同期や時刻リスト生成は requestAnimationFrame 内で行うとフレーム落ちが起きにくくなります。

フォント読み込み遅延があるとミリ秒単位で表示がブレるので、font-display: swap 指定で CLS を抑えてください。


これらを踏まえて実装すれば、OS 標準ピッカーの利点を活かしつつ、非対応環境や視認性・操作性の問題にも備えたモバイル向け<input type="time"> UI が完成します。

Fallback / Polyfill 戦略 ―― 旧ブラウザ & 秒単位対応

機能検出:

JavaScript

const supportsTime = (() => {
	const i = document.createElement('input');
	i.setAttribute('type', 'time');
	return i.type === 'time';
})();

flatpickr + timePlugin などの軽量ライブラリを条件付きで読み込む

pattern="\d{2}:\d{2}", inputmode="numeric" を併用し、最低限のモバイル対応を担保

秒が必要なら hidden フィールドに格納し、UI は polyfill 上で実装する

よくある落とし穴

高度なユースケース

React で “Controlled” に扱う

JavaScript

export default function TimeField() {
	const [time, setTime] = useState('12:00');
	return (
		<>
			<label htmlFor="start">開始時刻</label>
			<input
				id="start"
				type="time"
				value={time}
				onChange={e => setTime(e.target.value)}
				step={300}
				min="09:00"
				max="21:00"
			/>
			<span>{`→ 秒換算: ${toSeconds(time)}s`}</span>
		</>
	);
}

秒への換算は正規表現+分割で関数化して再利用するとバグを抑制できます。

Web Components でカスタム要素化

Shadow DOM 内に <input type="time"> をラップし、observedAttributesmin/max/step を動的管理。Form Associated Custom Elements を使えば <form> とネイティブ同等に連携可能です。

テストと QA チェックリスト

フォーマット検証
自動テストで /\A([01]\d|2[0-3]):[0-5]\d\z/ を回す
ロケール差
英語・日本語・アラビア語 UI でラベルオーバーフローを確認
アクセシビリティ
キーボードのみで開始→値入力→送信→エラー確認の一連を完走
境界ケース
min=09:00 step=360009:59 を入力 → エラー通知されるか
サマータイム
TZ が DST 切替日に跨る場合、アプリ側でローカル値がずれないか

まとめ

<input type="time"> は ローカル時刻用の軽量 UI を半自動で提供してくれる便利要素ですが、

といった制約を理解し、バリデーション・アクセシビリティ・フォールバック を組み合わせてはじめて “頑丈” なコンポーネントになります。

実運用では サーバー側での正規化 と テスト自動化 を最優先に据え、ユーザー体験を壊さないラインでネイティブ UI を活かしましょう。