2017.05.23

日付リテラル(相対日付値)検索について

日付リテラル

SuPICEバージョンアップしました! - SkyVisualEditor & SuPICE Blog でも触れていますが、SuPICEはバージョン2.0より日付リテラルによる相対的な日付での検索が可能となりました。

日付リテラルとは、実際に検索を実行する日から相対的に指定できる検索条件値です。

詳しくは以下のページをご覧ください。

日付形式と日付リテラル - Force.com SOQL および SOSL リファレンス

日付リテラルで検索可能な範囲

各リテラルで検索できる範囲は以下の画像のとおりです(本日を2017/01/12として)。

minowa_20170112_range_of_dateliteral.png minowa_20170112_range_of_dateliteral2.png 前3週間のときは今週より過去の3週間、翌3ヶ月のときは今月の次のつきから3ヶ月後までと、
基本的に前n〇〇のときは本〇〇を除外した過去の、翌n〇〇のときは本〇〇を除外した未来の期間が指定されますが、 例外として前n日の指定のときのみ本日を含みます。 リファレンスの範囲についての説明は、

・LAST_WEEKで「先週の最初の日の 00:00:00 から、その 7 日後まで」と記載されているがピッタリ7日後はもう今週なので含まれない (以下と未満の区別がつかない)
・LAST_N_WEEKSで「前週の最後の日の 00:00:00 から、その n 週前まで」とあるものの前週の最後の日は期間の終端なのでその日の最後の時間まで含まれる(0時と24時の区別がつかない)

など、やや微妙な表現が散見されるので実際に検索を実行するのが一番正確に情報を得られます。

Apexコード開発での日付移動の注意点

SuPICEでは日付リテラルのテストとして各日付リテラルの期間開始範囲外、期間開始範囲内、期間終了範囲内、期間終了範囲外の4種類の日時をもつテストデータをそれぞれ作成し、検索結果を確認しています。 例えば 2017/01/12に「翌3日」のテストを行う場合は
「2017/01/12 23:59:59」
「2017/01/13 00:00:00」
「2017/01/15 23:59:59」
「2017/01/16 00:00:00」
のデータを作成してから検索を実行し、
「2017/01/13 00:00:00」と
「2017/01/15 23:59:59」のデータが取得できることを確認します。 この方法で「前3ヶ月」のテストデータを作成する際には月の開始日時・終了日時を生成することになるのですが、そこで少々問題が発生しました。
// 今月の開始日を取得
Date startOfMonthDate = Date.today().toStartOfMonth();
// DateをDateTimeに変換
DateTime startOfMonthDateTime = DateTime.newInstance(startOfMonthDate.year(), startOfMonthDate.month(), startOfMonthDate.day());
// 3ヶ月引く
DateTime startDateTime = startOfMonthDateTime.addMonths(-3);
insert new List<TestObject__c> {
	// 期間開始範囲外(4ヶ月前の終わり)
	new TestObject__c( DateTimeField__c = startDateTime.addSeconds(-1) ),
	// 期間開始範囲内(3ヶ月前の始め)
	new TestObject__c( DateTimeField__c = startDateTime ),
	// 期間終了範囲内(先月の終わり)
	new TestObject__c( DateTimeField__c = startOfMonthDateTime ),
	// 期間終了範囲外(今月の始め)
	new TestObject__c( DateTimeField__c = startOfMonthDateTime.addSeconds(-1) )
};
List<TestObject__c> results = [SELECT DateTimeField__c FROM TestObject__c WHERE DateTimeField__c = LAST_N_MONTHS:3];
System.assert(results.size(), 2);
上記のApexコードは1月2月中は動作しますが、3月になるとテスト実行ユーザの設定次第で落ちるようになります。 原因は月を加減するaddMonthsメソッドをDateTime型で実行していることです。 上記のコードでは DateTime.newInstance メソッドでDateTime型に変換してから月を移動していますが、 DateTime.newInstance メソッドで生成するとタイムゾーンが適用された日時値が生成ます。 タイムゾーンが東京のユーザーで上記のコードを実行すると、取得できるのは「日本時間での 2017/01/01 00:00:00」であり、 グリニッジ標準時では「2016/12/31 15:00:00」、つまり1月前の日時になります。
これにより、addMonthsメソッドで月を移動するときに移動後の日付が先月を基準とした日付になってしまうのです。
// 1月と12月は日数が同じなので移動後も月の最終日、タイムゾーンを加味すると月始めになる
DateTime dt1 = DateTime.newInstance(2017, 1, 1);
System.debug(dt1);              //=> 2016-12-31 15:00:00
System.debug(dt1.addMonths(-1));//=> 2016-11-30 15:00:00
System.debug(dt1.addMonths(-2));//=> 2016-10-31 15:00:00
System.debug(dt1.addMonths(-3));//=> 2016-09-30 15:00:00
// 12月から実行すると11月の日数(30日)に統一されてしまう
DateTime dt2 = DateTime.newInstance(2016, 12, 1);
System.debug(dt2);              //=> 2016-11-30 15:00:00
System.debug(dt2.addMonths(-1));//=> 2016-10-30 15:00:00
System.debug(dt2.addMonths(-2));//=> 2016-09-30 15:00:00
System.debug(dt2.addMonths(-3));//=> 2016-08-30 15:00:00

この問題は処理の順番を入れ替え、

  1. 今月の最初の日付を取得
  2. 日付を日時型に変換
  3. 月を移動

とDateTime型に変換してから行っていた月の移動を、

  1. 今月の最初の日付を取得
  2. 月を移動
  3. 日付を日時型に変換

とDate型のまま月を移動することで対処できました。

おわりに

ということで、2017年最初の記事は日付リテラルによる相対検索の具体的な範囲と、Apexで日付を加減する際の注意点についてご紹介しました。

2017年もSkyVisualEditorとSuPICEをどうぞよろしくお願いいたします。

9 件
     
  • banner
  • banner

関連する記事