UNIXタイム変換ツール

入力した内容が保存・記録・共有されることはありません。

UNIXタイムスタンプを読みやすい日時に、またはその逆に変換します。単位は自動で判定され、結果には必ずタイムゾーンが表示されます。

使い方

  1. タイムスタンプ、または 2025-01-01T00:00:00 のような日時を貼り付けます。
  2. 必要に応じてタイムゾーンを選びます。
  3. 必要な形式をコピーします。

秒、ミリ秒、さらに細かい単位

UNIX時間は秒で定義されています。JavaScriptのDateはミリ秒です。複数の言語が混在するコードベースで起きる日付のバグは、ほとんどがこの境界から始まります。

症状は、知っていればすぐにわかります。ミリ秒を期待するところに秒の値を渡すと、1970年1月1日の未明になります。画面に1970年と表示される日付があれば、ほぼ間違いなくこれが原因です。ミリ秒の値を秒として読むと、西暦5万5000年ごろになります。これはたいてい気づかれますが、何も言わずに表示してしまうコードもあるので、常にとは限りません。

ここでは単位を桁数から推定しています。経験則ではありますが、予測可能で説明のつく方法です。現在の日時のタイムスタンプは、秒なら10桁、ミリ秒なら13桁、マイクロ秒なら16桁、ナノ秒なら19桁で、現実に扱うどの日付でも範囲は重なりません。推定した単位は前提にせず表示するので、変更することもできます。マイクロ秒のタイムスタンプはPostgresやChromeのトレースに、ナノ秒はGo、Prometheus、InfluxDBに出てきます。ナノ秒の値はJavaScriptの数値で正確に扱える範囲を超えるため、変換はBigIntで行っています。

タイムスタンプに含まれないもの

UNIXタイムスタンプは瞬間を表すだけで、それ以外は何も持ちません。タイムゾーンも、オフセットも、暦も含みません。東京とリスボンで同じタイムスタンプを読む2人は同じ瞬間を見ており、それぞれ異なる現地時刻を正しく書き留めます。これは欠点ではなく利点です。瞬間には、現地時刻には決してない曖昧さのなさがあります。

その結果、タイムスタンプからは現地で何日だったかがわからず、日付に変換するにはタイムゾーンを選ぶ必要があります。ここでの出力にすべてタイムゾーンを付けているのはそのためで、直接比較できるよう、現地時刻と並べて常にUTCも表示しています。

これはまた、本質的に現地のものを保存するのにタイムスタンプは向かないということでもあります。毎週9時の定例会議、誕生日、店の営業時間。これらはタイムゾーンとその規則に結びついた現地時刻であり、瞬間として保存すると、そのタイムゾーンのオフセットが次に変わったときに狂います。現地時刻とタイムゾーン名を保存し、必要なときに瞬間を計算してください。

うるう秒

UNIX時間は、1日がちょうど86,400秒であるかのように定義されています。地球はそれに合わせてくれないので、UTCを地球の自転に合わせるためにうるう秒が挿入されてきました。1972年以降で27回です。

定義にうるう秒の居場所がないため、UNIX時間はうるう秒を1秒追加するのではなく、同じ値を繰り返すことで処理します。うるう秒そのものを表すUNIXタイムスタンプは存在せず、そのためUNIX時間とTAI(国際原子時)の差は一定ではなく、数十年のあいだに広がってきました。2つのUNIXタイムスタンプの引き算で経過時間を求めるコードは、その間に挿入されたうるう秒の数だけ黙ってずれています。ほとんど問題になりませんが、ごくまれに大問題になります。

大手のプロバイダーはスミアリングでこの不連続を避けています。GoogleとAWSは、追加の1秒をうるう秒の前後の時間帯に分散させ、時計が同じ値を繰り返さないようにしています。その代わり、1日のあいだ時計がごくわずかに遅れます。この時間帯には、スミアリングした時計としていない時計の値が食い違うので、プロバイダーをまたいでタイムスタンプを比較する場合は知っておく価値があります。

2038年問題とそのほかの境界

符号付き32ビット整数は2038年1月19日 3時14分7秒(UTC)で上限に達し、次の1秒で1901年12月に巻き戻ります。組み込みシステム、古いファイル形式、拡張されないままのデータベース列など、UNIX時間を32ビットのフィールドに保存しているものは、そのとき故障します。この時点を過ぎるタイムスタンプにここで注意を表示しているのはそのためです。64ビットで保存すれば上限は宇宙の年齢をはるかに超え、現在のシステムはこれを使っています。

符号なし32ビットで保存すると境界は2106年に移りますが、1970年より前は表せなくなります。負のタイムスタンプはここでも使えます。-86400は1969年12月31日で、グレゴリオ暦の始まりまでさかのぼる日付も問題なく変換できます。JavaScriptのDateにはエポックの前後それぞれ8.64e15ミリ秒、約27万3000年という独自の上限があり、それを超える値はInvalid Dateと表示するのではなく、範囲外として報告します。

テキスト形式と、出力に選ぶべきもの

ISO 8601 は日付と時刻の書き方を定めた国際標準で、RFC 3339 はそれをインターネットプロトコル向けに絞り込んだプロファイルです。API から出力するなら RFC 3339 の形式、つまり 2026-03-14T09:30:00Z、あるいは +09:00 のようなオフセットを付けた形にしてください。文字列のまま並べ替えても正しい順序になり、解釈に曖昧さがなく、どの環境でもパースできるからです。

末尾の Z は UTC を意味し、+09:00 のようなオフセットはそこからの固定のずれを意味します。どちらもタイムゾーンではありません。タイムゾーンとは Asia/Tokyo のようなもので、任意の日付におけるオフセットがいくつになるかを、変更の履歴も含めて定めた規則の集合です。オフセットは時計が何時を指していたかを教えてくれますが、将来その時計が何時を指すかを教えてくれるのはゾーン名だけです。

データとして避けるべき形式は、2 桁の年を使うもの、スラッシュ区切りのもの、そしてメールヘッダー由来の RFC 2822 形式です。RFC 2822 は廃れたゾーン略称を引きずっています。中でも略称が最悪で、CST は北米の Central Standard Time でもあり、China Standard Time でもあり、Cuba Standard Time でもあります。IST に至ってはインド、アイルランド、イスラエルのどれにも使われます。

データベースに時刻を保存する

PostgreSQL の timestamptz は、ほとんどのアプリケーションが求めている型です。名前に反してゾーンそのものは保存せず、書き込み時に入力を UTC へ変換し、読み出し時にセッションのゾーンで表示します。ある瞬間を表す値に対しては、まさにこれが望ましい挙動です。一方、素の timestamp は与えられた壁時計の文字列をゾーンなしでそのまま保存するため、由来の異なる 2 つの値を比較すると、誰にも気づかれないまま無意味な結果が出ます。

MySQL の DATETIME は素の型と同じ振る舞いをし、TIMESTAMP は UTC へ変換しますが、2038 年で終わる 32 ビットの範囲に制限されています。未来の日付を保存するシステムにとっては現実的な制約です。SQLite にはそもそも日付型がなく、選んだ表現がそのまま保存されます。多くは ISO 8601 のテキストか整数です。

こうした差異をすべて乗り越える原則はひとつです。瞬間は UTC で保存し、変換は境界で行い、表示やスケジューリングのために必要なら利用者のゾーンを別の列として持つことです。値が本当に現地の壁時計の時刻である場合、たとえば店舗の営業時間や毎日 09:00 の定例会議などは、現地時刻とゾーン名を保存し、必要になった時点で瞬間を計算してください。オフセットは法改正によって変わり得るからです。

時計は食い違い、ときには逆戻りする

マシンの壁時計は NTP によって調整されるため、前に飛ぶことも、後ろへ戻されることもあります。したがって壁時計の 2 つの値を引き算して経過時間を測ると、ときに負の値が出ますし、日常的に間違った値が出ます。そのために単調増加クロックがあります。決して後戻りせず、performance.now や CLOCK_MONOTONIC を指定した clock_gettime、そしてあらゆるベンチマークライブラリが使っているのはこれです。

マシンをまたぐと問題はさらに悪化します。2 台のサーバーの時計が完全に同期していることはないからです。分散システムでイベントをタイムスタンプ順に並べると、因果関係と矛盾する順序、つまり返信が元のメッセージより前に記録されたような並びが生まれます。論理クロックやシーケンス番号が存在するのはこのためです。

普通のアプリケーションにとっての実践的な助言は、もっと控えめなものです。タイムスタンプを一意な識別子として使わないこと、タイムスタンプが同じ 2 つのイベントが同時に起きたと仮定しないこと、そして別のマシンで記録された値を比べるときは少なくとも数秒のクロックずれを見込むことです。トークンの検証にずれの許容値があるのは、まさにこの理由からです。

人に時刻を見せる

保存された瞬間は、読み手のゾーンで、読み手のロケールの書式で、読み手の言語で表示する必要があります。JavaScript では Intl.DateTimeFormat API がブラウザーの設定からこの 3 つをまとめて処理してくれますし、手作業では間違えがちな細部も面倒を見てくれます。月名を正しい言語で出すこと、日と月の並び順を正しくすること、そして 12 時間制か 24 時間制かを好みではなく慣習に従って選ぶことです。

相対表記、つまり「3 時間前」や「2 日後」といった表現は、最近の出来事には親しみやすい一方、古い出来事には向きません。「431 日前」と言われても誰も動きようがないからです。よくあるやり方は、1 日か 2 日以内は相対表記にし、それ以降は絶対的な日付にして、ホバーや長押しで完全なタイムスタンプを見られるようにすることです。

目に見える違いを生む点が 2 つあります。ひとつは、日付をサーバーのゾーンではなく利用者のゾーンで表示することです。これはつまり、サーバー側で文字列に整形するのではなく、瞬間を送ってクライアント側で整形するということです。もうひとつは、曖昧になり得るものには必ずゾーンを添えることです。ゾーンのない会議時刻ほど確実に誰かを 1 時間遅刻させる方法はありません。

存在しない日付と、その他の落とし穴

夏時間のあるゾーンで 02:30 に何かをスケジュールすれば、毎年必ず障害が起きます。春の切り替えではその時刻が存在せず、秋の切り替えでは 2 回訪れるからです。ちょうど 1 回だけ実行しなければならないジョブは、UTC で動かすか、切り替えが起きる 1 時から 3 時の範囲を外して動かしてください。

暦の計算にも固有の罠があります。1 月 31 日に 1 か月を足した答えは一意に定まらず、ライブラリによって 2 月 28 日になったり 3 月 3 日になったりします。切り替えをまたぐ場合、24 時間を足すことは 1 日を足すことと同じではありません。うるう年は規則全体、つまり 4 で割り切れる年、ただし 100 年単位の年は除き、さらにそのうち 400 で割り切れる年は含める、という条件が必要ですし、2 月 29 日は 4 年に一度、記念日の計算を壊します。

境界も実在します。符号付き 32 ビットの時刻は 2038 年 1 月 19 日で終わり、これは組み込み機器や古いデータベース列では今も問題になります。JavaScript の Date は 1970 年の前後およそ 273,000 年をカバーし、それを超える値は invalid として扱われます。住宅ローンや年金、アーカイブのシステムのように遠い未来の日付を扱うデータなら、どちらも試しておく価値があります。

よくある質問

このタイムスタンプは秒ですか、ミリ秒ですか?
桁数で判断できます。現在の日時なら秒は10桁、ミリ秒は13桁、マイクロ秒は16桁、ナノ秒は19桁です。本ツールは単位を自動判定し、どちらとして扱ったかを表示します。両者の取り違えは日付処理で最も多い不具合で、秒をミリ秒として読むと1970年1月に、ミリ秒を秒として読むと数万年後になってしまいます。
同じタイムスタンプなのに時刻表示が違うのはなぜですか?
UNIXタイムスタンプはタイムゾーンを持たない絶対的な時点です。対応する時計の表示は場所によって変わるため、本ツールでは各出力に必ずタイムゾーンを付記し、UTCも常に併記しています。
2038年問題とは何ですか?
UNIX時刻を符号付き32ビット整数で保持するシステムは、2038-01-19T03:14:07Zでオーバーフローし1901年に戻ってしまいます。本ツールはその時点を超えるタイムスタンプに警告を表示します。64ビットで保持していれば影響はありません。
UNIX時刻にうるう秒は含まれますか?
含まれません。UNIX時刻は1日を常に86,400秒として定義しているため、うるう秒はカウントされず読み飛ばされます。そのためうるう秒そのものを表現できず、TAIとの変換も単純なオフセットにはなりません。
1970年より前の日付も扱えますか?
はい。エポック以前は負の値になり、-86400は1969年12月31日を表します。

最終更新:

問題を報告

操作手順、期待した結果、実際の結果を記載してください。秘密情報や個人情報は削除してください。

関連ツール