JavaScriptでタイムゾーンの呪いをといて、WindowsでタイムゾーンDB作っていく。

タイムゾーン処理。ややこしすぎて、まとめる気もないので、備忘録的にとても雑な記事を書いておきます。日本語も変だし文章の並び順も変かもしれませんが、推敲や清書とかも面倒なので適当です。適当に読みたい人は読んでください。適当すぎる記事だから読みたくない人はスルーしてください。

とはいえ、コードがバグってたら悲しいので、バグみつかったら教えていただければ助かります。

dateToStringとstringToDateを作っていて、タイムゾーンの概念について実装していこうとすると、極めて困難な道だったということがわかってきました。

やっとできたよ。JS 標準Date型でタイムゾーン対応の dateToString() stringToDate()
https://zenn.dev/standard_soft/articles/272a0b82ca64a1

上記記事で作ってます。

下記記事を、とても参考にさせていただいています。

タイムゾーン呪いの書 (知識編)
https://zenn.dev/dmikurube/articles/curse-of-timezones-common-ja

元記事のQiitaの方では質問にも丁寧に答えていただいてとても助かっております。すんごく感謝。

日本だと夏時間ないから、タイムゾーンオフセット値が変化するというのがわからない概念だったので実に混乱しましたが、なんとかわかってきました。

ということで「タイムゾーン呪い」を参考にしていただいてどのようにJSで呪いを解除していくのか、ってー内容を記述しておきます。

## 概要
- 日本じゃほぼ意識しないけど、他地域だと夏時間などがあってどしどしタイムゾーンのUTC差分が変わる。
- Date.getTimezoneOffset が、分単位なので秒単位のtimezoneOffset値がとれなくてやくにたたない。
- Windowsのタイムゾーンを一覧して全部切り替えて、タイムゾーンDBを自分でこしらえて。ミリ秒単位でタイムゾーン変更時点を求める。
- 注意、未来のタイムゾーンは勝手に適当に変わったり、年々政治的に決められて変わったりするらしく、そういうの無視。現時点の未来予測しかできない。WindowsUpdateか何かで取得値が変わったら、また、タイムゾーンDBを作り直して対応したらいいんじゃねか？
- 注意、タイムゾーンDBでかすぎ...

## 日時関係の処理を作っていて。

タイムゾーンって単純にUTCとローカル日時が、何分ずれているか、ということだけの現代日本とかなら「9時間ずれてますー。」ということで非常に単純なのですが日本以外の国だと夏時間を採用しているところがかなり多いので、その国(都市)のx月x日が、UTCと何分ずれているのか、を知ることが困難だったりします。

それを記録しているのがタイムゾーンデータベースというやつですね。今回はこれを作っていきます。

JavaScriptでは、Date型に getTimezonOffset() というものがあり、指定日時のローカルのタイムゾーンが分単位で求まります。

その get とは逆の働きをする setTimezoneOffset というものはないのでその部分を自作はしてみていて、自分の Parts.js ライブラリでは、dateToString や stringToDate などの主な関数に、timezoneOffset を引数指定できるのである程度はタイムゾーン関係の機能をもっているのですが、

各地域のx月x日のタイムゾーンオフセットの値を知らなければ、結局の所、実用的には使えない、という問題がありました。

## JavaScript標準の機能が足りん。

自作ライブラリ作った上で、タイムゾーンを切り替えてテスト動作させたりしてわかったのですが、JavaScript標準の getTimezoneOffset は分の差分しか返してくれません。
これは現代的な時間帯では世界のどの地域でも問題が発生しないようですが、Windowsだけかもしれませんが、過去のだいぶ昔の時点ではUTCとローカル日時の差分が、分単位ではなく秒単位で変わってくることがありました。

なので、getTImezoneOffset は厳密なところでは役に立たなかったので、それは自作しています。そこはできた。

## コード

コードです。これをnodeで動かすと、最も過去から最も未来まで(MIN_INT から MAX_INTまで)で、タイムゾーンの変化点を求められるコードです。

日付の最大値は2300年にしています。MAX_INT までの限界値にすると日本は大丈夫なのですが、他国での夏時間計算が入ると西暦 275760 年までの、毎年のタイムゾーンの変化点が記録されて、計算がとても遅く、データベース(というかテキストファイル)がとても大きくなってしまうので、2300年くらいで十分かと思って区切っています。求めたければ簡単にもとまるし、30分も計算走らせたら取れるのですが、数十MBのでかいソースコードファイルになってしまうのでやめました。


```js:makeTimezoneDatabase_OneArea.js
var fs = require('fs');

if (![2, 3].includes(process.argv.length)) {
  throw new Error('argv.length is not 2 or 3.');
}

const equal = (a, b) => {
  if (Number.isNaN(a) && Number.isNaN(b)) {
    return true;
  }
  return a === b;
};

const getDateType = (time) => {
  return (new Date(time));
};

const _roundDown = (value, digit = 0) => {
  const plusFlag = 0 <= value;
  const powResult = Math.pow(10, digit);
  if (plusFlag) {
    return Math.floor(value * powResult) / powResult;
  } else {
    return -1 * Math.floor(-1 * value * powResult) / powResult;
  }
};

const objectToString = value => {
  return Object.prototype.toString.call(value);
};

const isDate = (value) => {
  return objectToString(value) === '[object Date]';
};

const isNaNStrict = (value) => value !== value;

const isInvalidDate = (value) => {
  return (isDate(value) && (isNaNStrict(value.getTime())));
};

const _getTimezoneOffset = (
  date,
) => {
  if (isInvalidDate(date)) {
    return {
      minutes: NaN,
      seconds: NaN,
    };
  }

  let diffMsec;
  let fullYear = date.getFullYear();
  let time = Date.UTC(
    fullYear, date.getMonth(), date.getDate(),
    date.getHours(), date.getMinutes(), date.getSeconds(),
    date.getMilliseconds(),
  );
  if (isInvalidDate(new Date(time))) {
    fullYear = date.getUTCFullYear();
    if ((0 <= fullYear) && (fullYear <= 99)) {
      throw new Error(`_getTimezoneOffset date.getUTCFullYear()=${fullYear}`);
    }
    time = new Date(
      fullYear, date.getUTCMonth(), date.getUTCDate(),
      date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(),
      date.getUTCMilliseconds(),
    ).getTime();
    diffMsec = time - date.getTime();
  } else {
    // Date.UTC is auto change 0-99 >> 1900-1999
    // 1900 = not leap year. 0 = leap year
    if ((0 <= fullYear) && (fullYear <= 99)) {
      if ((fullYear === 0) && (date.getMonth() <= 1)) {
        time += (
          (Date.UTC(-1, 11, 31, 23, 59, 59, 999) + 1)
          - (Date.UTC(1900, 0, 1, 0, 0, 0, 0))
        );
      } else {
        time += (
          (Date.UTC(-1, 11, 31, 23, 59, 59, 999) + 1)
          - (Date.UTC(1900, 0, 1, 0, 0, 0, 0))
          + (24 * 60 * 60 * 1000)
        );
      }
    }
    diffMsec = date.getTime() - time;
  }

  const minutes = _roundDown(diffMsec / 60 / 1000);
  const seconds = diffMsec / 1000;

  const dateGetTimezoneOffset = date.getTimezoneOffset();
  if (minutes !== dateGetTimezoneOffset) {
    const message =
      `_getTimezoneOffset ${minutes} ${dateGetTimezoneOffset} ${date.toISOString()}`;
    console.error(message);
    throw new Error(messsage);
  }

  return {
    minutes,
    seconds,
  };
};

const getTimezone = (time) => {
  // return getDateType(time).getTimezoneOffset();
  return _getTimezoneOffset(getDateType(time)).seconds;
};
const getToDateString = (time) => {
  const d = getDateType(time);
  const result = d.toString();
  if (result === 'Invalid Date') {
    return result;
  }
  const { minutes, seconds } = _getTimezoneOffset(d);
  // eslint-disable-next-line max-len
  return `${d.toISOString()} ${d.getTimezoneOffset().toString()} ${minutes} ${seconds / 60} ${seconds}`;
};

const timezoneChangePoint = (startTime, endTime) => {
  let beforeTime = startTime;
  let afterTime = endTime;

  const addTimes = [
    1000 * 60 * 60 * 24 * 30,
    1000 * 60 * 60 * 24,
    1000 * 60 * 60,
    1000 * 60,
    1000,
    1,
  ];

  for (const addTime of addTimes) {
    // console.log(addTime)
    startTime = beforeTime;
    endTime = afterTime;
    for (let i = startTime + addTime; i <= endTime; i += addTime) {
      if (!equal(getTimezone(beforeTime), getTimezone(i))) {
        afterTime = i;
        break;
      }
      beforeTime = i;
    }
    if (addTime === addTimes[0]) {
      if (afterTime === endTime) {
        // console.log(beforeTime, afterTime, endTime)
        return {};
      }
    }
  }

  return {
    beforeTime,
    afterTime,
  };
};

const windowsAreaName = process.argv.length === 3 ? process.argv[2] : '';

const main = () => {
  let startTime = Number.MIN_SAFE_INTEGER;
  // let endTime = Number.MAX_SAFE_INTEGER - 1000 * 60 * 60 * 24 * 365;
  // let endTime = Number.MAX_SAFE_INTEGER;
  const endTime = (new Date(2300, 0, 1)).getTime();

  let result = 'const timezoneDatabase = {\n';

  const toStringAreaName = ((new Date()).toString()).split(' (')[1].replaceAll(')', '');
  result += `  area: '${windowsAreaName}',\n`;
  // result += `  toStringArea: '${toStringAreaName}',\n`;
  result += `  history: [\n`;
  while (true) {
    const {
      beforeTime,
      afterTime,
    } = timezoneChangePoint(startTime, endTime);
    if (beforeTime === undefined) {
      console.log(`end[${windowsAreaName}][${toStringAreaName}]`);
      break;
    }
    if (windowsAreaName === '') {
      console.log('before', beforeTime, getToDateString(beforeTime));
      console.log(' after', afterTime, getToDateString(afterTime));
      // console.log(afterTime / endTime);
    }
    // eslint-disable-next-line max-len
    switch (2) {
    case 1:
      result += `    [${afterTime}, ${getTimezone(afterTime)}],\n`;
      break;
    case 2:
      var offSec = getTimezone(beforeTime);
      result +=
        `    { before: ${beforeTime}, offSec: ${offSec}, offMin: ${offSec / 60} },\n`;
      var offSec = getTimezone(afterTime);
      result +=
        `    {  after: ${afterTime}, offSec: ${offSec}, offMin: ${offSec / 60} },\n`;
      break;
    }
    startTime = afterTime;
  }
  result += `  ],\n`;
  result += `};\n`;
  if (windowsAreaName !== '') {
    fs.writeFileSync(`./output/${windowsAreaName}.js`, result);

  }
};

main();

// Japanでの出力
// before -8640000000000001 Invalid Date
//  after -8640000000000000 -271821-04-20T00:00:00.000Z -558 -558 -558.9833333333333 -33539
// before -2587712400001 1887-12-31T14:59:59.999Z -558 -558 -558.9833333333333 -33539
//  after -2587712400000 1887-12-31T15:00:00.000Z -540 -540 -540 -32400
// before -683802000001 1948-05-01T14:59:59.999Z -540 -540 -540 -32400
//  after -683802000000 1948-05-01T15:00:00.000Z -600 -600 -600 -36000
// before -672310800001 1948-09-11T14:59:59.999Z -600 -600 -600 -36000
//  after -672310800000 1948-09-11T15:00:00.000Z -540 -540 -540 -32400
// before -654771600001 1949-04-02T14:59:59.999Z -540 -540 -540 -32400
//  after -654771600000 1949-04-02T15:00:00.000Z -600 -600 -600 -36000
// before -640861200001 1949-09-10T14:59:59.999Z -600 -600 -600 -36000
//  after -640861200000 1949-09-10T15:00:00.000Z -540 -540 -540 -32400
// before -620298000001 1950-05-06T14:59:59.999Z -540 -540 -540 -32400
//  after -620298000000 1950-05-06T15:00:00.000Z -600 -600 -600 -36000
// before -609411600001 1950-09-09T14:59:59.999Z -600 -600 -600 -36000
//  after -609411600000 1950-09-09T15:00:00.000Z -540 -540 -540 -32400
// before -588848400001 1951-05-05T14:59:59.999Z -540 -540 -540 -32400
//  after -588848400000 1951-05-05T15:00:00.000Z -600 -600 -600 -36000
// before -577962000001 1951-09-08T14:59:59.999Z -600 -600 -600 -36000
//  after -577962000000 1951-09-08T15:00:00.000Z -540 -540 -540 -32400
// before 8640000000000000 +275760-09-13T00:00:00.000Z -540 -540 -540 -32400
//  after 8640000000000001 Invalid Date
```

また、JavaScriot の new Date とか、Date.UTC の仕様は実にひどくて、西暦0年(紀元前-1年のことになるらしい)から、西暦99年を指定することができず、勝手に1900年から1999年に変更されているみたいなので、その場合は0年から99年になるように調整したり、また、西暦0年のうるう年計算のあれやこれやの誤差があるので、1日分計算をずらしたりもしています。すごく変な計算になっているのですが、ここ、もっときれいに書ける方法しらないですかね？どなたか...。ほんとJSの日付系関数はひどいわ...。勝手に変換とかすんなよ。)

beforeとafterが1セットで出力されています。そのbeforeとafterのタイミングで、-540が-600にかわっていたりすることがわかると思います。

UTC の -271821-04-20T00:00:00.000Z から有効な日時がスタートして、その時の timezoneOffset は、分ではなく秒単位ですね。
で、UTC の 1887-12-31T15:00:00.000Z でタイムゾーンが変化して、-540分 = 9時間 になり、
1948年~1951年、には、夏時間があったんですかね。UTCとのずれが、9時間ではなく、10時間になっているようです。

で、その後はタイムゾーンの変更はなく、西暦 275760-09-13T00:00:00.000Z に有効日時が終わるようです。

## 手元のWindows環境のタイムゾーンを変更して各地でのタイムゾーン変化を記録する。

Tzutil - DOS コマンド一覧 - Programming Field
https://www.pg-fl.jp/program/dos/doscmd/tzutil.htm

tzutil /l で、選択エリアすべてが一覧ででますので、その名前をつかってタイムゾーンを切り替えつつ、上記のコマンドを打つbatを作ります。

```bat:makeTimezoneDatabase_AllArea.bat
tzutil /s "Dateline Standard Time"
node makeTimezoneDatabase_OneArea.js "Dateline Standard Time"
tzutil /s "UTC-11"
node makeTimezoneDatabase_OneArea.js "UTC-11"
tzutil /s "Aleutian Standard Time"
node makeTimezoneDatabase_OneArea.js "Aleutian Standard Time"
tzutil /s "Hawaiian Standard Time"
node makeTimezoneDatabase_OneArea.js "Hawaiian Standard Time"
tzutil /s "Marquesas Standard Time"
node makeTimezoneDatabase_OneArea.js "Marquesas Standard Time"
tzutil /s "Alaskan Standard Time"
node makeTimezoneDatabase_OneArea.js "Alaskan Standard Time"
tzutil /s "UTC-09"
node makeTimezoneDatabase_OneArea.js "UTC-09"
tzutil /s "Pacific Standard Time (Mexico)"
node makeTimezoneDatabase_OneArea.js "Pacific Standard Time (Mexico)"
tzutil /s "UTC-08"
node makeTimezoneDatabase_OneArea.js "UTC-08"
tzutil /s "Pacific Standard Time"
node makeTimezoneDatabase_OneArea.js "Pacific Standard Time"
tzutil /s "US Mountain Standard Time"
node makeTimezoneDatabase_OneArea.js "US Mountain Standard Time"
tzutil /s "Mountain Standard Time (Mexico)"
node makeTimezoneDatabase_OneArea.js "Mountain Standard Time (Mexico)"
tzutil /s "Yukon Standard Time"
node makeTimezoneDatabase_OneArea.js "Yukon Standard Time"
tzutil /s "Mountain Standard Time"
node makeTimezoneDatabase_OneArea.js "Mountain Standard Time"
tzutil /s "Easter Island Standard Time"
node makeTimezoneDatabase_OneArea.js "Easter Island Standard Time"
tzutil /s "Central Standard Time (Mexico)"
node makeTimezoneDatabase_OneArea.js "Central Standard Time (Mexico)"
tzutil /s "Canada Central Standard Time"
node makeTimezoneDatabase_OneArea.js "Canada Central Standard Time"
tzutil /s "Central America Standard Time"
node makeTimezoneDatabase_OneArea.js "Central America Standard Time"
tzutil /s "Central Standard Time"
node makeTimezoneDatabase_OneArea.js "Central Standard Time"
tzutil /s "US Eastern Standard Time"
node makeTimezoneDatabase_OneArea.js "US Eastern Standard Time"
tzutil /s "Turks And Caicos Standard Time"
node makeTimezoneDatabase_OneArea.js "Turks And Caicos Standard Time"
tzutil /s "Eastern Standard Time (Mexico)"
node makeTimezoneDatabase_OneArea.js "Eastern Standard Time (Mexico)"
tzutil /s "Haiti Standard Time"
node makeTimezoneDatabase_OneArea.js "Haiti Standard Time"
tzutil /s "Cuba Standard Time"
node makeTimezoneDatabase_OneArea.js "Cuba Standard Time"
tzutil /s "SA Pacific Standard Time"
node makeTimezoneDatabase_OneArea.js "SA Pacific Standard Time"
tzutil /s "Eastern Standard Time"
node makeTimezoneDatabase_OneArea.js "Eastern Standard Time"
tzutil /s "Paraguay Standard Time"
node makeTimezoneDatabase_OneArea.js "Paraguay Standard Time"
tzutil /s "Venezuela Standard Time"
node makeTimezoneDatabase_OneArea.js "Venezuela Standard Time"
tzutil /s "Central Brazilian Standard Time"
node makeTimezoneDatabase_OneArea.js "Central Brazilian Standard Time"
tzutil /s "Pacific SA Standard Time"
node makeTimezoneDatabase_OneArea.js "Pacific SA Standard Time"
tzutil /s "SA Western Standard Time"
node makeTimezoneDatabase_OneArea.js "SA Western Standard Time"
tzutil /s "Atlantic Standard Time"
node makeTimezoneDatabase_OneArea.js "Atlantic Standard Time"
tzutil /s "Newfoundland Standard Time"
node makeTimezoneDatabase_OneArea.js "Newfoundland Standard Time"
tzutil /s "Tocantins Standard Time"
node makeTimezoneDatabase_OneArea.js "Tocantins Standard Time"
tzutil /s "SA Eastern Standard Time"
node makeTimezoneDatabase_OneArea.js "SA Eastern Standard Time"
tzutil /s "Greenland Standard Time"
node makeTimezoneDatabase_OneArea.js "Greenland Standard Time"
tzutil /s "Bahia Standard Time"
node makeTimezoneDatabase_OneArea.js "Bahia Standard Time"
tzutil /s "Saint Pierre Standard Time"
node makeTimezoneDatabase_OneArea.js "Saint Pierre Standard Time"
tzutil /s "Argentina Standard Time"
node makeTimezoneDatabase_OneArea.js "Argentina Standard Time"
tzutil /s "E. South America Standard Time"
node makeTimezoneDatabase_OneArea.js "E. South America Standard Time"
tzutil /s "Magallanes Standard Time"
node makeTimezoneDatabase_OneArea.js "Magallanes Standard Time"
tzutil /s "Montevideo Standard Time"
node makeTimezoneDatabase_OneArea.js "Montevideo Standard Time"
tzutil /s "UTC-02"
node makeTimezoneDatabase_OneArea.js "UTC-02"
tzutil /s "Azores Standard Time"
node makeTimezoneDatabase_OneArea.js "Azores Standard Time"
tzutil /s "Cape Verde Standard Time"
node makeTimezoneDatabase_OneArea.js "Cape Verde Standard Time"
tzutil /s "UTC"
node makeTimezoneDatabase_OneArea.js "UTC"
tzutil /s "Sao Tome Standard Time"
node makeTimezoneDatabase_OneArea.js "Sao Tome Standard Time"
tzutil /s "GMT Standard Time"
node makeTimezoneDatabase_OneArea.js "GMT Standard Time"
tzutil /s "Greenwich Standard Time"
node makeTimezoneDatabase_OneArea.js "Greenwich Standard Time"
tzutil /s "Morocco Standard Time"
node makeTimezoneDatabase_OneArea.js "Morocco Standard Time"
tzutil /s "W. Europe Standard Time"
node makeTimezoneDatabase_OneArea.js "W. Europe Standard Time"
tzutil /s "Central European Standard Time"
node makeTimezoneDatabase_OneArea.js "Central European Standard Time"
tzutil /s "Romance Standard Time"
node makeTimezoneDatabase_OneArea.js "Romance Standard Time"
tzutil /s "Central Europe Standard Time"
node makeTimezoneDatabase_OneArea.js "Central Europe Standard Time"
tzutil /s "W. Central Africa Standard Time"
node makeTimezoneDatabase_OneArea.js "W. Central Africa Standard Time"
tzutil /s "GTB Standard Time"
node makeTimezoneDatabase_OneArea.js "GTB Standard Time"
tzutil /s "Jordan Standard Time"
node makeTimezoneDatabase_OneArea.js "Jordan Standard Time"
tzutil /s "Namibia Standard Time"
node makeTimezoneDatabase_OneArea.js "Namibia Standard Time"
tzutil /s "Israel Standard Time"
node makeTimezoneDatabase_OneArea.js "Israel Standard Time"
tzutil /s "Egypt Standard Time"
node makeTimezoneDatabase_OneArea.js "Egypt Standard Time"
tzutil /s "West Bank Standard Time"
node makeTimezoneDatabase_OneArea.js "West Bank Standard Time"
tzutil /s "Kaliningrad Standard Time"
node makeTimezoneDatabase_OneArea.js "Kaliningrad Standard Time"
tzutil /s "E. Europe Standard Time"
node makeTimezoneDatabase_OneArea.js "E. Europe Standard Time"
tzutil /s "South Sudan Standard Time"
node makeTimezoneDatabase_OneArea.js "South Sudan Standard Time"
tzutil /s "Syria Standard Time"
node makeTimezoneDatabase_OneArea.js "Syria Standard Time"
tzutil /s "Libya Standard Time"
node makeTimezoneDatabase_OneArea.js "Libya Standard Time"
tzutil /s "South Africa Standard Time"
node makeTimezoneDatabase_OneArea.js "South Africa Standard Time"
tzutil /s "Sudan Standard Time"
node makeTimezoneDatabase_OneArea.js "Sudan Standard Time"
tzutil /s "Middle East Standard Time"
node makeTimezoneDatabase_OneArea.js "Middle East Standard Time"
tzutil /s "FLE Standard Time"
node makeTimezoneDatabase_OneArea.js "FLE Standard Time"
tzutil /s "Turkey Standard Time"
node makeTimezoneDatabase_OneArea.js "Turkey Standard Time"
tzutil /s "Volgograd Standard Time"
node makeTimezoneDatabase_OneArea.js "Volgograd Standard Time"
tzutil /s "Arab Standard Time"
node makeTimezoneDatabase_OneArea.js "Arab Standard Time"
tzutil /s "E. Africa Standard Time"
node makeTimezoneDatabase_OneArea.js "E. Africa Standard Time"
tzutil /s "Arabic Standard Time"
node makeTimezoneDatabase_OneArea.js "Arabic Standard Time"
tzutil /s "Belarus Standard Time"
node makeTimezoneDatabase_OneArea.js "Belarus Standard Time"
tzutil /s "Russian Standard Time"
node makeTimezoneDatabase_OneArea.js "Russian Standard Time"
tzutil /s "Iran Standard Time"
node makeTimezoneDatabase_OneArea.js "Iran Standard Time"
tzutil /s "Astrakhan Standard Time"
node makeTimezoneDatabase_OneArea.js "Astrakhan Standard Time"
tzutil /s "Arabian Standard Time"
node makeTimezoneDatabase_OneArea.js "Arabian Standard Time"
tzutil /s "Russia Time Zone 3"
node makeTimezoneDatabase_OneArea.js "Russia Time Zone 3"
tzutil /s "Caucasus Standard Time"
node makeTimezoneDatabase_OneArea.js "Caucasus Standard Time"
tzutil /s "Saratov Standard Time"
node makeTimezoneDatabase_OneArea.js "Saratov Standard Time"
tzutil /s "Georgian Standard Time"
node makeTimezoneDatabase_OneArea.js "Georgian Standard Time"
tzutil /s "Azerbaijan Standard Time"
node makeTimezoneDatabase_OneArea.js "Azerbaijan Standard Time"
tzutil /s "Mauritius Standard Time"
node makeTimezoneDatabase_OneArea.js "Mauritius Standard Time"
tzutil /s "Afghanistan Standard Time"
node makeTimezoneDatabase_OneArea.js "Afghanistan Standard Time"
tzutil /s "West Asia Standard Time"
node makeTimezoneDatabase_OneArea.js "West Asia Standard Time"
tzutil /s "Pakistan Standard Time"
node makeTimezoneDatabase_OneArea.js "Pakistan Standard Time"
tzutil /s "Ekaterinburg Standard Time"
node makeTimezoneDatabase_OneArea.js "Ekaterinburg Standard Time"
tzutil /s "Qyzylorda Standard Time"
node makeTimezoneDatabase_OneArea.js "Qyzylorda Standard Time"
tzutil /s "Sri Lanka Standard Time"
node makeTimezoneDatabase_OneArea.js "Sri Lanka Standard Time"
tzutil /s "India Standard Time"
node makeTimezoneDatabase_OneArea.js "India Standard Time"
tzutil /s "Nepal Standard Time"
node makeTimezoneDatabase_OneArea.js "Nepal Standard Time"
tzutil /s "Central Asia Standard Time"
node makeTimezoneDatabase_OneArea.js "Central Asia Standard Time"
tzutil /s "Omsk Standard Time"
node makeTimezoneDatabase_OneArea.js "Omsk Standard Time"
tzutil /s "Bangladesh Standard Time"
node makeTimezoneDatabase_OneArea.js "Bangladesh Standard Time"
tzutil /s "Myanmar Standard Time"
node makeTimezoneDatabase_OneArea.js "Myanmar Standard Time"
tzutil /s "North Asia Standard Time"
node makeTimezoneDatabase_OneArea.js "North Asia Standard Time"
tzutil /s "Tomsk Standard Time"
node makeTimezoneDatabase_OneArea.js "Tomsk Standard Time"
tzutil /s "N. Central Asia Standard Time"
node makeTimezoneDatabase_OneArea.js "N. Central Asia Standard Time"
tzutil /s "Altai Standard Time"
node makeTimezoneDatabase_OneArea.js "Altai Standard Time"
tzutil /s "SE Asia Standard Time"
node makeTimezoneDatabase_OneArea.js "SE Asia Standard Time"
tzutil /s "W. Mongolia Standard Time"
node makeTimezoneDatabase_OneArea.js "W. Mongolia Standard Time"
tzutil /s "North Asia East Standard Time"
node makeTimezoneDatabase_OneArea.js "North Asia East Standard Time"
tzutil /s "Ulaanbaatar Standard Time"
node makeTimezoneDatabase_OneArea.js "Ulaanbaatar Standard Time"
tzutil /s "Singapore Standard Time"
node makeTimezoneDatabase_OneArea.js "Singapore Standard Time"
tzutil /s "W. Australia Standard Time"
node makeTimezoneDatabase_OneArea.js "W. Australia Standard Time"
tzutil /s "Taipei Standard Time"
node makeTimezoneDatabase_OneArea.js "Taipei Standard Time"
tzutil /s "China Standard Time"
node makeTimezoneDatabase_OneArea.js "China Standard Time"
tzutil /s "Aus Central W. Standard Time"
node makeTimezoneDatabase_OneArea.js "Aus Central W. Standard Time"
tzutil /s "Korea Standard Time"
node makeTimezoneDatabase_OneArea.js "Korea Standard Time"
tzutil /s "Transbaikal Standard Time"
node makeTimezoneDatabase_OneArea.js "Transbaikal Standard Time"
tzutil /s "Yakutsk Standard Time"
node makeTimezoneDatabase_OneArea.js "Yakutsk Standard Time"
tzutil /s "Tokyo Standard Time"
node makeTimezoneDatabase_OneArea.js "Tokyo Standard Time"
tzutil /s "North Korea Standard Time"
node makeTimezoneDatabase_OneArea.js "North Korea Standard Time"
tzutil /s "Cen. Australia Standard Time"
node makeTimezoneDatabase_OneArea.js "Cen. Australia Standard Time"
tzutil /s "AUS Central Standard Time"
node makeTimezoneDatabase_OneArea.js "AUS Central Standard Time"
tzutil /s "Vladivostok Standard Time"
node makeTimezoneDatabase_OneArea.js "Vladivostok Standard Time"
tzutil /s "AUS Eastern Standard Time"
node makeTimezoneDatabase_OneArea.js "AUS Eastern Standard Time"
tzutil /s "West Pacific Standard Time"
node makeTimezoneDatabase_OneArea.js "West Pacific Standard Time"
tzutil /s "E. Australia Standard Time"
node makeTimezoneDatabase_OneArea.js "E. Australia Standard Time"
tzutil /s "Tasmania Standard Time"
node makeTimezoneDatabase_OneArea.js "Tasmania Standard Time"
tzutil /s "Lord Howe Standard Time"
node makeTimezoneDatabase_OneArea.js "Lord Howe Standard Time"
tzutil /s "Sakhalin Standard Time"
node makeTimezoneDatabase_OneArea.js "Sakhalin Standard Time"
tzutil /s "Central Pacific Standard Time"
node makeTimezoneDatabase_OneArea.js "Central Pacific Standard Time"
tzutil /s "Russia Time Zone 10"
node makeTimezoneDatabase_OneArea.js "Russia Time Zone 10"
tzutil /s "Norfolk Standard Time"
node makeTimezoneDatabase_OneArea.js "Norfolk Standard Time"
tzutil /s "Bougainville Standard Time"
node makeTimezoneDatabase_OneArea.js "Bougainville Standard Time"
tzutil /s "Magadan Standard Time"
node makeTimezoneDatabase_OneArea.js "Magadan Standard Time"
tzutil /s "Russia Time Zone 11"
node makeTimezoneDatabase_OneArea.js "Russia Time Zone 11"
tzutil /s "New Zealand Standard Time"
node makeTimezoneDatabase_OneArea.js "New Zealand Standard Time"
tzutil /s "Fiji Standard Time"
node makeTimezoneDatabase_OneArea.js "Fiji Standard Time"
tzutil /s "UTC+12"
node makeTimezoneDatabase_OneArea.js "UTC+12"
tzutil /s "Chatham Islands Standard Time"
node makeTimezoneDatabase_OneArea.js "Chatham Islands Standard Time"
tzutil /s "Samoa Standard Time"
node makeTimezoneDatabase_OneArea.js "Samoa Standard Time"
tzutil /s "Tonga Standard Time"
node makeTimezoneDatabase_OneArea.js "Tonga Standard Time"
tzutil /s "UTC+13"
node makeTimezoneDatabase_OneArea.js "UTC+13"
tzutil /s "Line Islands Standard Time"
node makeTimezoneDatabase_OneArea.js "Line Islands Standard Time"

tzutil /s "Tokyo Standard Time"
```

これで地域を切り替えて、その地域の全てのタイムゾーン変更点を記録してファイルに落として、タイムゾーンデータベースを作ることができます。

これがあれば、地域を指定することで、その地域の特定日時の timezoneOffset 値が取得できるので、実用的に使えるかな、というところです。

これで、dateToStringはタイムゾーン地域別に作れることはわかったのでよかったです。だがしかし、stringToDateはまだ作れない。苦難は続く感じ。

出力後のタイムゾーンデータベースの値を全部合算して1ファイルにまとめるコードは次の通り

```js:makeTimezoneDatabase_Join.js
var fs = require('fs');

const timezoneAreas = [
  'Dateline Standard Time',
  'UTC-11',
  'Aleutian Standard Time',
  'Hawaiian Standard Time',
  'Marquesas Standard Time',
  'Alaskan Standard Time',
  'UTC-09',
  'Pacific Standard Time (Mexico)',
  'UTC-08',
  'Pacific Standard Time',
  'US Mountain Standard Time',
  'Mountain Standard Time (Mexico)',
  'Yukon Standard Time',
  'Mountain Standard Time',
  'Easter Island Standard Time',
  'Central Standard Time (Mexico)',
  'Canada Central Standard Time',
  'Central America Standard Time',
  'Central Standard Time',
  'US Eastern Standard Time',
  'Turks And Caicos Standard Time',
  'Eastern Standard Time (Mexico)',
  'Haiti Standard Time',
  'Cuba Standard Time',
  'SA Pacific Standard Time',
  'Eastern Standard Time',
  'Paraguay Standard Time',
  'Venezuela Standard Time',
  'Central Brazilian Standard Time',
  'Pacific SA Standard Time',
  'SA Western Standard Time',
  'Atlantic Standard Time',
  'Newfoundland Standard Time',
  'Tocantins Standard Time',
  'SA Eastern Standard Time',
  'Greenland Standard Time',
  'Bahia Standard Time',
  'Saint Pierre Standard Time',
  'Argentina Standard Time',
  'E. South America Standard Time',
  'Magallanes Standard Time',
  'Montevideo Standard Time',
  'UTC-02',
  'Azores Standard Time',
  'Cape Verde Standard Time',
  'UTC',
  'Sao Tome Standard Time',
  'GMT Standard Time',
  'Greenwich Standard Time',
  'Morocco Standard Time',
  'W. Europe Standard Time',
  'Central European Standard Time',
  'Romance Standard Time',
  'Central Europe Standard Time',
  'W. Central Africa Standard Time',
  'GTB Standard Time',
  'Jordan Standard Time',
  'Namibia Standard Time',
  'Israel Standard Time',
  'Egypt Standard Time',
  'West Bank Standard Time',
  'Kaliningrad Standard Time',
  'E. Europe Standard Time',
  'South Sudan Standard Time',
  'Syria Standard Time',
  'Libya Standard Time',
  'South Africa Standard Time',
  'Sudan Standard Time',
  'Middle East Standard Time',
  'FLE Standard Time',
  'Turkey Standard Time',
  'Volgograd Standard Time',
  'Arab Standard Time',
  'E. Africa Standard Time',
  'Arabic Standard Time',
  'Belarus Standard Time',
  'Russian Standard Time',
  'Iran Standard Time',
  'Astrakhan Standard Time',
  'Arabian Standard Time',
  'Russia Time Zone 3',
  'Caucasus Standard Time',
  'Saratov Standard Time',
  'Georgian Standard Time',
  'Azerbaijan Standard Time',
  'Mauritius Standard Time',
  'Afghanistan Standard Time',
  'West Asia Standard Time',
  'Pakistan Standard Time',
  'Ekaterinburg Standard Time',
  'Qyzylorda Standard Time',
  'Sri Lanka Standard Time',
  'India Standard Time',
  'Nepal Standard Time',
  'Central Asia Standard Time',
  'Omsk Standard Time',
  'Bangladesh Standard Time',
  'Myanmar Standard Time',
  'North Asia Standard Time',
  'Tomsk Standard Time',
  'N. Central Asia Standard Time',
  'Altai Standard Time',
  'SE Asia Standard Time',
  'W. Mongolia Standard Time',
  'North Asia East Standard Time',
  'Ulaanbaatar Standard Time',
  'Singapore Standard Time',
  'W. Australia Standard Time',
  'Taipei Standard Time',
  'China Standard Time',
  'Aus Central W. Standard Time',
  'Korea Standard Time',
  'Transbaikal Standard Time',
  'Yakutsk Standard Time',
  'Tokyo Standard Time',
  'North Korea Standard Time',
  'Cen. Australia Standard Time',
  'AUS Central Standard Time',
  'Vladivostok Standard Time',
  'AUS Eastern Standard Time',
  'West Pacific Standard Time',
  'E. Australia Standard Time',
  'Tasmania Standard Time',
  'Lord Howe Standard Time',
  'Sakhalin Standard Time',
  'Central Pacific Standard Time',
  'Russia Time Zone 10',
  'Norfolk Standard Time',
  'Bougainville Standard Time',
  'Magadan Standard Time',
  'Russia Time Zone 11',
  'New Zealand Standard Time',
  'Fiji Standard Time',
  'UTC+12',
  'Chatham Islands Standard Time',
  'Samoa Standard Time',
  'Tonga Standard Time',
  'UTC+13',
  'Line Islands Standard Time',
];

const main = () => {
  let result = '';
  for (const timezoneArea of timezoneAreas) {
    if (!fs.existsSync(`./output/${timezoneArea}.js`)) { continue; }
    let text = fs.readFileSync(`./output/${timezoneArea}.js`, 'utf8');
    text = text.replaceAll(`const timezoneDatabase = {`, `{`),
    text = text.replaceAll(`};`, `},`),
    text = text.replaceAll(`\n`, `\n  `);
    result += text;
  }
  result = 'const timezoneDatabase = [\n  ' + result + `];\n`;
  result = result.replace(`\n  ]`, `\n]`);
  fs.writeFileSync(`./output/timezoneDatabase.js`, result);
};

main();

```
以上。終わり。