具体例でDay.jsと標準Dateクラスを比較する
Day.js
Day.jsはJavascriptの軽量な日付操作ライブラリです。
シンプルなAPI・Immutable性・i18nを備えており、githubでのスター数は47,000を超えています。
今回は日付関連のよくありそうな仕様をDay.jsとJavascriptの標準Dateクラスの両方で実装し、比較します。
簡単な例だと差がわかりにくいので、ある程度現実にありそうな例にします。
今年の残り営業日
与えられた日付に対して、その年の残った平日の日数を返します。簡単のため、祝日などは考えません。
Day.js
const getRemainingBusinessDaysDayjs = (date) => {
const startDate = dayjs(date);
const endDate = dayjs(date).endOf("year");
const totalDays = endDate.diff(startDate, "day") + 1;
return new Array(totalDays)
.fill()
.map((_, index) => startDate.add(index, "day"))
.filter((date) => {
const dayOfWeek = date.day();
return dayOfWeek !== 0 && dayOfWeek !== 6;
}).length;
};
console.log(getRemainingBusinessDaysDayjs("2000-06-01"));
// 152
Date
const getRemainingBusinessDaysDate = (date) => {
const startDate = new Date(date);
const endDate = new Date(startDate.getFullYear(), 11, 31);
const totalDays =
Math.floor((endDate - startDate) / (1000 * 60 * 60 * 24)) + 1;
return Array.from({ length: totalDays })
.map((_, index) => {
return new Date(
startDate.getFullYear(),
startDate.getMonth(),
startDate.getDate() + index
);
})
.filter((date) => {
const dayOfWeek = date.getDay();
return dayOfWeek !== 0 && dayOfWeek !== 6;
}).length;
};
どちらも今年の残っている日付の配列を作成し、それぞれの要素で平日かどうか判定しています。
記述量という点だと両者にさほど大きな差はないのですが、Day.jsのendOf
, diff
, add
などの地味なユーティリティが便利です。
全体的に、Dateクラスは日付の減算・加算がつらい印象があります。
ただ、これだけならまだDateクラスを使ってもさほど問題はないのかなと思います。
国際フライトの出発/到着時刻
Day.js
タイムゾーンの異なる国から国へと飛行機でフライトするようなケースで、発着時刻をUTCと現地時間で取得します。
// dayjs
const dayjs = require("dayjs");
const timezone = require("dayjs/plugin/timezone");
const utc = require("dayjs/plugin/utc");
dayjs.extend(timezone);
dayjs.extend(utc);
const getFlightInformationDayjs = (
fromTz,
toTz,
departureLocalTime,
flightTimeHours
) => {
const departure = dayjs.tz(departureLocalTime, fromTz);
const arrival = departure.add(flightTimeHours, "hours").tz(toTz);
return {
departureLocalTime: departure.format("YYYY-MM-DD HH:mm"),
departureUtcTime: departure.utc().format("YYYY-MM-DD HH:mm"),
arrivalLocalTime: arrival.format("YYYY-MM-DD HH:mm"),
arrivalUtcTime: arrival.utc().format("YYYY-MM-DD HH:mm"),
};
};
console.log(
getFlightInformationDayjs(
"Asia/Tokyo",
"America/New_York",
"2000-01-01 00:00",
12
)
);
// {
// departureLocalTime: '2000-01-01 00:00',
// departureUtcTime: '1999-12-31 15:00',
// arrivalLocalTime: '1999-12-31 22:00',
// arrivalUtcTime: '2000-01-01 03:00'
// }
dayjs.tz()
でタイムゾーンを持った日付のオブジェクトを生成、そこからutc()
を呼び出してUTCへの変換しています。
タイムゾーンに関連するパッケージはプラグイン化されているので別途インポートする必要がありますが、実装はかなりシンプルでそのほか特筆すべき点はありません。
Date
標準Dateクラスでの実装例を紹介しますが、それなりに長いです。
Dateクラスでの実装
const getFlightInformationDate = (
fromTz,
toTz,
departureLocalTime,
flightTimeHours
) => {
const getTimezoneOffset = (date, timeZone) => {
// 12:00:00 PM GMT+9という形式のstringが出力される
const tz = date.toLocaleString("en-US", {
timeZone,
timeZoneName: "shortOffset",
});
// 数値部分を取得する
return parseInt(tz.split("GMT")[1], 10);
};
const format = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const hours = String(date.getHours()).padStart(2, "0");
const minutes = String(date.getMinutes()).padStart(2, "0");
return `${year}-${month}-${day} ${hours}:${minutes}`;
};
const departure = new Date(departureLocalTime);
const departureUtc = new Date(
departure.getTime() - getTimezoneOffset(departure, fromTz) * 60 * 60 * 1000
);
const arrivalUtc = new Date(
departureUtc.getTime() + flightTimeHours * 60 * 60 * 1000
);
const arrival = new Date(
arrivalUtc.getTime() + getTimezoneOffset(arrivalUtc, toTz) * 60 * 60 * 1000
);
return {
departureLocalTime: format(departure),
departureUtcTime: format(departureUtc),
arrivalLocalTime: format(arrival),
arrivalUtcTime: format(arrivalUtc),
};
};
Dateクラスはタイムゾーンを保持しておらず、toLocaleString
でタイムゾーンを指定することではじめてタイムゾーンに絡めた実装が可能になります。
ここでは、タイムゾーンを指定して出力されたstringからUTCとのオフセットをどうにか取得してUTCへの変換をしています。
若干ハックな印象は受けますが、Dateクラスのみを使う場合妥当なアプローチだと思います。
また日付のフォーマットについても記述量が多いのはそうですが、悪名高いgetMonth() + 1
[1]などが気になってくるところです。
複雑なタイムゾーンを扱う場合だとDay.jsはかなり検討の余地がありそうです。
おわりに
他にちょうどいい例が思いつかなかったのでおわりです。
Dateクラスはどこでも使えてかつ慣れているので引力が強いですが、多少複雑なことをしようと思うと可読性の低さやイミュータブルではないことが問題になることがあり、そういった場面では積極的にDay.jsを採用していきたいです。
ありがとうございました。
X (Twitter): @koyo_k0
-
getMonthは0を起点とした月を返すため、1月の場合0になる ↩︎
Discussion