同様の処理を行うライブラリとしてはdayjsやMoment.jsがメジャーかと思いますが、個人的にはあれほど多機能なものは要らなかったので作成してみました。
toLocaleDateString()
やtoLocaleTimeString()
等でも色々な表現はできますが、個人的にはやっぱり使い慣れたフォーマット文字列での指定が楽です。
基本的にPHPのdate()
、mktime()
とほぼ同様に扱えるかと思いますが、以下の既知の相違があります。
####mktime()####
第7引数 is_dst はありません。
####date()####
フォーマット文字はPHPで使えるものすべてに対応しているわけではなく、現状 e タイムゾーン識別子、T タイムゾーンの略称、I サマータイム が未対応です。
###参考###
PHP mktime マニュアル
PHP date マニュアル
[PHP DateTime::format]
(https://www.php.net/manual/ja/datetime.format.php)
###スクリプト###
mktime_date.js
/**
* 日付をUNIXタイムスタンプに変換
*/
function mktime(...args) {
const
d = new Date(),
// 引数受け取り
hour = args[0] ?? d.getHours(),
minute = args[1] ?? d.getMinutes(),
second = args[2] ?? d.getSeconds(),
month = args[3] ?? d.getMonth() + 1,
day = args[4] ?? d.getDate(),
year = args[5] ?? d.getFullYear(),
// 年指定補正
_year = year < 0 || year > 100 ? year :
year < 70 ? 2000 + year : 1900 + year;
try {
// 引数に数値でないものが含まれる
if( isNaN(hour) ||
isNaN(minute) ||
isNaN(second) ||
isNaN(month) ||
isNaN(day) ||
isNaN(year)
) throw new Error('A non-numeric value was specified for the argument');
} catch(e) {
console.error(e.message);
}
// 日時設定
d.setFullYear(_year, month -1, day);
d.setHours(hour, minute, second);
// 戻り値
return Math.floor(d / 1000);
}
/**
* 日付/時刻を書式化
*/
function date(format, timeStamp) {
let milliSeconds = microSeconds = 0;
// timeStamp未指定の場合は現在のタイムスタンプを設定
if(timeStamp === undefined) {
const now =
performance.timeOrigin ?
performance.timeOrigin + performance.now() : Date.now();
timeStamp = Math.floor(now / 1000);
milliSeconds = Math.floor(now) % 1000;
microSeconds = Math.floor(now * 1000) % 1e6;
}
try {
// format未指定
if(format === undefined)
throw new Error('Argument `format` not specified');
// timeStampが数値ではない
if(isNaN(timeStamp))
throw new Error('A non-numeric value was specified for the argument');
} catch(e) {
console.error(e.message);
}
const
tzDiff = -Math.floor(new Date(timeStamp * 1000).getTimezoneOffset() * 60),
absTzDiff = Math.abs(tzDiff),
d = new Date(timeStamp * 1000),
df = new Date(timeStamp * 1000),
year = d.getFullYear(),
month = d.getMonth() + 1,
day = d.getDate(),
hour = d.getHours(),
minute = d.getMinutes(),
second = d.getSeconds(),
w = d.getDay(),
spellingMonth =
d.toLocaleDateString('en', {month: 'long'}),
spellingWeek =
d.toLocaleDateString('en', {weekday: 'long'}),
utcLocalAdj =
new Date(+d - d.getTimezoneOffset() * 6e4),
sDate = new Date(Math.floor((+utcLocalAdj + 2592e5) / 6048e5) * 6048e5),
sYear = sDate.getUTCFullYear(),
mTimeStamp =
(timeStamp < 0 ? 86400 - Math.abs(timeStamp) % 86400 : timeStamp) * 1000
+ milliSeconds;
// 1月1日からの日数
df.setFullYear(year, 0, 1);
const days = (d - df) / 864e5;
// 当月の日数
df.setFullYear(year, month, 0);
const daysInMonth = df.getDate();
// 閏年か
df.setFullYear(year, 1, 29);
const leap = df.getDate() === 29 ? 1 : 0;
// 戻り値
// 不要なreplaceメソッドチェーンがあれば
// 適宜コメントアウトや削除をしてください
return String(format)
// 文字誤置換防止 下処理
.replace(/(\w)/g, ":$1:")
// フォーマット文字エスケープ対応
.replace(/\\:(\w):/g, "#$1#")
// RFC2822フォーマットされた日付
.replace(/:r:/g, ':D:, :d: :M: :Y: :H:::i:::s: :O:')
// ISO8601 日付
.replace(/:c:/g, ':Y:-:m:-:d:#T#:H:::i:::s::P:')
// 年 4桁
.replace(/:Y:/g, (year < 0 ? '-' : '') +
String(Math.abs(year)).padStart(4, '0'))
// 月 2桁
.replace(/:m:/g, String(month).padStart(2, '0'))
// 日 2桁
.replace(/:d:/g, String(day).padStart(2, '0'))
// 時 24時間単位 2桁
.replace(/:H:/g, String(hour).padStart(2, '0'))
// 分 2桁
.replace(/:i:/g, String(minute).padStart(2, '0'))
// 秒 2桁
.replace(/:s:/g, String(second).padStart(2, '0'))
// 年 2桁
.replace(/:y:/g, ('0' + year).slice(-2))
// 月 桁揃えなし
.replace(/:n:/g, month)
// 日 桁揃えなし
.replace(/:j:/g, day)
// 時 24時間単位 桁揃えなし
.replace(/:G:/g, hour)
// 時 12時間単位 2桁
.replace(/:h:/g, String((hour + 11) % 12 + 1).padStart(2, '0'))
// 時 12時間単位 桁揃えなし
.replace(/:g:/g, (hour + 11) % 12 + 1)
// 閏年か 0:閏年ではない 1:閏年
.replace(/:L:/g, leap)
// 日に対する英語形式序数サフィックス
.replace(/:S:/g, ['th', 'st', 'nd', 'rd']
[Math.floor(day / 10) % 10 !== 1 && day % 10 < 4 ? day % 10 : 0])
// 曜日 0:日~6:土
.replace(/:w:/g, w)
// ISO8601 曜日 1:月~7:日
.replace(/:N:/g, (w + 6) % 7 + 1)
// 年間通算日 0~365
.replace(/:z:/g, days)
// 月の日数
.replace(/:t:/g, daysInMonth)
// ISO8601 週番号による年
.replace(/:o:/g, sYear)
// ISO8601 月曜日に始まる年単位の週番号
.replace(/:W:/g,
String(1 + Math.floor((sDate - new Date(sYear + '-01-01T00:00Z')) / 6048e5)).padStart(2, '0'))
// UNIXタイムスタンプ
.replace(/:U:/g, timeStamp)
// ミリ秒
.replace(/:v:/g, String(milliSeconds).padStart(3, '0'))
// マイクロ秒
.replace(/:u:/g, String(microSeconds).padStart(6, '0'))
// GMTとの時差 ±HH:MM
.replace(/:P:/g,
(tzDiff < 0 ? '-' : '+') +
String(Math.floor(absTzDiff / 3600) % 24).padStart(2, '0') + ':' +
String(Math.floor(absTzDiff / 60) % 60).padStart(2, '0'))
// GMTとの時差 ±HHMM
.replace(/:O:/g,
(tzDiff < 0 ? '-' : '+') +
String(Math.floor(absTzDiff / 3600) % 24).padStart(2, '0') +
String(Math.floor(absTzDiff / 60) % 60).padStart(2, '0'))
// タイムゾーンオフセット秒数
.replace(/:Z:/g, tzDiff)
// Swatchインターネットタイム
.replace(/:B:/g,
String(Math.floor(((mTimeStamp + 36e5) % 864e5) / 86400)).padStart(3, '0'))
// 曜日 3文字
.replace(/:D:/g, spellingWeek.slice(0, 3))
// 月 3文字
.replace(/:M:/g, spellingMonth.slice(0, 3))
// 曜日 フルスペル
.replace(/:l:/g, spellingWeek)
// 月 フルスペル
.replace(/:F:/g, spellingMonth)
// am/pm
.replace(/:a:/g, hour < 12 ? 'am' : 'pm')
// AM/PM
.replace(/:A:/g, hour < 12 ? 'AM' : 'PM')
// 誤置換防止処理 後始末
.replace(/:(\w):/g , "$1")
.replace(/#(\w)#/g , "$1")
}
/**
* mktimeへ年月日時分秒の順に引数を渡すラッパー
*/
function dateToTime(...args) {
return mktime(
args[3], args[4], args[5],
args[1], args[2], args[0]);
}
####実行サンプル####
// example
// Date.now()でタイムスタンプ取得
time = Math.floor(Date.now() / 1000);
console.log( time ); // 1596325709
// mktime()へ引数なしでのタイムスタンプ取得
time = mktime();
console.log( time ); // 1596325709
formatStr = 'Y-m-d H:i:s y w z t D M l F a A G h g L jS';
console.log( date(formatStr, time) ); // 2020-08-02 08:48:29 20 0 214 31 Sun Aug Sunday August am AM 8 08 8 1 2nd
console.log( date(formatStr) ); // 2020-08-02 08:48:29 20 0 214 31 Sun Aug Sunday August am AM 8 08 8 1 2nd
console.log( date('r', time) ); // Sun, 02 Aug 2020 08:48:29 +0900
console.log( date('o-\\WW-N', time) ); // 2020-W31-7
for(let t=dateToTime(2020,12,29); t<=dateToTime(2021,1,5); t+=86400)
console.log( date('Y-m-d o-\\WW-N', t) );
// 2020-12-29 2020-W53-2
// 2020-12-30 2020-W53-3
// 2020-12-31 2020-W53-4
// 2021-01-01 2020-W53-5
// 2021-01-02 2020-W53-6
// 2021-01-03 2020-W53-7
// 2021-01-04 2021-W01-1
// 2021-01-05 2021-W01-2
console.log( date('c', mktime(0,0,0, 8,1,1800)) ); // 1800-08-01T00:00:00+09:18
console.log( date('c', mktime(0,0,0, 8,1,1950)) ); // 1950-08-01T00:00:00+10:00
console.log( date('c', mktime(0,0,0, 8,1,2020)) ); // 2020-08-01T00:00:00+09:00
console.log( date('Y-m-d\\TH:i:s.vP Z B s') ); // 2020-08-02T08:48:29.853+09:00 32400 033 29
console.log( date('Y-m-d H:i:s D', time) ); // 2020-08-02 08:48:29 Sun
time = mktime(0, 0, 0, 1, 1, 2000);
console.log( date('Y-m-d H:i:s D', time) ); // 2000-01-01 00:00:00 Sat
time = mktime(4, 5, 6, 12, 3, 2000);
console.log( date('Y-m-d H:i:s D', time) ); // 2000-12-03 04:05:06 Sun
time = mktime(0, 0, 0, 2, 29, 2020);
console.log( date('Y-m-d H:i:s D', time) ); // 2020-02-29 00:00:00 Sat
time = mktime(0, 0, 0, 2, 29, 2019);
console.log( date('Y-m-d H:i:s D', time) ); // 2019-03-01 00:00:00 Fri
time = mktime(0, 0, 0, 1, 0, 2000);
console.log( date('Y-m-d H:i:s D', time) ); // 1999-12-31 00:00:00 Fri
time = mktime(0, 0, -1, 1, 1, 2000);
console.log( date('Y-m-d H:i:s D', time) ); // 1999-12-31 23:59:59 Fri
time = mktime(24, 0, 0, 1, 1, 2000);
console.log( date('Y-m-d H:i:s D', time) ); // 2000-01-02 00:00:00 Sun
console.log( date('Y-m-d H:i:s D', 0) ); // 1970-01-01 09:00:00 Thu
console.log(date('Y-m-d H:i:s', mktime(123, 456, 789, -123, -4567)) ); // 1997-03-05 10:49:09
// dateToTime()はmktime()へ年,月,日,時,分,秒の順で引数を渡すラッパーです
console.log(date('Y-m-d H:i:s', dateToTime(2020, 1, 2, 12, 34, 56))); // 2020-01-02 12:34:56