Unixタイムスタンプとは?仕組みと日付変換をわかりやすく解説
Unixタイムスタンプとは、1970年1月1日00:00:00 UTC(Unixエポックと呼ばれる基準時刻)から経過した秒数を表す数値です。たとえば1700000000は2023年11月14日22:13:20 UTCを指しており、この一つの整数だけで年月日と時刻、そしてタイムゾーンの情報までまとめて表現できます。データベースのcreated_atカラムやAPIレスポンス、cronジョブの実行時刻、JWTのexpクレームなど、時刻を扱うほぼすべての場所でこの形式を目にします。
Unixタイムスタンプの正体
Unixタイムスタンプの実体は、ただの整数です。1970年1月1日00:00:00 UTCを0として、そこから1秒経過するごとに1ずつ増えていきます。うるう秒の扱いなど細かな例外はあるものの、基本的な考え方はそれだけです。
なぜコンピュータはこんな単純な数値を使うのでしょうか。「2026年7月11日 14時30分」のような文字列は人間には読みやすい一方で、コンピュータにとっては扱いにくい形式です。文字列同士を比較して時間の前後を判定するには文字列比較の細かなルールに頼る必要があり、月名や曜日名の表記も言語やロケールによってばらばらになります。タイムゾーンの表記が省略されていれば、それがローカル時間なのかUTCなのかも文字列だけでは判断できません。これに対して整数のタイムスタンプなら、2つの値を引き算するだけで経過秒数がそのまま求まり、大小を比較するだけで前後関係が分かり、そもそもタイムゾーンという概念自体が存在しないため曖昧さが生まれません。ログのソートやAPIレスポンスのシリアライズで整数が好まれるのは、このコンパクトさと明確さのためです。
実際の値を読み解いてみる
具体的な数値と、それが指すUTC日時の対応を見てみましょう。以下はすべて実際に変換した正確な値です。
| Unixタイムスタンプ(秒) | UTC日時 |
|---|---|
| 0 | 1970年1月1日 00:00:00 UTC(エポックそのもの) |
| 1000000000 | 2001年9月9日 01:46:40 UTC |
| 1700000000 | 2023年11月14日 22:13:20 UTC |
| 2000000000 | 2033年5月18日 03:33:20 UTC |
| 2147483647 | 2038年1月19日 03:14:07 UTC |
1700000000がなぜ2023年11月に着地するのか、感覚を掴んでおくとタイムスタンプが身近になります。1年はおよそ31,536,000秒(365日 × 24時間 × 3600秒、うるう年を含めるとさらに数日分増えます)です。1970年から数えて53年ほど経過すると、53 × 31,536,000はおよそ16億〜17億秒になり、1,700,000,000という値のオーダーとちょうど一致します。秒数がちょうど10桁に達するのは2001年9月9日(表の1000000000がその瞬間です)からで、それ以降の値はしばらくの間ずっと10桁のまま増え続けます。
秒とミリ秒、どう見分けるか
タイムゾーンの次によくある混乱が、秒とミリ秒の取り違えです。現在の時代では、秒単位のUnixタイムスタンプはちょうど10桁になります。1700000000がその例です。同じ瞬間をミリ秒単位で表すと1700000000000となり、桁数は13桁に増えます。どちらも2023年11月14日22:13:20 UTCという同じ一瞬を指しているだけで、単位が1000倍違うにすぎません。
JavaScriptのDate.now()や多くのブラウザAPI、Web APIのレスポンスはミリ秒を返します。一方でUnix系のシステムコール、cronのタイムスタンプ、そしてほとんどのUnix/Linuxコマンドラインツールは秒を基準にしています。ミリ秒の値を秒だと思い込んでツールに貼り付けると、1000倍のずれが生じて実際の日時よりはるか未来、具体的には数万年先の日付として解釈されてしまいます。JWTのexpクレームに秒単位を渡すべきところをミリ秒の値のまま設定してしまうと、トークンの有効期限が意図せず何千年も先になり、実質的に無期限のトークンを発行してしまうことにもなりかねません。
タイムスタンプを日付に変換する
ログの一行やデータベースのcreated_atカラムに並んだ生の数値を見て、それが何年何月何日を指しているのか即座に判断するのは簡単ではありません。下の入力欄に値を貼り付ければ、UTC・ローカル時間・ISO 8601・「3日前」のような相対表記まで、その場で変換結果を確認できます。
日付をタイムスタンプに変換する
逆方向の変換が必要になる場面もあります。たとえば特定の日時にcronジョブを実行させたい、あるいはAPIのリクエストパラメータとして特定の日付をUnixタイムスタンプで渡したいといったケースです。カレンダーから日付と時刻を選ぶだけで、秒とミリ秒の両方の値がすぐに得られます。
よくある間違いと注意点
秒とミリ秒を取り違える。 前述の通り、秒のタイムスタンプは10桁、ミリ秒のタイムスタンプは13桁です。API開発中にどちらの単位を受け取っているか確認せずに計算処理を書いてしまうと、桁数が3つずれるだけで日付が大きく狂います。
2038年問題。 32ビットの符号付き整数でタイムスタンプを扱っているシステムは、2147483647(2038年1月19日03:14:07 UTC)でオーバーフローを起こします。この値を1秒でも超えると、内部的には負の巨大な数値に折り返ってしまい、日付の前後関係を判定するロジックが壊れます。古いCの構造体やレガシーなファイルフォーマットの一部には今もこの32ビット表現が残っています。64ビットでタイムスタンプを扱う現代的なシステムには、この上限はありません。
タイムゾーンとの混同。 Unixタイムスタンプそのものにはタイムゾーンという概念がありません。あくまである瞬間を絶対的に指し示す数値です。混乱が生じるのは、その数値をどう「表示」するかの段階です。東京にいる人とロサンゼルスにいる人が同じタイムスタンプを見ても、時計の針が示す時刻はまったく異なりますが、指している瞬間そのものは完全に同じです。ログを突き合わせるときは、双方が同じタイムゾーンで表示しているかを必ず確認してください。
負のタイムスタンプ。 マイナスの値は1970年1月1日より前の日時を表します。たとえば-86400は1969年12月31日00:00:00 UTCです。すべてのツールやシステムが負の値をサポートしているわけではなく、日付入力欄が1970年以前を受け付けないケースや、負の値を渡すとエラーになるライブラリも珍しくないため、過去の日付を扱う際は事前に確認が必要です。
よくある質問
Unixタイムスタンプとは何ですか? 1970年1月1日00:00:00 UTC(Unixエポック)から経過した秒数を表す数値です。タイムゾーンに依存しない、コンピュータが時刻を扱うための普遍的な形式として、プログラミングのほぼすべての場面で使われています。
自分のタイムスタンプが秒かミリ秒か、どう判断すればいいですか? 現在の日付を表す値であれば、桁数を数えるのが一番早い方法です。秒単位なら10桁(例: 1700000000)、ミリ秒単位なら13桁(例: 1700000000000)になります。9,999,999,999以下の値は秒、それより大きければミリ秒とみなしてほぼ間違いありません。
2038年問題とは何ですか? 32ビットの符号付き整数でUnixタイムスタンプを保持しているシステムが、2147483647(2038年1月19日03:14:07 UTC)に達した時点でオーバーフローし、内部的に負の値へ折り返ってしまう問題です。日付比較や有効期限判定のロジックが誤動作する可能性があります。64ビットで扱う現代的なシステムはこの制限を受けません。
1970年より前の日付を変換できますか? はい。負のUnixタイムスタンプは1970年1月1日より前の日時を表し、正しく計算できます。ただし、すべてのツールやライブラリが負の値をサポートしているわけではないため、古いデータを扱うシステムでは事前に対応状況を確認しておくと安全です。
なぜデータベースは日付を文字列ではなくUnixタイムスタンプで保存するのですか? 整数は文字列より保存領域が小さく、比較やソートも純粋な数値演算で済むため高速です。さらにタイムゾーンに依存しないので、異なる地域のサーバーやクライアント間でデータをやり取りしても解釈のズレが起きません。表示用のフォーマットは、必要になったときにアプリケーション側でいくらでも作り直せます。