ServiceNowにおいて日付・日時を扱う方法を説明します。ServiceNowに限らずシステムで日付・日時を扱う際の難所であるグローバル対応もカバーします。
ServiceNowにおける日付・時刻の基本
具体的なクラスの説明に入る前に、ServiceNowにおける日付・時刻の基本的な事項を説明します。
サーバ側では協定世界時(UTC)で保存され、ユーザに表示される際に適切なタイムゾーン、フォーマットに変換される
日付・時刻処理のイメージを示します。サーバ側では日付・時刻は協定世界時(UTC)で保存されており、ユーザに表示されるタイミングでユーザに応じた適切なタイムゾーン、フォーマットに変換されます/されるように開発者が実装する必要があります。
ユーザのタイムゾーン・フォーマット
日付・時刻は、システム内部(サーバ側)だけで処理される場合もありますが、多くの場合最終的にユーザ(クライアント側)に返されます。その際には、現在のユーザに応じたタイムゾーンやフォーマットに変換する必要があります。このときのユーザのタイムゾーンは、次の3つのタイムゾーン・フォーマットのうち最も優先度の高いものが採用されます。
種別 | 優先度 | 説明 |
---|---|---|
システムタイムゾーン/フォーマット | 3位 | システム全体の設定。下記プロパティで指定される。 ・タイムゾーン: glide.sys.default.tz ・日付フォーマット: glide.sys.date_format ・時刻フォーマット: glide.sys.time_format |
ユーザ設定タイムゾーン/フォーマット | 2位 | 現在のユーザのユーザ設定上のタイムゾーン。ユーザテーブルの下記フィールドで指定される。 ・日付フォーマット: Date format ・時刻フォーマット: Time format ユーザが自身で画面右上ユーザのアイコン > Profileから設定することが可能。(Time formatフィールドは標準では表示されない) |
セッションタイムゾーン/フォーマット | 1位 | 現在アクセスしているセッションにおけるタイムゾーン。 ユーザが自身で画面右上ユーザのアイコン > Preferences > Displayより設定することが可能。 |
Internal format(yyyy-MM-dd HH:mm:ss)
日時を表現するフォーマットとして、Internal formatと呼ばれるフォーマットが存在します。これは“yyyy-MM-dd HH:mm:ss”で固定です。Internal formatは、スクリプト中で特定の日時を決め打ちで指定したい場合などで使います。
日付・日時を扱うフィールドタイプ、クラス
ServiceNowで日付・日時を扱う際には、次のフィールドタイプが用いられます。スクリプトで処理する場合は、それぞれのタイプに対応したクラスを用います。
概念 | 対応する フィールドのデータ型 | 例 | 対応するクラス |
---|---|---|---|
日付 | Date | 12月10日 | GlideDate |
日時 | DateTime | 12月10日午前9時0分0秒 | GlideDateTime |
時間・時刻 | Time | 1時間10分, 9時0分0秒 | GlideTime |
期間 | Duration | 1日と3時間, 1年6カ月 | GlideDuration |
クライアント側では日付・日時を扱わない方がよい
日付に関するポイントとして、クライアント側のスクリプトでは日付・日時を扱わない方がよいです。クライアント側から日付・日時を扱いたい場合は処理をサーバ側においてGlideAjaxで呼び出します。理由は次の通りです。
- 日付・日時を扱うための豊富な機能を持つクラスがサーバ側だけに用意されている
- システムタイムゾーンなどの各種プロパティをクライアント側からは取得できない(GlideAjaxを使って取得することもできるが、それをやるなら日付・日時処理ごとサーバ側においてそれをGlideAjaxで呼び出したほうが早い
javascriptの標準モジュールを使ってクライアント側で処理できなくもないですし、ちょっとした処理ならそうしている例もCommunityで見かけますが、私の経験上推奨しません。
日時を扱うGlideDateTime
ここからは日付・日時を扱うクラスの使い方を説明していきます。サンプルコードはメニュー > System Definition > Scrips – Backgroundに張り付けてそのまま実行できます。なお、サンプルコードはコピペ性を高めるため変数の重複定義を頻繁に行っていますのでご注意ください。
日時(GlideDateTimeオブジェクト)の生成
現在時刻から生成
現在時刻からGlideDateTimeオブジェクトを生成するときは引数無しでコンストラクタを呼びます。
// 現在時刻からGlideDateTimeオブジェクトを生成
var gdt = new GlideDateTime();
gs.info('Now(UTC): ' + gdt.getValue());
特定の日時(UTC)を指定して生成
特定の日時(UTC)からGlideDateTimeオブジェクトを生成するときは、引数を指定してコンストラクタを呼ぶか、setValue()、setValueUTC()を利用します。サーバ側で完結する日時の処理はUTCのまま扱うと扱いやすいので、これらを使うことが多いです。
// UTCかつInternal format(yyyy-MM-dd HH:mm:dd)で指定 コンストラクタver [よく使う]
var gdt = new GlideDateTime('2022-12-01 00:00:00');
gs.info('gdt(UTC): ' + gdt.getValue());
// UTCかつInternal format(yyyy-MM-dd HH:mm:dd)で指定 後から指定ver [よく使う]
var gdt = new GlideDateTime();
gdt.setValue('2022-12-01 00:00:00');
gs.info('gdt(UTC): ' + gdt.getValue());
// UTCかつ任意のフォーマットで指定 [あまり使わない]
var gdt = new GlideDateTime();
gdt.setValueUTC("01-12-2022 00:00:00", "dd-MM-yyyy HH:mm:ss");
gs.info('gdt(UTC): ' + gdt.getValue());
// UNIX時間(ミリ秒)で指定 [あまり使わない]
var gdt = new GlideDateTime();
gdt.setValue(1669852800 * 1000); // 2022-12-01 00:00:00 (UTC)
gs.info('gdt(UTC): ' + gdt.getValue());
最後の2つは、他システムから連携された日時を用いてGlideDateTimeオブジェクトを生成するときに利用する程度でしょう。とはいえ、基本的に他システムから日時を連携してもらうときは、Internal format(yyyy-MM-dd HH:mm:ss)に合わせてもらう方が扱いやすいので、そのように調整すべきです。
特定の日時(ユーザのタイムゾーン・フォーマット)を指定して生成
クライアント側でユーザが指定した日時を取得して、それをそのままサーバ側で処理するような場合に利用します。
// 現在のユーザのタイムゾーン、フォーマットで指定 [よく使う]
var gdt = new GlideDateTime();
gdt.setDisplayValue('2022-12-01 09:00:00');
gs.info('gdt(UTC): ' + gdt.getValue());
// 現在のユーザのタイムゾーン、Internal format(yyyy-MM-dd HH:mm:ss)で指定 [たまに使う]
gdt.setDisplayValueInternal('2022-12-01 09:00:00');
gs.info('gdt(UTC): ' + gdt.getValue());
// 現在のユーザのタイムゾーン、任意のフォーマットで指定 [あまり使わない]
var gdt = new GlideDateTime();
gdt.setDisplayValue("01-12-2022 09:00:00", "dd-MM-yyyy HH:mm:ss");
gs.info('gdt(UTC): ' + gdt.getValue());
setDisplayValue()はユーザが入力した値をクライアント側で取得(g_form.getValue(<DateTime型フィールド>))し、それをそのままサーバ側で受け取ってGlideDateTimeオブジェクトを生成するときに使います。
setDisplayValueInternal()はユーザのタイムゾーンで日付を決め打ち指定したいとき、例えばユーザのタイムゾーンでむこう3ヶ月の1日と15日の日付を生成したい場合に利用します(この値はそのままクライアント側に返されある項目の選択肢として設定されるしょう)。この用途でsetDisplayValue()を使うと、ユーザがフォーマットを変更していた場合にバグ可能性があるので注意です。
日時の複製(ディープコピー)
日時をコピーしたい場合は次のようにします。なお、どの方法もディープコピー(コピー先を変更してもコピー元には影響を与えない)です。
// コピー元のGlideDateTimeオブジェクト
var gdt_from = new GlideDateTime("2022-12-01 00:00:00");
gs.info('gdt_from(UTC): ' + gdt_from.getValue());
// GlideDateTimeオブジェクトを複製 コンストラクタver [よく使う]
var gdt_to = new GlideDateTime(gdt_from);
gs.info('gdt_to(UTC): ' + gdt_to.getValue());
// GlideDateTimeオブジェクトを複製 後から指定ver1 [あまり使わない]
var gdt_to = new GlideDateTime();
gdt_to.setValue(gdt_from);
gs.info('gdt_to(UTC): ' + gdt_to.getValue());
// GlideDateTimeオブジェクトを複製 後から指定ver2 [あまり使わない]
var gdt_to = new GlideDateTime();
gdt_to.setGlideDateTime(gdt_from);
gs.info('gdt_to(UTC): ' + gdt_to.getValue());
日時の操作
GlideDateTimeには日時を操作するための豊富な関数が用意されています。日時を操作するほとんどの関数には、UTC版とユーザのタイムゾーン版が存在します。
年、月、日付を変更(月末の指定)
GlideDateTimeオブジェクトの年、月、日付を特定の値に変更することができます。setDayOfMonthUTC()、setDayOfMonthLocalTime()は指定した日付が月末を超えた場合は月末の日付になるので、月末を指定したい場合はsetDayOfMonthUTC(31)などとします。
// ◆ 年を指定
var gdt = new GlideDateTime("1999-12-01 00:00:00");
// UTCで年を指定 [たまに使う]
gdt.setYearUTC(2020);
gs.info('gdt(UTC): ' + gdt.getValue());
// ユーザのタイムゾーンで年を指定 [あまり使わない]
gdt.setYearLocalTime(2022);
gs.info('gdt(UTC): ' + gdt.getValue());
// ◆ 月を指定
var gdt = new GlideDateTime("1999-12-01 00:00:00");
// UTCで月を指定 [たまに使う]
gdt.setMonthUTC(1);
gs.info('gdt(UTC): ' + gdt.getValue());
// ユーザのタイムゾーンで月を指定 [あまり使わない]
gdt.setMonthLocalTime(3);
gs.info('gdt(UTC): ' + gdt.getValue());
// ◆ 日付 を指定
var gdt = new GlideDateTime("1999-12-01 00:00:00");
// UTCで日付を指定 [よく使う]
gdt.setDayOfMonthUTC(1);
gs.info('gdt(UTC): ' + gdt.getValue());
// ユーザのタイムゾーンで日付を指定 [よく使う]
gdt.setDayOfMonthLocalTime(3);
gs.info('gdt(UTC): ' + gdt.getValue());
// 指定した日付が月末を超えた場合は、月末の日付になる [よく使う]
gdt.setDayOfMonthUTC(31); // 31日が存在しない月の場合は月末
gs.info('gdt(UTC): ' + gdt.getValue());
年、月、日付を変更するときの注意 UTC系 vs LocalTime系
日時の操作関数にはUTC系とユーザタイムゾーン系があり、特にクライアント側に返すための日時を生成する処理では、正しく使い分けないとバグります。これらの違いは格納されている時刻をUTC時刻として解釈するか、ユーザのローカルタイムゾーンの時刻として解釈するかの違いです。大抵の場合同じ結果になりますが、UTCとユーザのタイムゾーンで年、月を跨ぐ場合に結果が異なります。具体例を示します。
// GlideDateTimeオブジェクトを生成
// 2021-01-01 07:00:00 (JST) = 2020-12-31 22:00:00 (UTC)
var gdt = new GlideDateTime("2020-12-31 22:00:00");
gs.info("origin " + gdt.getDisplayValue() + " (JST)");
gs.info("origin " + gdt.getValue() + " (UTC)");
// UTC関数を使って年を2000年に指定
gdt.setYearUTC(2000);
// JSTとUTCで結果を出力
gs.info("setYearUTC(2000) > " + gdt.getDisplayValue() + " (JST)");
gs.info("setYearUTC(2000) > " + gdt.getValue() + " (UTC)");
gs.info("\n");
// LocalTime関数を使って年を2000年に指定
gdt.setYearLocalTime(2000);
// JSTとUTCで結果を出力
gs.info("setYearLocalTime(2000) > " + gdt.getDisplayValue() + " (JST)");
gs.info("setYearLocalTime(2000) > " + gdt.getValue() + " (UTC)");
結果
----------
*** Script: origin 2021-01-01 07:00:00 (JST)
*** Script: origin 2020-12-31 22:00:00 (UTC)
*** Script:
*** Script: setYearUTC(2000) > 2001-01-01 07:00:00 (JST)
*** Script: setYearUTC(2000) > 2000-12-31 22:00:00 (UTC)
*** Script:
*** Script: setYearLocalTime(2000) > 2000-01-01 07:00:00 (JST)
*** Script: setYearLocalTime(2000) > 1999-12-31 22:00:00 (UTC)
UTC系の関数を使った場合と、LocalTime系の関数を使った場合で結果が1年ずれてしまいました。理解の助けになると思われるイメージを示します。
サーバ側で完結する処理であっても、ユーザのタイムゾーンに基づいて年や月を指定する場合はLocalTime系関数を使わなければならないことに注意です。
年、月、週、日の加算、減算
全ての関数名がadd~となっていますが、引き算をしたい場合は引数に負の値を指定します。set~系関数と違い、UTC版とローカルタイムゾーン版で内部的な動作に差はありませんが、今どちらのタイムゾーンとして扱っているかに応じて明示的に使い分けると、タイムゾーンによるバグが減ります。
// ◆ 年を加算、減算
var gdt = new GlideDateTime("2000-12-01 00:00:00");
// UTCで年を加算 [よく使う]
gdt.addYearsUTC(1);
gs.info('gdt(UTC): ' + gdt.getValue());
// ユーザのタイムゾーンで年を加算 [あまり使わない]
gdt.addYearsLocalTime(1);
gs.info('gdt(UTC): ' + gdt.getValue());
// ◆ 月を加算、減算
var gdt = new GlideDateTime("2000-12-01 00:00:00");
// UTCで月を減算 [よく使う]
gdt.addMonthsUTC(-1);
gs.info('gdt(UTC): ' + gdt.getValue());
// ユーザのタイムゾーンで月を減算 [あまり使わない]
gdt.addMonthsLocalTime(-1);
gs.info('gdt(UTC): ' + gdt.getValue());
// ◆ 週を加算、減算
var gdt = new GlideDateTime("2000-12-01 00:00:00");
// UTCで週を加算 [よく使う]
gdt.addWeeksUTC(1);
gs.info('gdt(UTC): ' + gdt.getValue());
// ユーザのタイムゾーンで週を加算 [あまり使わない]
gdt.addWeeksLocalTime(1);
gs.info('gdt(UTC): ' + gdt.getValue());
// ◆ 日を加算、減算
var gdt = new GlideDateTime("2000-12-01 00:00:00");
// UTCで日を減算 [よく使う]
gdt.addDaysUTC(-1);
gs.info('gdt(UTC): ' + gdt.getValue());
// ユーザのタイムゾーンで週を減算 [あまり使わない]
gdt.addDaysLocalTime(-1);
gs.info('gdt(UTC): ' + gdt.getValue());
時間の加算、減算
時間単位での加算、減算も可能です。時間の加算・減算の関数は、加算のためのadd()と減算のためのsubtract()が存在します。
// ◆ GlideTimeで加算・減算
var gdt = new GlideDateTime("2000-12-01 00:00:00");
// GlideTimeで加算 [たまに使う]
var offset = new GlideTime();
offset.setValue("01:01:01"); // 1時間1分1秒
gdt.add(offset);
gs.info('gdt(UTC): ' + gdt.getValue());
// GlideTimeで減算 [たまに使う]
gdt.subtract(offset);
gs.info('gdt(UTC): ' + gdt.getValue());
// ◆ ミリ秒で加算・減算
var gdt = new GlideDateTime("2000-12-01 00:00:00");
// ミリ秒で加算 [あまり使わない]
gdt.add(24 * 60 * 60 * 1000); // 1日
gs.info('gdt(UTC): ' + gdt.getValue());
// ミリ秒で減算 [あまり使わない]
gdt.subtract(24 * 60 * 60 * 1000); // 1日
gs.info('gdt(UTC): ' + gdt.getValue());
ミリ秒で指定する場合は、引数に負の値を指定することで動作を反転させることができます。
日時の比較・判定
日時の比較
日時の比較を行う関数を紹介します。
var subject = new GlideDateTime("2022-12-01 00:00:00");
var target = new GlideDateTime("2022-12-01 00:00:01");
// ◆ 前後判定 [よく使う]
// subject > target(subjectの方がtargetよりも後)かどうかを判定
var subject_is_after_target = subject.after(target);
gs.info('subject_is_after_target: ' + subject_is_after_target);
// subject >= target(subjectの方がtargetよりも後か同じ)かどうかを判定
var subject_is_on_or_after_target = subject.onOrAfter(target);
gs.info('subject_is_on_or_after_target: ' + subject_is_on_or_after_target);
// subject < target(subjectの方がtargetよりも前)かどうかを判定
subject.before(target); // true
var subject_is_before_target = subject.before(target);
gs.info('subject_is_before_target: ' + subject_is_before_target);
// subject <= target(subjectの方がtargetよりも前か同じ)かどうかを判定
var subject_is_on_or_before_target = subject.onOrAfter(target);
gs.info('subject_is_on_or_before_target: ' + subject_is_on_or_after_target);
// ◆ 等価判定 [あまり使わない]
var is_equal = subject.equals(target);
gs.info('is_equal:' + is_equal);
// ◆ 前か後か同じかまとめて判定 前=-1, 後=1, 同じ=0 [よく使う]
var result = subject.compareTo(target);
gs.info('result:' + result);
日時の判定(エラー判定)
システム連携などで受け取った文字列からGlideDateTimeオブジェクトを生成する場合は、isValue()で正しく変換できたかを判定をしておくとよいです。
var gdt = new GlideDateTime("2022-12-aa 00:00:00");
gs.info(gdt.isValid());
gs.info(gdt.getErrorMsg());
日時の取得(文字列化)
日時を取得
日時を生成・指定する関数に対応して、各種タイムゾーン・フォーマットで日時を取得する関数があります。
var gdt = new GlideDateTime();
// UTCかつInternal format(yyyy-MM-dd HH:mm:dd)で取得 [よく使う]
var utc = gdt.getValue();
gs.info('utc: ' + utc);
// ユーザタイムゾーンかつユーザのフォーマットで取得 [よく使う]
var local = gdt.getDisplayValue();
gs.info('local: ' + local);
// ユーザのタイムゾーンかつInternal format(yyyy-MM-dd HH:mm:dd)で取得 [あまり使わない]
var local = gdt.getDisplayValueInternal();
gs.info('local: ' + local);
setのときはユーザのフォーマットに依存せず日付を決め打ちで設定するためsetDisplayValue()よりもsetDisplayValueInternal()を使うべき場面が多いですが、getのときは基本的にユーザのフォーマットに合わせてに値を返すはずなのでgetDisplayValue()を使うべき場面が多いでしょう。
年、月、その月の日数、曜日、日付を取得
var gdt = new GlideDateTime("2022-12-01 00:00:00");
// ◆ 年を取得
// UTCで年を取得
var year = gdt.getYearUTC();
gs.info('year: ' + year);
// ローカルタイムゾーンで年を取得
var year = gdt.getYearLocalTime();
gs.info('year: ' + year);
// ◆ 月を取得
// UTCで月を取得
var month = gdt.getMonthUTC();
gs.info('month: ' + month);
// ローカルタイムゾーンで月を取得
var month = gdt.getMonthLocalTime();
gs.info('month: ' + month);
// ◆ その月の日数
// UTCで日数を取得
var dayinmonth = gdt.getDaysInMonthUTC();
gs.info('dayinmonth : ' + dayinmonth );
// ローカルタイムゾーンで日数を取得
var dayinmonth = gdt.getDaysInMonthLocalTime()
gs.info('dayinmonth : ' + dayinmonth );
// ◆ 曜日を取得(月=1, 日=7)
// UTCで週を取得
var dayofweek = gdt.getDayOfWeekUTC();
gs.info('dayofweek: ' + dayofweek);
// ローカルタイムゾーンで週を取得
var dayofweek = gdt.getDayOfWeekLocalTime();
gs.info('dayofweek: ' + dayofweek);
// ◆ 日付を取得
// UTCで週を取得
var dayofmonth = gdt.getDayOfMonthUTC();
gs.info('dayofmonth : ' + dayofmonth );
// ローカルタイムゾーンで週を取得
var dayofmonth = gdt.getDayOfMonthLocalTime();
gs.info('dayofmonth : ' + dayofmonth );
日付部分を取得
日付部分だけを取得したい場合は、GlideDateオブジェクトとして取得できます。多くの場合、それをそのまま文字列に変換します。
var gdt = new GlideDateTime('2022-12-01 23:00:00');
// UTCで日付を取得
var gd_utc = gdt.getDate(); // この時点ではGlideDateオブジェクト
var date_utc = gd_utc.getValue(); // 文字列化
gs.info('date_utc: ' + date_utc);
// ユーザのローカルタイムゾーンで日付を取得
var gd_local = gdt.getLocalDate(); // この時点ではGlideDateオブジェクト
var date_local = gd_local.getValue(); // 文字列化
gs.info('date_local: ' + date_local);
なお、ユーザのタイムゾーンで日付を取得したいとき、getDate()を使ってUTCでGlideDateオブジェクトを取得した後、GlideDateオブジェクトが側でgetDisplayValue()を用いてユーザのタイムゾーンに変換する方法も考えられますが、getDisplayValue()はなぜかUTCのまま返す仕様になっているため、GlidaDateを取得する時点でローカルタイムゾーンに変換するようにします。
var gdt = new GlideDateTime('2022-12-01 23:00:00');
// [こっちを使う] ユーザのローカルタイムゾーンでGlieDateオブジェクトを取得 > そのまま文字列化
var gd_local = gdt.getLocalDate();
var date_local = gd_local .getValue(); // 文字列化
gs.info('date_local: ' + date_local);
// [こっちはダメ] UTCでGlieDateオブジェクトを取得 > ユーザのローカルタイムゾーンで文字列化
var gd_utc = gdt.getDate();
var date_local = gd_utc .getDisplayValue(); // 文字列化
gs.info('date_local: ' + date_local);
時刻部分を取得
時刻部分だけを取得したい場合は、GlideTimeオブジェクトとして取得するか、一気に文字列で取得することもできます。
var gdt = new GlideDateTime();
// UTCで時刻を取得
var gt_utc = gdt.getTime(); // この時点ではGlideTimeオブジェクト
var time_utc = gt_utc.getValue(); // 文字列化
gs.info('time_utc: ' + time_utc);
// ユーザのローカルタイムゾーンで時刻を取得
var gt_local = gdt.getLocalTime(); // この時点ではGlideTimeオブジェクト
var time_local = gt_local.getValue(); // 文字列化
gs.info('time_local: ' + time_local);
// ユーザのローカルタイムゾーンかつユーザのフォーマットで時刻文字列を取得
var time_local = gdt.getUserFormattedLocalTime();
gs.info('time_local: ' + time_local);
// ユーザのローカルタイムゾーンかつInternal format(yyyy-MM-dd HH:mm:dd)で時刻文字列を取得
var time_local = gdt.getInternalFormattedLocalTime()
gs.info('time_local: ' + time_local);
なお、ユーザのローカルタイムゾーンで時刻を取得するとき、getTime()を使ってUTCでGlideTimeオブジェクトを取得した後、GlideTimeオブジェクトが側でgetDisplayValue()などでユーザのローカルタイムゾーンに変換することもできます(上述の日付の時と違ってGlideTimeのgetDisplayValue()はちゃんと動く)。
var gdt = new GlideDateTime();
// ユーザのローカルタイムゾーンでGlideTimeオブジェクトを取得 > そのまま文字列化
var gt_local = gdt.getLocalTime(); // この時点ではGlideTimeオブジェクト
var time_local = gt_local .getValue(); // 文字列化
gs.info('time_local: ' + time_local);
// UTCでGlieTimeオブジェクトを取得 > ユーザのローカルタイムゾーンで文字列化
var gt_utc = gdt.getTime();
var time_local = gt_utc .getDisplayValue(); // 文字列化
gs.info('time_local: ' + time_local);
GlideDateTimeやGlideTimeなどのオブジェクトは常にUTCで扱い、文字列化するときにローカルに変換するという考え方であれば、後者の方が見通しがよくなるかもしれません。
日時の差分を取得
2つの日付の差分を計算することができます。戻り値はGlideDurationオブジェクトです。
var gdt1 = new GlideDateTime("2022-01-01 00:00:00");
var gdt2 = new GlideDateTime("2023-02-02 01:01:01");
var dur = GlideDateTime.subtract(gdt1, gdt2); //the difference between gdt1 and gdt2
gs.info(dur.getDisplayValue());
日付のみを扱いたい場合もGlideDateは使わずGlideDateTimeを使う
標準のAPIとして日付を扱うGlideDateクラスが用意されていますが、こちらは2022年12月現在、getDisplayValue()およびsetDisplayValue()が想定外の動きをするため使わない方がよいです。
その代わりに、時刻部分が”00:00:00″のGlideDateTimeオブジェクトとして処理を行い、最後に日付に変換するとよいです。
日付の生成
日付をGlideDateTimeで扱う場合は、時刻に”00:00:00″を補って生成します。
// 現在日付から生成
var gd = new GlideDate();
var gdt = new GlideDateTime(gd.getValue() + " 00:00:00");
gs.info('gdt(UTC): ' + gdt.getValue());
// UTCの日付文字列から生成
var date_utc = '2022-12-01';
var gdt = new GlideDateTime(date_utc + " 00:00:00");
gs.info('gdt(UTC): ' + gdt.getValue());
// ユーザのローカルタイムゾーンの日付文字列から生成
var gdt = new GlideDateTime()
var date_local = '2022-12-01';
gdt.setDisplayValue(date_local + " 00:00:00");
gs.info('gdt(Local): ' + gdt.getDisplayValue());
日付の操作・比較・判定
操作・比較・判定はGlideDateTimeと同様に行えばOKです。
日付の取得
上述のGlideDateTimeの日付を取得する処理と同様です。
GlideDateのgetDisplayValue(), setDisplayValue()はUTCで処理する
GlideDateを使わない方がよい理由である、getDisplayValue(), setDisplayValue()の謎の挙動についてみておきます。API Referenceでは、これらの関数はユーザのタイムゾーン、フォーマットで日付を取得、または設定できるとありますが、実際に動かしてみるとUTCで動きます。
下記は検証用のコードです。
var gdt = new GlideDateTime();
gs.info("◆ タイムゾーンと現在時刻の確認");
gs.info("システムタイムゾーン\t" + gs.getProperty('glide.sys.default.tz'));
gs.info("現在セッションタイムゾーン\t" + gs.getSession().getTimeZoneName());
gs.info("現在日時(UTC)\t\t" + gdt.getValue());
gs.info("現在日時(PST=UTC-8)\t\t" + gdt.getDisplayValue());
gs.info("");
var gd = new GlideDate();
gs.info("◆ 現在日付をnew GlideDate()で取得");
gs.info("gd.getValue():\t\t\t" + gd.getValue());
gs.info("gd.getDisplayValue():\t\t" + gd.getDisplayValue());
gs.info("gd.getDisplayValueInternal():\t" + gd.getDisplayValueInternal());
gs.info("");
gs.info("◆ setValue()で2022-12-10を指定");
gd.setValue('2022-12-10');
gs.info("gd.getValue():\t\t\t" + gd.getValue());
gs.info("gd.getDisplayValue():\t\t" + gd.getDisplayValue());
gs.info("gd.getDisplayValueInternal():\t" + gd.getDisplayValueInternal());
gs.info("");
gs.info("◆ setDisplayValue()で2022-12-10を指定");
gd.setDisplayValue('2022-12-10');
gs.info("gd.getValue():\t\t\t" + gd.getValue());
gs.info("gd.getDisplayValue():\t\t" + gd.getDisplayValue());
gs.info("gd.getDisplayValueInternal():\t" + gd.getDisplayValueInternal());
メニュー > System Definition > Scripts – Backgroundで実行してみると結果は次のようになります。タイムゾーンをUS/Pacificにしているのは、たまたま検証していた時間にUTCと日付が異なっていたからで、特に意味はありません。
*** Script: ◆ タイムゾーンと現在時刻の確認
*** Script: システムタイムゾーン US/Pacific
*** Script: 現在セッションタイムゾーン US/Pacific
*** Script: 現在日時(UTC) 2022-12-11 05:12:00
*** Script: 現在日時(PST=UTC-8) 2022-12-10 21:12:00
*** Script:
*** Script: ◆ 現在日付をnew GlideDate()で取得
*** Script: gd.getValue(): 2022-12-11
*** Script: gd.getDisplayValue(): 2022-12-11
*** Script: gd.getDisplayValueInternal(): 2022-12-10
*** Script:
*** Script: ◆ setValue()で2022-12-10を指定
*** Script: gd.getValue(): 2022-12-10
*** Script: gd.getDisplayValue(): 2022-12-10
*** Script: gd.getDisplayValueInternal(): 2022-12-09
*** Script:
*** Script: ◆ setDisplayValue()で2022-12-10を指定
*** Script: gd.getValue(): 2022-12-10
*** Script: gd.getDisplayValue(): 2022-12-10
*** Script: gd.getDisplayValueInternal(): 2022-12-09
getDisplayValue()について、ローカルタイムゾーンの現在時刻は”2022-12-10 21:12:00″なので、ローカルタイムゾーンの現在日付は”2022-12-10″となるはずですが、結果を見るとUTCと同じ”2022-12-11″となっています。なお、getDisplayValueInternal()の方は正しくローカルタイムゾーンになっています。
同様に、setDisplayValue()もユーザのローカルタイムゾーンの”2022-12-10″を指定した場合、UTCでは”2022-12-11″のはずですが、getValue()の結果を見ると”2022-12-10″となっています。この原因としては、setDisplayValue()かgetValue()いずれかが正しく動いていない可能性が考えられますが、getValue()は現在時刻の表示で正しく動いていると思われるため、setDisplayValue()がUTCで日付を格納してしまっていると考えるのが自然です。
時間・時刻を扱うGlideTime
時刻を扱うときに使いますが、ほとんどの場合GlideDateTimeと一緒に扱い、時間の計算をするために一時的に使われることが多いです。扱いもGlideDateTimeによく似ているため、簡単に紹介します。
時間・時刻(GlideTimeオブジェクト)の生成
扱いはほとんどGlideDateTimeと同じです
// GlideDateTimeオブジェクトの時刻部分から生成
var gdt = new GlideDateTime('2022-12-01 00:00:00');
var gt = gdt.getTime();
gs.info('gt(UTC): ' + gt.getValue());
// 現在時刻から時間・時刻を生成
var gt = new GlideTime();
gs.info('gt(UTC): ' + gt.getValue());
// ミリ秒で時間を生成
var gt = new GlideTime(60 * 60 * 1000); // 1時間
gs.info('gt(UTC): ' + gt.getValue());
// UTCで特定時刻を設定
var gt = gdt.getTime();
gt.setValue("01:01:01");
gs.info('gt(UTC): ' + gt.getValue());
// ユーザのローカルタイムゾーン、フォーマットで特定時刻を設定
var gt = gdt.getTime();
gt.setDisplayValue("01:01:01");
gs.info('gt(UTC): ' + gt.getValue());
時刻の操作・比較・判定
操作・比較・判定をする場合は基本的にGlideDateTimeで行います。下記の時刻の差を計算する関数はあるものの、ほとんどの場合GlideDateTimeのsubtract()を使います。戻り値はGlideDurationオブジェクトです。
// 時刻の差を計算
var gt1 = new GlideTime();
gt1.setValue('10:00:00');
var gt2 = new GlideTime();
gt2.setValue('11:01:01');
var dur = GlideDate.subtract(gt1, gt2);
gs.info(dur.getDisplayValue());
時刻の取得
GlideTimeで最もよく使うのはこれらの関数だと思います。
var gt = new GlideTime();
gt.setValue('10:00:00');
// UTCで時刻を取得
var utc = gt.getValue();
gs.info('utc: ' + utc);
// ユーザのローカルタイムゾーン、フォーマットで時刻を取得
var local = gt.getDisplayValue();
gs.info('local: ' + local);
// ユーザのローカルタイムゾーン、Internal format(yyyy-MM-dd HH:mm:dd)で時刻を取得
var local = gt.getDisplayValueInternal();
gs.info('local: ' + local);
時、分、秒を個別に取得することもできます。
var gt = new GlideTime();
gt.setValue('10:20:30');
// ◆ 時(0-12)を取得
// UTCで時(0-12)を取得
var hour12_utc = gt.getHourUTC();
gs.info('hour12_utc: ' + hour12_utc);
// ユーザのローカルタイムゾーンで時(0-12)を取得
var hour12_local = gt.getHourLocalTime();
gs.info('hour12_local: ' + hour12_local);
// ◆ 時(0-24)を取得
// UTCで時(0-24)を取得
var hour24_utc = gt.getHourOfDayUTC();
gs.info('hour24_utc: ' + hour24_utc);
// ユーザのローカルタイムゾーンで時(0-24)を取得
var hour24_local = gt.getHourOfDayLocalTime();
gs.info('hour24_local: ' + hour24_local);
// ◆ 分を取得
// UTCで分を取得
var minutes_utc = gt.getMinutesUTC();
gs.info('minutes_utc: ' + minutes_utc);
// ユーザのローカルタイムゾーンで時(0-24)を取得
var minutes_local = gt.getMinutesLocalTime();
gs.info('minutes_local: ' + minutes_local);
// ◆ 秒を取得
var seconds = gt.getSeconds();
gs.info('seconds : ' + seconds);
おわりに
日付・時刻の扱いは非常に難しい上に、API Referenceに書いてあることと実際の動きが違ったりするので、検証しつつコーディングしましょう。慎重に検証しながら書いたつもりですが、誤りやご指摘などあればお問い合わせフォームよりお願いします。
また、この記事では営業日・稼働日を考慮した日時の加算・減算処理は扱いませんでしたが、また別記事で説明したいと思います。
以上です。
コメント