日時変換ツールをつくってみた

ツール
ツール
この記事は約17分で読めます。

概要

仕事で、日時の変換を頻繁にする必要があり、毎回ツールを探すのが大変だったので、自分で作ってみることにしてみました。
方針としては、気軽に使えるようにすること。そのため、HTMLにしています。コピーすれば社内とかでも気軽に使えるようになるので。
また、UI系はあまり気にしなくてもいいBootstrapで、日付変換のライブラリは、Luxonにしています。
moment.jsを使おうとしたけど、いつの間にかメンテナンスモードになっていたので、その代替として選んだ。ほかにもあったが、Timezoneの処理がよさそうだったので使ってます。

開発環境

  • Windows11
  • VSCode
  • JavaScript、CSSライブラリ
    • Bootstrap 5
      • UIライブラリ
      • 久々に触って手間取った。あと、ネット検索でバージョン5の情報があまり乗ってなかった。
    • Luxon 3
      • 日時変換のライブラリ

使い方

特徴として、Unix時間とMongoDBのObjectIDの変換がよくあるので、それらを変換できるようにしています。初期表示では、入力ボックスに現在の日時が表示されるようになっています。
変換ボタンを押すことで、入力された日付を変換して、出力にある形式に変換します。

コード

日付変換ツールのすべてのソースコードです。各種ライブラリは、CDNを指定していますので、コピーするだけで使用可能です。

<!doctype html>
<html lang="ja">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.9.1/font/bootstrap-icons.css">
  <title>日時変換ツール</title>
</head>

<body>
  <div class="container">
    <p class="h1 mt-3">日時変換ツール</p>
    <div class="row">
      <div class="col-sm">
        <div class="form-group">
          <label for="input-format">自由フォーマット</label>
          <div class="input-group">
            <input type="text" class="form-control" id="input-format">
            <input type="text" class="form-control" id="input-format-text" value="yyyy/MM/dd HH:mm:ss">
            <select class="form-select" id="input-format-zone">
              <option value="Asia/Tokyo">Asia/Tokyo</option>
              <option value="UTC">UTC</option>
            </select>
            <div class="input-group-append">
              <button class="btn btn-outline-primary" onclick="javascript:convertFormat()">変換</button>
            </div>
          </div>
          <small class="form-text text-muted">指定されたフォーマットの日付</small>
        </div>
      </div>
    </div>
    <div class="row">
      <div class="col-sm">
        <div class="form-group">
          <label for="input-unixtime">Unixtime</label>
          <div class="input-group">
            <input type="text" class="form-control" id="input-unixtime">
            <div class="input-group-append">
              <button class="btn btn-outline-primary" onclick="javascript:convertFormat()">変換</button>
            </div>
          </div>
          <small class="form-text text-muted">10桁(秒)、13桁(ミリ秒)のUnix時間</small>
        </div>
      </div>
    </div>
    <div class="row">
      <div class="col-sm">
        <div class="form-group">
          <label for="input-iso">ISO</label>
          <div class="input-group">
            <input type="text" class="form-control" id="input-iso">
            <div class="input-group-append">
              <button class="btn btn-outline-primary" onclick="javascript:convertISO()">変換</button>
            </div>
          </div>
          <small class="form-text text-muted">国際標準化機構(ISO)の日付。ISO8601準拠</small>
        </div>
      </div>
    </div>
    <div class="row">
      <div class="col-sm">
        <div class="form-group">
          <label for="input-rfc2822">RFC2822</label>
          <div class="input-group">
            <input type="text" class="form-control" id="input-rfc2822">
            <div class="input-group-append">
              <button class="btn btn-outline-primary" onclick="javascript:convertRFC2822()">変換</button>
            </div>
          </div>
          <small class="form-text text-muted">インターネットメールの日時形式。RFC2822互換。</small>
        </div>
      </div>
    </div>
    <div class="row">
      <div class="col-sm">
        <div class="form-group">
          <label for="input-http">HTTP</label>
          <div class="input-group">
            <input type="text" class="form-control" id="input-http">
            <div class="input-group-append">
              <button class="btn btn-outline-primary" onclick="javascript:convertHTTP()">変換</button>
            </div>
          </div>
          <small class="form-text text-muted">HTTPヘッダーのGTMの日付。RFC1123に準拠。</small>
        </div>
      </div>
    </div>
    <div class="row">
      <div class="col-sm">
        <div class="form-group">
          <label for="input-objectid">ObjectId</label>
          <div class="input-group">
            <input type="text" class="form-control" id="input-objectid">
            <div class="input-group-append">
              <button class="btn btn-outline-primary" onclick="javascript:convertObjectId();">変換</button>
            </div>
          </div>
          <small class="form-text text-muted">Mongo DBのObjectId</small>
        </div>
      </div>
    </div>
    <p class="h3 mt-5 mb-4 border-bottom border-secondary"><i class="bi bi-download"></i>出力</p>
    <div class="d-grid gap-2 col-6 mx-auto">
      <button class="btn btn-outline-primary mb-4" onclick="javascript:outputAll()">現在日時</button>
    </div>
    <table class="table table-bordered">
      <thead>
        <tr>
          <th scope="col">形式</th>
          <th scope="col">値</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>日本時間: yyyy/MM/dd hh:mm:ss</td>
          <td id="output-format"></td>
        </tr>
        <tr>
          <td>Unixtime(秒)</td>
          <td id="output-unixtime"></td>
        </tr>
        <tr>
          <td>ISO(JST)</td>
          <td id="output-iso"></td>
        </tr>
        <tr>
          <td>RFC2822</td>
          <td id="output-rfc2822"></td>
        </tr>
        <tr>
          <td>HTTP</td>
          <td id="output-http"></td>
        </tr>
        <tr>
          <td>ObjectId</td>
          <td id="output-objectid"></td>
        </tr>
      </tbody>
    </table>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"
    integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
    crossorigin="anonymous"></script>
  <script src="https://cdn.jsdelivr.net/npm/luxon@3.0.1/build/global/luxon.min.js"
    integrity="sha256-uQ0RrcqAQ8NxzNqZH11eXx3qFLAgwEgSQN1V0N1+UlM=" crossorigin="anonymous"></script>

  <script>
    window.addEventListener('DOMContentLoaded', (event) => {
      outputAll()
      inputAll()

    })
    function outputFormat(dt) {
      setText("output-format", dt.toFormat("yyyy/MM/dd HH:mm:ss", { zone: 'Asia/Tokyo', }))
      setText("output-unixtime", dt.toUnixInteger())
      setText("output-iso", dt.toISO())
      setText("output-rfc2822", dt.toRFC2822())
      setText("output-http", dt.toHTTP())
      setText("output-objectid", toObjectId(dt))
    }
    function inputFormat(dt) {
      setValue("input-format", dt.toFormat("yyyy/MM/dd HH:mm:ss"))
      setValue("input-unixtime", dt.toUnixInteger())
      setValue("input-iso", dt.toISO())
      setValue("input-rfc2822", dt.toRFC2822())
      setValue("input-http", dt.toHTTP())
      setValue("input-objectid", toObjectId(dt))
    }
    function inputAll() {
      var dt = now()
      inputFormat(dt)
    }
    function outputAll() {
      var dt = now()
      outputFormat(dt)
    }

    function toObjectId(dt) {
      return Math.floor(dt.toUnixInteger()).toString(16) + "0000000000000000";
    }
    function fromObjectId(str) {
      return parseInt(str.substring(0, 8), 16)
    }
    function getText(id) {
      return document.getElementById(id).textContent
    }
    function setText(id, text) {
      document.getElementById(id).textContent = text
    }
    function setValue(id, text) {
      document.getElementById(id).value = text
    }
    function getValue(id) {
      return document.getElementById(id).value
    }

    function now() {
      var DateTime = luxon.DateTime;
      var dt = DateTime.now().setZone("Asia/Tokyo")
      return dt
    }

    function convertUnixtime() {
      var DateTime = luxon.DateTime;
      var str = getValue("input-unixtime")
      if (/\d{1,10}/.test(str)) {
        outputFormat(DateTime.fromSeconds(parseInt(str, 10)))
      } else if (/\d{11,13}/.test(str)) {
        outputFormat(DateTime.fromMills(parseInt(str, 13)))
      }
    }
    function convertISO() {
      var DateTime = luxon.DateTime;
      var str = getValue("input-iso")
      outputFormat(DateTime.fromISO(str))
    }
    function convertRFC2822() {
      var DateTime = luxon.DateTime;
      var str = getValue("input-rfc2822")
      outputFormat(DateTime.fromRFC2822(str))
    }
    function convertHTTP() {
      var DateTime = luxon.DateTime;
      var str = getValue("input-http")
      outputFormat(DateTime.fromHTTP(str))
    }
    function convertFormat() {
      var DateTime = luxon.DateTime;
      var str = getValue("input-format")
      var format = getValue("input-format-text")
      var z = getValue("input-format-zone")
      outputFormat(DateTime.fromFormat(str, format, { zone: z }))
    }
    function convertObjectId() {
      var DateTime = luxon.DateTime;
      var str = getValue("input-objectid")
      outputFormat(DateTime.fromSeconds(fromObjectId(str)))     
    }
  </script>
</body>

</html>

まとめ

リファクタリングできるところはありますが、作業に時間がかかりすぎたので、いったん公開してほそぼそ修正していきます。
gitに挙げるのと、コピー機能を付けたり、UI的にも出力部分をもうちょっとできたかなぁと思えるので。
やっぱり、デザイン系は難しいと再認識したのと、時間がかかるので、JavaScriptをモジュールっぽく書かなくてよかった(初心者にはわかりやすいしね)と思う今日この頃。。。

コメント