0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

UNIXタイムスタンプと日時をJavaScriptで相互変換する(秒とミリ秒・タイムゾーンの罠)

0
Posted at

ログの時刻、APIのレスポンス、DBに入っていた数値——1700000000 のような数字を見て「これ、結局いつ?」となること、ありませんか。逆に「今のUNIX時間がサッと欲しい」場面も地味に多いです。

UNIXタイムスタンプ(エポック秒)と日時の変換は、ブラウザ標準の Date だけで完結します。ライブラリも通信も不要です。この記事では、秒⇔日時の相互変換と、ハマりやすい「秒とミリ秒」「タイムゾーン」の罠をまとめます。

ゴール:1700000000 のような数値を、ライブラリなしで日時に(そしてその逆に)正しく変換できるようになること。


そもそもUNIXタイムスタンプとは

1970年1月1日 00:00:00 UTC からの経過秒数のことです(エポック秒とも呼びます)。タイムゾーンに依存しない「世界共通の一点」を1つの数値で表せるので、ログやDB、API間のやり取りでよく使われます。

ここで最初の落とし穴。**UNIXタイムスタンプは「秒」だけど、JavaScriptの Date は「ミリ秒」**で動きます。この単位の食い違いが、変換ミスの9割の原因です(後述)。


現在のUNIXタイムスタンプを取得する

Date.now() は現在時刻をミリ秒で返します。UNIXタイムスタンプ(秒)が欲しいなら、1000で割って切り捨てます。

const nowMs  = Date.now();                 // 例: 1700000000123(ミリ秒)
const nowSec = Math.floor(Date.now() / 1000); // 例: 1700000000(秒)

Math.round ではなく Math.floor(切り捨て) を使うのがポイント。四捨五入にすると、ミリ秒部分が500以上のときに1秒進んでしまい、実際の時刻より未来になってしまいます。


UNIXタイムスタンプ → 日時

new Date() にミリ秒を渡すと Date オブジェクトになります。手元にあるのが秒なら、1000倍してから渡します。

function unixToDate(sec) {
  return new Date(sec * 1000); // 秒 → ミリ秒にしてから渡す
}

const d = unixToDate(1700000000);
d.toLocaleString(); // 端末のローカル時刻で表示(例: 2023/11/15 6:13:20)
d.toISOString();    // UTCのISO8601(例: 2023-11-14T22:13:20.000Z)

ここを new Date(1700000000) と1000倍し忘れると、1970年1月20日あたりの意味不明な日時になります。「やけに昔の日付が出る」ときは、まず秒とミリ秒を疑ってください。


日時 → UNIXタイムスタンプ

逆方向も同じ発想です。Date から getTime()(ミリ秒)を取り、1000で割って切り捨てます。

function dateToUnix(date) {
  return Math.floor(date.getTime() / 1000);
}

dateToUnix(new Date("2023-11-15T00:00:00+09:00")); // 1699974000(秒)

date.getTime() と Date.now() はどちらもミリ秒。粒度を合わせたいとき以外は、最後に /1000 して Math.floor で統一しておくと混乱しません。


落とし穴①:秒かミリ秒かを「桁数」で見分ける

もらった数値が秒なのかミリ秒なのか分からない——これはよくあります。目安として、現在近辺の秒は10桁、ミリ秒は13桁です。

function normalizeToMs(num) {
  // 13桁 ≒ ミリ秒、10桁 ≒ 秒 とみなす素朴な判定
  return String(num).length >= 13 ? num : num * 1000;
}

new Date(normalizeToMs(1700000000));    // 秒とみなして ×1000
new Date(normalizeToMs(1700000000123)); // ミリ秒とみなしてそのまま

厳密な判定ではありませんが、2001年〜2286年くらいの範囲なら桁数で十分に見分けられます。外部から来る値を扱うときの保険として便利です。


落とし穴②:タイムゾーン(ローカルとUTCの取り違え)

Date の表示系メソッドは、どれが「ローカル」でどれが「UTC」かを意識しないと事故ります。

  • toLocaleString() … 端末のタイムゾーンで表示(人に見せる用)
  • toISOString() … 常にUTC。末尾に Z(=UTC)が付く
  • getHours() などはローカル、getUTCHours() などはUTC
const d = new Date(1700000000 * 1000);
d.toISOString();                         // "2023-11-14T22:13:20.000Z"(UTC)
d.toLocaleString("ja-JP", { timeZone: "Asia/Tokyo" }); // 日本時間で表示

「ISOで出したら9時間ずれてる!」の正体は、たいてい toISOString() がUTCだからです。ずれているのではなく、UTCで正しく出ているだけ。日本時間で見せたいなら toLocaleString に timeZone を渡します。


落とし穴③:日付文字列のパースはブラウザ任せにしない

new Date("2023-11-15") のような文字列パースは、形式によって解釈がUTC扱いになったりローカル扱いになったりします("2023-11-15" はUTC午前0時、"2023/11/15" はローカル午前0時、など)。

安全なのは、数値の各要素から組み立てる方法です。

// ローカル時刻として組み立て(月は0始まりに注意)
const local = new Date(2023, 10, 15, 0, 0, 0); // 2023-11-15 00:00 ローカル

// UTCとして組み立てたいとき
const utcMs = Date.UTC(2023, 10, 15, 0, 0, 0); // ミリ秒が返る
const utc   = new Date(utcMs);

Date の月は0始まり(1月=0、11月=10)です。ここも定番のバグ源なので要注意。


落とし穴④:2038年問題は「どこで」起きるか

UNIXタイムスタンプを32ビット符号付き整数で持つシステムは、2038年1月19日にオーバーフローします(いわゆる2038年問題)。

ただし JavaScript 側は心配無用です。Date は64ビット浮動小数点でミリ秒を扱うので、約27万年先まで表現できます。問題が起きるのは、受け渡し先のC/組み込み/古いDBなど32ビットで秒を保持している側です。フロントで変換するだけなら気にしなくて大丈夫ですが、「送った先がどう保持しているか」は頭の片隅に置いておくと安心です。


まとめ

  • UNIXタイムスタンプ=1970年からの経過"秒"。Date は**"ミリ秒"**で動く(ここの単位差が最大の罠)
  • 現在の秒:Math.floor(Date.now() / 1000)(四捨五入はダメ)
  • 秒 → 日時:new Date(sec * 1000)(1000倍を忘れると1970年になる)
  • 日時 → 秒:Math.floor(date.getTime() / 1000)
  • 秒かミリ秒かは**桁数(10 or 13)**で見分けると実務的
  • toISOString() は常にUTC、toLocaleString() はローカル。ずれて見えるのはたいてい正常
  • 文字列パースは形式依存で危険。各要素から組み立て、月は0始まり
  • 2038年問題はJS側では起きない。保持する先の話

数値と日時を行き来する処理は、Date 標準メソッドだけで十分まかなえます。参考になれば嬉しいです。


0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?