Unix時間の基本:1970年から2038年まで
2026年11月更新
1767225600のようなUnixタイムスタンプは、決まった起点であるUnixエポック、つまり1970年1月1日00:00:00(UTC)からの経過秒数を数えたものです。この数字はちょうど2026年1月1日0時(UTC)にあたります。タイムスタンプはタイムゾーンを持たないただの数値なので、コンピューターが時刻を保存したりやり取りしたりするときの最も一般的な方法になっています。このガイドでは、タイムスタンプの読み方、主要な言語での正しい変換方法、そして実際にバグの原因になるミリ秒、タイムゾーン、うるう秒、2038年問題について説明します。
Unix時間のしくみ
Unix時間は1秒ごとに1ずつ増え、同じ瞬間であれば地球上のどこでも同じ値になります。目安になる値をいくつか見てみましょう。
| タイムスタンプ | 日時(UTC) | 備考 |
|---|---|---|
0 | 1970-01-01 00:00:00 | エポック |
86400 | 1970-01-02 00:00:00 | 1日は86,400秒 |
-86400 | 1969-12-31 00:00:00 | 負の値は1970年より前 |
1000000000 | 2001-09-09 01:46:40 | 初めて10桁になったタイムスタンプ |
1767225600 | 2026-01-01 00:00:00 | 以下の例で使用 |
2000000000 | 2033-05-18 03:33:20 | |
2147483647 | 2038-01-19 03:14:07 | 符号付き32ビット整数の最大値 |
Unix時間では1日が常にちょうど86,400秒なので、計算が簡単です。1,767,225,600 ÷ 86,400 = 20,454なので、2026年1月1日はエポックから20,454日目にあたり、タイムスタンプに7 × 86,400を足せばちょうど1週間後になります。ただし、現地の時刻となると話は別で、それはタイムゾーンの節で説明します。
秒、ミリ秒、さらに細かい単位
本来のUnixの慣習では秒単位で数えますが、もっと細かい単位を使うシステムもたくさんあります。たいていは桁数で見分けられます。2001年から2286年までの日付なら、秒単位のタイムスタンプは10桁です。
| 桁数 | 単位 | 主な使われ方 | 2026年1月1日0時(UTC) |
|---|---|---|---|
| 10 | 秒 | Unixのシェル、PHP、ほとんどのデータベース、JWTのexpクレーム | 1767225600 |
| 13 | ミリ秒 | JavaScriptのDate.now()、JavaのSystem.currentTimeMillis() | 1767225600000 |
| 16 | マイクロ秒 | 一部のAPI、ログ、分析システム | 1767225600000000 |
| 19 | ナノ秒 | GoのUnixNano()、Pythonのtime.time_ns() | 1767225600000000000 |
単位の取り違えは、とてもよくあるバグです。ミリ秒を秒として読むと何万年も先の日付になり、秒をミリ秒として読むと1970年1月に戻ってしまいます。変換した日付が1970年になったら、まずこれを疑いましょう。Unixタイムスタンプ変換ツールは、桁数から単位を自動で判別します。
タイムゾーンとUTC
タイムスタンプにはタイムゾーンがありません。ある瞬間を指しているだけで、タイムゾーンが関係するのは表示するときだけです。タイムスタンプ1767225600は、次のようになります。
- ロンドン:2026年1月1日 00:00(冬はUTC+0)
- 上海:2026年1月1日 08:00(UTC+8)
- 東京・ソウル:2026年1月1日 09:00(UTC+9)
- ニューヨーク:2025年12月31日 19:00(冬はUTC−5)
ここから、よい習慣が見えてきます。
- 時刻はUTCで保存し、UTCでやり取りしましょう。Unixタイムスタンプか、
Zまたはオフセットで終わるISO 8601形式の文字列を使います。 - 現地時刻に変換するのは、人に日付を見せるときや人から入力してもらうときなど、最後の段階だけにしましょう。
- 固定のオフセットや略称ではなく、
Asia/TokyoやAmerica/New_YorkのようなIANAのタイムゾーン名を使いましょう。名前にはその地域のサマータイムのルールと変更の歴史が含まれますが、−05:00のようなオフセットは、ニューヨークでは1年のうち一部の期間しか正しくありません。日本にはサマータイムがなく常にUTC+9ですが、海外の時刻を扱うときにはこの違いが効いてきます。 - 「毎週月曜日、ベルリン時間の9時に会議」のように現地時刻で決めた未来の予定は、前もって計算したタイムスタンプではなく、現地時刻とタイムゾーン名をセットで保存しましょう。政府がサマータイムのルールを変えることがあり、今日計算したタイムスタンプが1時間ずれてしまうおそれがあるからです。
都市ごとの時刻を比べるなら、サマータイムを自動で反映するタイムゾーン変換ツールを、2つの日付の間の日数を数えたり日付に日数を足したりするなら日付計算ツールを使ってください。
コードでタイムスタンプを変換する
例はすべて、2026-01-01 00:00:00 UTCを表す1767225600を使います。
JavaScript
| やりたいこと | コード |
|---|---|
| 現在時刻(秒) | Math.floor(Date.now() / 1000) |
| タイムスタンプ → ISO文字列 | new Date(1767225600 * 1000).toISOString() の結果は "2026-01-01T00:00:00.000Z" |
| タイムゾーンを指定して表示 | new Date(1767225600 * 1000).toLocaleString("ja-JP", { timeZone: "Asia/Tokyo" }) |
| 日付の文字列 → タイムスタンプ | Date.parse("2026-01-01T09:00:00+09:00") / 1000 の結果は 1767225600 |
JavaScriptの日付はミリ秒単位なので、渡すときは1,000を掛け、取り出すときは1,000で割りましょう。また、new Date("2026-01-01")(日付のみ)はUTCの0時として解釈されますが、new Date("2026-01-01T00:00")(オフセットなしの日付と時刻)は現地時刻として解釈されます。日本で実行すると、この2つは9時間ずれます。特定の瞬間を表したいときは、必ずZかオフセットを付けましょう。
Python
| やりたいこと | コード |
|---|---|
| 現在時刻(秒) | time.time()(浮動小数点数) |
| タイムスタンプ → UTCのdatetime | datetime.fromtimestamp(1767225600, tz=timezone.utc) |
| タイムスタンプ → 指定したタイムゾーン | datetime.fromtimestamp(1767225600, tz=ZoneInfo("Asia/Tokyo")) |
| datetime → タイムスタンプ | datetime(2026, 1, 1, tzinfo=timezone.utc).timestamp() の結果は 1767225600.0 |
インポートはfrom datetime import datetime, timezoneとfrom zoneinfo import ZoneInfo(Python 3.9以降)です。tzinfoのない「naive」なdatetimeは避けましょう。naiveな値に.timestamp()を呼ぶとコンピューターのローカルタイムゾーンとみなされますし、naiveな値を返すdatetime.utcfromtimestamp()はPython 3.12で非推奨になりました。Windowsには最初からIANAのタイムゾーンデータベースが入っていないため、zoneinfoを使うにはtzdataパッケージ(pip install tzdata)も必要です。
SQL
| データベース | タイムスタンプ → 日付 | 日付 → タイムスタンプ |
|---|---|---|
| PostgreSQL | to_timestamp(1767225600) | EXTRACT(EPOCH FROM TIMESTAMPTZ '2026-01-01 00:00:00+00') |
| MySQL | FROM_UNIXTIME(1767225600) | UNIX_TIMESTAMP('2026-01-01 00:00:00') |
| SQLite | datetime(1767225600, 'unixepoch') | unixepoch('2026-01-01 00:00:00') |
PostgreSQLのto_timestampはtimestamptzを返し、セッションのTimeZone設定に従って表示されます。MySQLのFROM_UNIXTIMEとUNIX_TIMESTAMPはどちらもセッションのタイムゾーンを使うため、同じクエリでもサーバーによって結果が変わることがあります。UTCで扱いたいときは、先にSET time_zone = '+00:00'を実行しましょう。SQLiteはUTCで動作します。unixepoch()にはSQLite 3.38以降が必要で、それより古いバージョンではテキストを返すstrftime('%s', '2026-01-01 00:00:00')が使えます。
シェル
| やりたいこと | Linux(GNU date) | macOS(BSD date) |
|---|---|---|
| 現在時刻(秒) | date +%s | date +%s |
| タイムスタンプ → 日付(UTC) | date -u -d @1767225600 | date -u -r 1767225600 |
| 日付 → タイムスタンプ | date -u -d '2026-01-01 00:00:00' +%s | date -u -j -f '%Y-%m-%d %H:%M:%S' '2026-01-01 00:00:00' +%s |
うるう秒
UTCは、少し不規則な地球の自転に合わせるため、ときどきうるう秒を挿入します。1972年以降に27回追加され、最も新しいのは2016年12月31日23:59:60(UTC)、日本時間では2017年1月1日8時59分60秒でした。Unix時間はうるう秒が存在しないかのように扱います。1日は常に86,400秒なので、うるう秒の間はUnix時計が1秒を繰り返すか止まり、一部の大手事業者は余分な1秒を何時間にもわたって少しずつ吸収(「スミア」)しています。ほとんどのソフトウェアでは表に出てきません。短い時間間隔の計測のように影響が出る場面では、実時間の時計ではなく、JavaScriptのperformance.now()やPythonのtime.monotonic()のような単調増加する時計を使いましょう。なお、2022年の国際度量衡総会で、うるう秒を2035年までに事実上廃止することが決議されました。
2038年問題
古いシステムの多くは、Unix時間を符号付き32ビット整数で保存しています。その最大値は2,147,483,647で、2038年1月19日(火)03:14:07 UTC、日本時間では同日12:14:07にあたります。その1秒後に値は−2,147,483,648へと桁あふれし、そうしたシステムはこれを1901年12月13日20:45:52(UTC)と解釈してしまいます。
現在の64ビットのOSや言語は64ビットのタイムスタンプを使っており、あふれるまでには約2,920億年かかります。リスクが残っているのは、今も32ビットを使っている古い機器や組み込み機器、ファイル形式、データベースの列です。たとえばMySQLのTIMESTAMP型の列は2038-01-19 03:14:07 UTCまでしか扱えませんが、DATETIME型にはこの制限がありません。15年のローン返済計画や有効期間の長い証明書のように、遠い未来の日付を計算するソフトウェアは、2038年が来るずっと前にこの限界にぶつかることがあります。
ISO 8601:人が読みやすい表記
プログラムだけでなく人も読む時刻には、ISO 8601か、それをインターネット向けにより厳密にしたRFC 3339を使いましょう。大きな単位から小さな単位の順に並べます。
2026-01-01T00:00:00Z:UTC。Zはオフセットがゼロという意味です。2026-01-01T09:00:00+09:00:同じ瞬間を日本時間で書いたもの2026-01-01:時刻のない日付
年から秒までの順に並ぶので、形式とオフセットがそろったISO 8601の文字列は、テキストとして並べ替えるだけで時系列順になり、ファイル名やログに便利です。一方、01/02/2026のような書き方は、米国では1月2日、ヨーロッパの多くの国では2月1日を意味するので避けましょう。
関連ツール
- Unixタイムスタンプ変換 – 秒、ミリ秒、マイクロ秒、ナノ秒のタイムスタンプを日付に、日付をタイムスタンプに変換します。
- タイムゾーン変換 – サマータイムを考慮して、都市ごとの日付と時刻を比べます。
- 日付計算 – 2つの日付の間の日数を数えたり、日付に日・週・月を足したりします。