JavaScript UNIXタイムスタンプと日時の相互変換
2020. 8. 4 (火)
以前Qiitaに投稿したもののJavaScript版です。
もちろんDateオブジェクトを利用すれば面倒な計算をすることなく相互に変換できますが、あえて自前で計算したい場合はどんな方法があるか、という例です。
UTCとの時差取得と引数省略時のデフォルト値(現在日時)取得にのみDateオブジェクトを使用していますが、それ以外のUNIX時間と日時の変換はDateオブジェクトのパーサーに頼らず自前で算出しています。
date2time()
渡した日付からUNIXタイムスタンプを返します。
引数: 年,月,日,時,分,秒,時差反映
引数を省略した場合は現在日時を使います。
時差反映は0が無し、1が有りでデフォルトは1です。
time2date()
渡したUNIXタイムスタンプから日付をオブジェクトで返します。
引数: UNIXタイムスタンプ,時差反映
引数を省略した場合は現在のUNIXタイムスタンプを使います。
時差反映は0が無し、1が有りでデフォルトは1です。
時差反映0の場合はUTCを基準とした日時での処理、1の場合は実行環境で設定された時差が反映されます。
スクリプト
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
/** * 日時 -> UNIXタイムスタンプ変換 */ function date2time(...p) { // 引数取得 未指定時は現在の日時 const d = new Date(), // 年月日時分秒 year = p[0] ?? d.getFullYear(), month = p[1] ?? d.getMonth() + 1, day = p[2] ?? d.getDate(), hour = p[3] ?? d.getHours(), minute = p[4] ?? d.getMinutes(), second = p[5] ?? d.getSeconds(), // 時差反映 0:なし 1:あり useOffset = p[6] ?? 1; // 前年 const prevYear = year - 1; // 該当年各月の日数 const monthLastDays = getMonthLastDays(year); // UNIXタイムスタンプを返す return hour * 3600 + minute * 60 + second + ( 365 * prevYear + Math.floor(prevYear / 4) - Math.floor(prevYear / 100) + Math.floor(prevYear / 400) + (month > 1 ? monthLastDays.slice(0, month - 1).reduce((a, b) => a + b) : 0) + day - 719163 ) * 86400 + (useOffset ? (new Date().getTimezoneOffset()) * 60 : 0); } /** * UNIXタイムスタンプ -> 日時変換 */ function time2date(...p) { // 引数取得 未指定時は現在のタイムスタンプ const timestamp = p[0] ?? Math.floor(Date.now() / 1000), // 時差反映 0:なし 1:あり useOffset = p[1] ?? 1; // タイムゾーン時差 const tzOffset = useOffset ? -(new Date().getTimezoneOffset()) * 60 : 0; // 0001-01-01からの通算日数 let absoluteDays = Math.floor((timestamp + tzOffset) / 86400) + 719162; // 1日内の秒数 const seconds = ((timestamp + tzOffset) % 86400 + 86400) % 86400; // 年算出用 閏年の区切り const r = { 400: 146097, // 400年 = 36524 * 4 + 1日 100: 36524, // 100年 = 1461 * 25 - 1日 4: 1461, // 4年 = 365 * 4 + 1日 1: 365, }; // 通算日数マイナス補正 let absDaysMinusMagnification = 0; if(absoluteDays < 0) { absDaysMinusMagnification = Math.floor((absoluteDays -1) / r[400]); absoluteDays = ((absoluteDays % r[400]) + r[400]) % r[400]; } // オブジェクトyに年を振り分け // 最後に余るabsoluteDaysは年内通算日数 const y = {}; for(const i of [400, 100, 4, 1]) { y[i] = Math.floor(absoluteDays / r[i]) * i; absoluteDays %= r[i]; } // 閏年末日が翌年1月1日に繰り上がるケースの補正 if(y[1] === 4 || y[100] === 400) { y[1]--; absoluteDays = 365; } // 年及び年内通算日数の取得と1スタートへの変更 let year = Object.values(y).reduce((a, b) => a + b) + 1; absoluteDays += 1; // 該当年の各月日数 const monthLastDays = getMonthLastDays(year); // 月取得 let month = 1; while(monthLastDays.slice(0, month).reduce((a, b) => a + b) < absoluteDays && month < 13) month++; // 日取得 const day = month > 1 ? absoluteDays - monthLastDays.slice(0, month - 1).reduce((a, b) => a + b) : absoluteDays; // 通算日数マイナス補正があれば年に反映 if(absDaysMinusMagnification) { year += 400 * absDaysMinusMagnification; } // 時分秒取得 const hour = Math.floor((seconds % 86400) / 3600) % 24, minute = Math.floor((seconds % 3600) / 60), second = seconds % 60; // 日時をオブジェクトで返す return { date: String(year ).padStart(4, '0') + '-' + String(month ).padStart(2, '0') + '-' + String(day ).padStart(2, '0') + ' ' + String(hour ).padStart(2, '0') + ':' + String(minute).padStart(2, '0') + ':' + String(second).padStart(2, '0') + ' ' + (useOffset ? (tzOffset < 0 ? '-' : '+') + String(Math.floor(Math.abs(tzOffset / 3600))).padStart(2, '0') + ':' + String(Math.floor(Math.abs(tzOffset / 60) % 60)).padStart(2, '0') : 'UTC'), year: year, month: month, day: day, hour: hour, minute: minute, second: second, offset: tzOffset, } } function getMonthLastDays(year) { const y_ = ((year % 400) + 400) % 400; // 年マイナス補正 return [ 31, (!(y_ % 4) && (y_ % 100) || !(y_ % 400)) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; } |
使用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// example // 日時からタイムスタンプ取得 let timestamp = date2time(2000, 8, 1, 12, 34, 56, 0); // 年,月,日,時,分,秒,時差反映(0/1) console.log(timestamp); // 965133296 // タイムスタンプから日時取得 let date = time2date(timestamp, 0); // UNIXタイムスタンプ,時差反映(0/1) console.log(date); // date: "2000-08-01 12:34:56 UTC" // day: 1 // hour: 12 // minute: 34 // month: 8 // offset: 0 // second: 56 // year: 2000 console.log(date2time()); // 1596487711 console.log(time2date()); // date: "2020-08-04 05:48:31 +09:00" // day: 4 // hour: 5 // minute: 48 // month: 8 // offset: 32400 // second: 31 // year: 2020 |
タイムスタンプから年を算出する場合、目標の日数に達するまで年でループさせる力技なども考えられますが、このスクリプトでは4年、100年、400年といったグレゴリオ暦における閏年の節目の配分を調べることで年を導き出していますので、数年先、数万年先の結果を計算させたとしても負荷は変わりません。