Luxon を使って kintone の日付や日時フィールドのフォーマットをカスタマイズする

目次

はじめに

Luxon は Moment.js の後継的なライブラリにあたり、日付を処理できます。
この記事では、kintone カスタマイズにおける Luxon の導入方法やよくある日付処理を Luxon で扱う方法を紹介します。

Luxon とは

Luxon は日付データのフォーマット、バリデーション、計算、表示するための JavaScript ライブラリです。
ソースコードは GitHub (External link) にあり、 公式ドキュメント (External link) も公開されています。

Moment.js との違い

メリット 1:Luxon 単体でタイムゾーンを扱える

Moment.js でタイムゾーンに関する操作を扱う場合、 Moment Timezone (External link) というライブラリの追加が必要でした。
参考: Moment Timezone を使って簡単時差管理

Luxon は単体でタイムゾーンに対する操作ができます。

1
2
luxon.DateTime.local().setZone('America/New_York').toISO();
// 2020-05-21T00:09:04.124-04:00
メリット 2:Luxon で生成するオブジェクトはイミュータブルなオブジェクト

イミュータブルなオブジェクトとは、作成した後に状態が変わらないオブジェクトのことです。
Moment.js のオブジェクトはミュータブルなため(イミュータブルではないため)、日付の加算などの操作をすると操作元のオブジェクトも変更されます。

1
2
3
4
5
6
7
8
const date = moment();
const oneDayLater = date.add(1, 'days'); // 元のオブジェクトに対して操作する

console.log('date:' + date.format('YYYY-MM-DD'));
console.log('oneDayLater:' + oneDayLater.format('YYYY-MM-DD'));
// 元のオブジェクト(date)も、1日後の日付に変更されている
// date: 2020-05-22
// oneDayLater: 2020-05-22

Moment.js で元のオブジェクトに影響させたくない場合は、オブジェクトを複製し、複製したオブジェクトに対して操作する必要がありました。

1
2
3
4
5
6
7
8
const date = moment();
const oneDayLater = date.clone().add(1, 'days'); // 複製したオブジェクトに対して操作する

console.log('date:' + date.format('YYYY-MM-DD'));
console.log('oneDayLater:' + oneDayLater.format('YYYY-MM-DD'));
// 元のオブジェクト(date)は、元の日付のまま
// date: 2020-05-21
// oneDayLater: 2020-05-22

Luxon はイミュータブルなオブジェクトのため、日付の加算などを行っても、元のオブジェクトが変更されることはありません。

1
2
3
4
5
6
7
8
const date = luxon.DateTime.local();
const oneDayLater = date.plus({days: 1});

console.log('date:' + date.toFormat('yyyy-MM-dd'));
console.log('oneDayLater:' + oneDayLater.toFormat('yyyy-MM-dd'));
// 元のオブジェクト(date)は、元の日付のまま
// date: 2020-05-21
// oneDayLater: 2020-05-22

注意点:ブラウザーによっては利用できない機能がある

Luxon は主要ブラウザーのメジャーバージョンの 2 世代前までを公式にサポートしています。
詳細は公式サイト> Support matrix の Official support (External link) を参照してください。

kintone カスタマイズでの導入方法

次の URL を kintone の「JavaScript / CSS でカスタマイズ」画面で指定し、その後にカスタマイズファイルを指定します。

  • https://js.cybozu.com/luxon/3.0.4/luxon.min.js

上記 URL は、2022 年 10 月 3 日時点での Cybozu CDN で配信されている最新バージョンの URL です。
Luxon を導入する際は、 Cybozu CDN を確認し、必要に応じて配信されているバージョンを変更してください。

Luxon の使い方

基本編

CDN を読み込むと、グローバルオブジェクトとして luxon が追加されています。
これを使って、Luxon オブジェクトを生成してみましょう。

1
2
3
4
5
6
7
8
// 現在日時から生成
const currentDate = luxon.DateTime.local();

// kintone の日時フィールドの値から生成(フィールドコードが「日時」)
const dateFieldCode = '日時';
const record = kintone.app.record.get().record;
const dateFieldValue = record[dateFieldCode].value;
const dateFieldDate = luxon.DateTime.fromISO(dateFieldValue);

いろいろな書式の文字列を取得してみましょう。
その他の書式については、公式サイト> Formatting (External link) をご参考ください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Luxon オブジェクトを生成
const date = luxon.DateTime.local();

// yyyy-MM-dd 形式
// 例: 2020-06-02
date.toFormat('yyyy-MM-dd');

// 年月のみ(0埋めなし)
// 例: 2020年6月
date.toFormat('yyyy年M月');

// 曜日
// 例: 火曜日
date.setLocale('ja').toFormat('EEEE');

// 時刻フィールドの書式
// 例: 09:00
date.toFormat('HH:mm');

// 日時フィールドの書式
// 例: 2020-06-02T15:00:00.000+09:00
date.toISO();

x 日後や月初などの日付を操作してみましょう。
その他の操作については、公式サイト> Math (External link) をご参考ください。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Luxon オブジェクトを生成
const date = luxon.DateTime.local();

// 3日後
const threeDaysLater = date.plus({days: 3});

// 月初
const startOfMonth = date.startOf('month');

// 月末
const endOfMonth = date.endOf('month');

// その週の月曜日
const mondayOfTheWeek = date.set({weekday: 1});

kintone カスタマイズでの実用例

cybozu developer network で紹介されている Tips で行っている日付計算を Luxon を使って実装してみましょう。

年齢や経過年数を計算する

経過年数を表示する では、生年月日フィールドから年齢の計算や、入社年月日フィールドから入社してからの経過年月を計算しています。
これらの計算を Luxon を使って行ってみましょう。

カスタマイズを適用するアプリには、以下のフィールドがあるものとします。

フィールド名 フィールドの種類 フィールドコード
生年月日 日付 BirthDay
入社年月日 日付 JoiningDay

サンプルコードでは、アプリのレコードの詳細画面を開いたときに、年齢や入社してからの経過年月を開発者ツールのコンソールに表示します。

 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
/*
 * Luxon sample program
 * Copyright (c) 2020 Cybozu
 *
 * Licensed under the MIT License
 * https://opensource.org/license/mit/
 */

(() => {
  'use strict';
  kintone.events.on('app.record.detail.show', (event) => {
    const record = event.record;

    const birthDayFieldCode = 'BirthDay';
    const joiningDayFieldCode = 'JoiningDay';

    /**
     * 経過年月日を計算する
     * @param {string} dateStr 日付文字列
     * @returns {object} 計算結果のオブジェクト
     */
    const calculateDuration = function(dateStr) {
      const currentDate = luxon.DateTime.local().startOf('day');
      const date = luxon.DateTime.fromISO(dateStr).startOf('day');
      // 経過期間を計算する
      const duration = currentDate.diff(date, ['years', 'months', 'days']);
      return duration.toObject();
    };

    // 年齢を計算する
    const birthDayValue = record[birthDayFieldCode].value;
    const birthDayDuration = calculateDuration(birthDayValue);
    console.log(birthDayDuration.years + '歳');

    // 入社からの経過年月を計算する
    const joiningDayValue = record[joiningDayFieldCode].value;
    const joiningDayDuration = calculateDuration(joiningDayValue);
    console.log(joiningDayDuration.years + '年' + joiningDayDuration.months + 'ヶ月');

    return event;
  });
})();
期限を過ぎているかを計算する

ログインユーザーが担当しているレコードに背景色をつける では、期限フィールドの値を使って、「期限が過ぎているか」や「期限の x 日前を過ぎているか」を計算しています。
これらの計算を Luxon を使って行ってみましょう。

カスタマイズを適用するアプリには、以下のフィールドがあるものとします。

フィールド名 フィールドの種類 フィールドコード
期限 日付 LimitDay

サンプルコードでは、アプリのレコード一覧画面を開いたとき、それぞれのレコードが期限を過ぎていた場合や期限の 5 日前を過ぎていた場合に、開発者ツールのコンソールに表示します。

 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
/*
 * Luxon sample program
 * Copyright (c) 2020 Cybozu
 *
 * Licensed under the MIT License
 * https://opensource.org/license/mit/
 */

(() => {
  'use strict';
  kintone.events.on('app.record.index.show', (event) => {
    const DAYS = 5;
    const records = event.records;

    const idFieldCode = '$id';
    const limitDayFieldCode = 'LimitDay';

    /**
     * 期限のx日前か
     * @param {string} dateStr 日付文字列
     * @param {number} days x日
     * @returns {boolean} 期限のx日前なら true
     */
    const isBeforeDeadline = function(dateStr, days) {
      const date = luxon.DateTime.fromISO(dateStr).startOf('day');
      // 今日からx日後の日付オブジェクト
      const addedDate = luxon.DateTime.local().plus({days: days}).startOf('day');
      return date <= addedDate;
    };
    /**
     * 期限を過ぎているか
     * @param {string} dateStr 日付文字列
     * @returns {boolean} 期限が過ぎていれば true
     */
    const isExpired = function(dateStr) {
      const date = luxon.DateTime.fromISO(dateStr).startOf('day');
      const currentDate = luxon.DateTime.local().startOf('day');
      return date < currentDate;
    };

    records.forEach((record) => {
      const limitDayValue = record[limitDayFieldCode].value;
      const idValue = record[idFieldCode].value;
      if (isExpired(limitDayValue)) {
        console.log('レコード番号' + idValue + 'は、期限を過ぎています');
      } else if (isBeforeDeadline(limitDayValue, DAYS)) {
        console.log('レコード番号' + idValue + 'は、期限の' + DAYS + '日前を過ぎています');
      }
    });
    return event;
  });
})();

おわりに

Luxon や Moment.js といった日付処理ライブラリを使うと、JavaScript の Date 型では面倒な日付処理を楽に扱うことができます。
cybozu developer network には、Moment.js を使った kintone の日時フィールドのフォーマットを、Moment.js でカスタマイズする もあります。
kintone カスタマイズで日付処理をしたいときには、ぜひこちらもご参考ください。
また Moment.js から Luxon に移行する際には、公式サイトの For Moment users (External link) をご参考ください。

information

この Tips は、2022 年 10 月版 kintone で動作を確認しています。