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;
  });
})();
期限を過ぎているかを計算する

ログインユーザーが担当しているレコードに背景色をつけるでは、期限フィールドの値を使って、「期限が過ぎているか」や「期限のn日前を過ぎているか」を計算しています。
これらの計算を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で動作を確認しています。