001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.fukurou.util; 017 018import org.opengion.fukurou.system.OgRuntimeException ; // 6.4.2.0 (2016/01/29) 019import java.util.Calendar; 020import java.util.Locale; 021import java.util.Map; 022import java.util.HashMap; 023import java.text.DateFormat; 024import java.text.SimpleDateFormat; 025import java.text.ParseException; 026import java.util.List; // 7.0.5.0 (2019/09/09) 027import java.util.ArrayList; // 7.0.5.0 (2019/09/09) 028 029import org.opengion.fukurou.system.DateSet; // 6.4.2.0 (2016/01/29) 030import static org.opengion.fukurou.system.HybsConst.BUFFER_SMALL; // 6.1.0.0 (2014/12/26) refactoring 031 032/** 033 * HybsDateUtil.java は、共通的に使用される Date,Calender関連メソッドを集約した、staticメソッドのみで構成されるクラスです。 034 * 035 * @og.rev 5.5.7.2 (2012/10/09) 新規作成 036 * 037 * @og.group ユーティリティ 038 * 039 * @version 5.5 040 * @author Kazuhiko Hasegawa 041 * @since JDK7.0, 042 */ 043public final class HybsDateUtil { 044 045 /** 各種フォーマットを簡易的に表した文字列 */ 046 /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */ 047 private static final Map<String,String> DATE_FORMAT = new HashMap<>(); 048 static { 049 DATE_FORMAT.put( "Y4" ,"yyyy" ); // 6.9.2.1 (2018/03/12) 050 DATE_FORMAT.put( "YMD" ,"yyyyMMdd" ); 051 DATE_FORMAT.put( "Y2MD" ,"yyMMdd" ); 052 DATE_FORMAT.put( "YM" ,"yyyyMM" ); 053 DATE_FORMAT.put( "MD" ,"MMdd" ); // 5.5.5.2 (2012/08/18) 054 DATE_FORMAT.put( "HMS" ,"HHmmss" ); 055 DATE_FORMAT.put( "HM" ,"HHmm" ); // 6.7.4.1 (2017/02/17) 056 DATE_FORMAT.put( "YMDHMS" ,"yyyyMMddHHmmss" ); 057 DATE_FORMAT.put( "YMDHM" ,"yyyyMMddHHmm" ); // 7.0.5.0 (2019/09/16) 058 DATE_FORMAT.put( "YMDH" ,"yyyyMMddHH" ); // 7.0.5.0 (2019/09/16) 059 DATE_FORMAT.put( "EEE" ,"EEE" ); 060 DATE_FORMAT.put( "YMDF" ,"yyyy/MM/dd" ); 061 DATE_FORMAT.put( "Y2MDF" ,"yy/MM/dd" ); 062 DATE_FORMAT.put( "YMF" ,"yyyy/MM" ); 063 DATE_FORMAT.put( "HMSF" ,"HH:mm:ss" ); 064 DATE_FORMAT.put( "HMF" ,"HH:mm" ); // 6.7.4.1 (2017/02/17) 065 DATE_FORMAT.put( "YMDHMSF" ,"yyyy/MM/dd HH:mm:ss" ); 066 DATE_FORMAT.put( "YMDHMF" ,"yyyy/MM/dd HH:mm" ); // 7.0.5.0 (2019/09/16) 067 DATE_FORMAT.put( "MDF" ,"MM/dd" ); // 5.5.0.2 (2012/03/09) 068 DATE_FORMAT.put( "MDEF" ,"MM/dd(EEE)" ); // 5.5.0.2 (2012/03/09) 和暦 069 DATE_FORMAT.put( "MDHMF" ,"MM/dd HH:mm" ); // 7.0.0.1 (2018/10/09) 070 DATE_FORMAT.put( "MD2F" ,"MM月dd日" ); // 5.5.5.2 (2012/08/18) 漢字 071 DATE_FORMAT.put( "HM2F" ,"HH時mm分" ); // 7.0.0.1 (2018/10/09) 漢字 072 DATE_FORMAT.put( "MDHM2F" ,"MM月dd日 HH時mm分" ); // 7.0.0.1 (2018/10/09) 漢字 073 DATE_FORMAT.put( "GYMDF" ,"GGGGyyyy年MM月dd日" ); // 5.5.0.2 (2012/03/09) 和暦 074 DATE_FORMAT.put( "G2YMDF" ,"Gyyyy/MM/dd" ); // 5.5.0.2 (2012/03/09) 和暦 075 DATE_FORMAT.put( "GYMF" ,"GGGGyyyy年MM月" ); // 5.5.0.2 (2012/03/09) 和暦 076 DATE_FORMAT.put( "GYF" ,"GGGGyyyy" ); // 5.5.0.2 (2012/03/09) 和暦 077 } 078 079 private static final int DD = 1000 * 60 * 60 * 24 ; // ミリ秒 → 日 080 private static final int HH = 1000 * 60 * 60 ; // ミリ秒 → 時 081 private static final int MM = 1000 * 60 ; // ミリ秒 → 分 082 private static final int SS = 1000 ; // ミリ秒 → 秒 083 084 /** 085 * デフォルトコンストラクターをprivateにして、 086 * オブジェクトの生成をさせないようにする。 087 * 088 * @og.rev 5.5.7.2 (2012/10/09) 新規作成 089 * 090 */ 091 private HybsDateUtil() {} 092 093 /** 094 * 指定の文字列から、以下の文字を削除した文字列を返します。 095 * '/' , '-' , ' ' , ':' の数字以外の文字を含むフォーマットされた 096 * 日付文字列を、日付データだけに変換する場合に利用することを想定しています。 097 * よって、マイナス記号や、小数点、コンマなども削除されます。 098 * このメソッドでは、日付としての整合性や桁チェックは行いませんが、 099 * 桁数は、6桁、8桁、14桁のどれかに、合わせます。 100 * 「yyyy/MM/dd HH:mm:ss」 形式を基準としますが、「yyyy/M」「yyyy/M/d」「yy/M/d」「M/d」 101 * 「HH:mm:ss」「H:m」形式にも、対応します。 102 * "/" が、"-" に変更されているケースも対応可能ですが、月/年 形式や、英語、日本語の 103 * 月表示には未対応です。 104 * 105 * 引数が、null の場合は、ゼロ文字列に、変換します。 106 * 107 * ※ 6.0.2.5 (2014/10/31) 桁数チェックだけは行います。 108 * 桁数は、6桁、8桁、14桁のどれかに、合わせます。 109 * 110 * @og.rev 5.5.7.2 (2012/10/09) 新規作成 111 * @og.rev 5.5.8.3 (2012/11/17) 数字のみ返す仕様だったが、対象以外の文字入力はそのまま返すよう変更 112 * @og.rev 6.0.2.5 (2014/10/31) 簡易的な桁数チェックだけは行います。 113 * @og.rev 6.2.3.0 (2015/05/01) 内部処理を大幅に変更します。 114 * 115 * @param value 任意の文字列(例:2001/04/17 15:48:22) 116 * 117 * @return 数字だけで構成される文字列(例:20010417154822)(nullはゼロ文字列を返します) 118 * @og.rtnNotNull 119 */ 120 public static String parseNumber( final String value ) { 121 if( value == null || value.isEmpty() ) { return ""; } 122 123 // 年 や、年月日 が省略された場合は、実行日をセットする。 124 final String today = DateSet.getDate( "yyyyMMdd" ); 125 126 String val = value.trim(); 127 val = val.replaceAll( "-" , "/" ); // yyyy-MM-dd 形式を、yyyy/MM/dd 形式にする。 128 129 final int ad = val.indexOf( ' ' ) ; 130 String ymd = val ; 131 String hms = null ; 132 133 if( ad > 0 ) { // スペースがあれば、年月日 と 時分秒 に別れる。 134 ymd = val.substring( 0,ad ); 135 hms = val.substring( ad+1 ); 136 } 137 else if( val.indexOf( ':' ) > 0 ) { 138 ymd = today; // 年月日 は今日になる。 139 hms = val; 140 } 141 142 final StringBuilder buf = new StringBuilder( BUFFER_SMALL ); 143 144 if( ymd != null ) { 145 final String[] ymdSp = ymd.split( "/" ); 146 switch( ymdSp.length ) { 147 case 1 : buf.append( ymdSp[0] ); break; // "/" が存在しない。 148 case 2 : if( ymdSp[0].length() < 4 ) { // MM/dd のケース。yy/MM は想定外 149 buf.append( today.substring( 0,4 ) ); // yyyy の年を設定 150 } 151 buf.append( addZero( ymdSp[0] ) ).append( addZero( ymdSp[1] ) ); break; 152 default: if( ymdSp[0].length() == 2 ) { // yy/MM/dd のケースを想定 153 buf.append( today.substring( 0,2 ) ); // yy の年の先頭2桁を設定 154 } 155 buf.append( ymdSp[0] ) 156 .append( addZero( ymdSp[1] ) ) 157 .append( addZero( ymdSp[2] ) ); break; 158 } 159 } 160 if( hms != null ) { 161 final String[] hmsSp = hms.split( ":" ); // HH:mm:ss 162 switch( hmsSp.length ) { 163 case 1 : buf.append( hmsSp[0] ); break; // ":" が存在しない。 164 case 2 : buf.append( addZero( hmsSp[0] ) ) // HH:mm のケース。mm:ss は想定外 165 .append( addZero( hmsSp[1] ) ) 166 .append( "00" ); break; 167 default: buf.append( addZero( hmsSp[0] ) ) // HH:mm:ss のケースを想定 168 .append( addZero( hmsSp[1] ) ) 169 .append( addZero( hmsSp[2] ) ); break; 170 } 171 } 172 173 return buf.toString(); 174 } 175 176 /** 177 * 指定の文字列が、一桁の場合、先頭に 0 を追加します。 178 * 179 * これは、3/4 の様な日付を、0304 にする場合のサポートメソッドです。 180 * 181 * @og.rev 6.2.3.0 (2015/05/01) 新規追加 182 * 183 * @param val 任意の文字列 184 * 185 * @return 一桁の場合、先頭に 0 を追加 186 */ 187 private static String addZero( final String val ) { 188 return val.length() == 1 ? "0" + val : val ; 189 } 190 191 /** 192 * 指定の文字列から、yyyy-mm-dd hh:mm:ss 形式の文字列を作成します。 193 * 194 * これは、java.sql.Timestamp オブジェクトを文字列から作成するに当たり、 195 * Timestamp の文字列形式にしなければならないためです。 196 * 桁数は、8桁 または、14桁以外の場合は、変換エラーとします。 197 * 198 * @og.rev 5.5.8.5 (2012/11/27) 新規作成 199 * 200 * @param value 任意の文字列(例:20010417 or 20010417154822) 201 * 202 * @return Timestampの文字列形式(例:2001-04-17 00:00:00 or 2001-04-17 15:48:22) 203 */ 204 public static String parseTimestamp( final String value ) { 205 if( value == null || value.length() != 8 && value.length() != 14 ) { // 6.9.7.0 (2018/05/14) PMD 206 final String errMsg = "日付文字列は、8桁 または、14桁で指定してください。" 207 + " value=[" + value + "]" ; 208 throw new OgRuntimeException( errMsg ); 209 } 210 211 // 6.0.2.5 (2014/10/31) char を append する。 212 final StringBuilder buf = new StringBuilder( BUFFER_SMALL ) 213 .append( value.substring( 0,4 ) ).append( '-' ) 214 .append( value.substring( 4,6 ) ).append( '-' ) 215 .append( value.substring( 6,8 ) ).append( ' ' ); 216 if( value.length() == 8 ) { 217 buf.append( "00:00:00" ); 218 } 219 else { 220 buf.append( value.substring( 8,10 ) ).append( ':' ) 221 .append( value.substring( 10,12 ) ).append( ':' ) 222 .append( value.substring( 12,14 ) ); 223 } 224 225 return buf.toString(); 226 } 227 228 /** 229 * 日付文字列の桁数の整合性を取ります。 230 * これは、内部で、parseNumber(String) 処理により、不要なフォーマット記号を削除します。 231 * ここでは、基本的には、6文字(yyyyMM)、8文字(yyyyMMdd)、14文字(yyyyMMddHHmmss) 232 * の日付文字列を作成することを想定していますが、指定の桁数以外は、エラーになります。 233 * 234 * 引数が、null ⇒ 桁数に無関係に、空文字列を返す。 235 * 引数の桁数が一致 ⇒ その値を返す。 236 * 引数の桁数が不一致 ⇒ エラー 237 * ただし、引数の最大長は、14ケタに制限しています。 238 * 239 * このメソッドでは、日付として成立しているかどうか(99999999など)は判定していません。 240 * 241 * @og.rev 5.6.6.0 (2013/07/05) メソッドの内容を移す。 242 * 243 * @param value 任意の日付け文字列 244 * @param size 変換したい桁数 245 * 246 * @return 数字だけで構成される文字列(例:20010417154822)(nullはゼロ文字列を返します) 247 * @og.rtnNotNull 248 */ 249 public static String parseDate( final String value , final int size ) { 250 return parseDate( value , size , size ); // 最小と最大を同じ値にする。 251 } 252 253 /** 254 * 日付文字列の桁数の整合性を取ります。 255 * これは、内部で、parseNumber(String) 処理により、不要なフォーマット記号を削除します。 256 * ここでは、基本的には、6文字(yyyyMM)、8文字(yyyyMMdd)、14文字(yyyyMMddHHmmss) 257 * の日付文字列を作成することを想定していますが、それ以外の桁数でも下記のルールに従って 258 * 処理されます。 259 * 260 * 引数が、null ⇒ 桁数に無関係に、空文字列を返す。 261 * 引数の桁数が範囲内 ⇒ 以下の処理を実行する。 262 * 引数の桁数を同じ ⇒ そのまま返す。 263 * 引数の桁数より大きい ⇒ 余をカットして、引数の最大長にそろえる。 264 * 引数の桁数に足りない ⇒ "20000101000000" の文字列の部分文字列を結合させて、引数の最大長にそろえる。 265 * ただし、引数の最大長は、14ケタに制限しています。 266 * 267 * このメソッドでは、日付として成立しているかどうか(99999999など)は判定していません。 268 * 269 * @og.rev 5.5.7.2 (2012/10/09) 新規作成 270 * @og.rev 5.6.1.1 (2013/02/08) 桁数チェック導入。6桁以下だとエラーにする。 271 * @og.rev 5.6.6.0 (2013/07/05) 桁数チェックの最小-最大指定 272 * @og.rev 6.2.3.0 (2015/05/01) len == maxSize のとき、パース文字列ではなく、元の値を返していた。 273 * 274 * @param value 任意の日付け文字列 275 * @param minSize 変換したい桁数の最小値 276 * @param maxSize 変換したい桁数の最大値 277 * 278 * @return 数字だけで構成される文字列(例:20010417154822)(nullはゼロ文字列を返します) 279 * @og.rtnNotNull 280 */ 281 public static String parseDate( final String value , final int minSize , final int maxSize ) { 282 if( value == null ) { return ""; } 283 284 // 引数の最大長は、14ケタに制限しています。 285 if( maxSize > 14 ) { 286 final String errMsg = "日付登録に許可できる最大桁数は、14ケタです。" 287 + " maxSize=[" + maxSize + "]" ; 288 throw new OgRuntimeException( errMsg ); 289 } 290 291 final String rtn = parseNumber( value ); 292 final int len = rtn.length() ; 293 294 if( len < minSize || len > maxSize ) { 295 final String errMsg = "日付文字列は、最小[" 296 + minSize + "] から、最大[" + maxSize + "]の範囲で指定してください。" 297 + " value=[" + value + "]" ; 298 throw new OgRuntimeException( errMsg ); 299 } 300 301 return rtn ; 302 } 303 304 /** 305 * 日付文字列の厳密な整合性チェックを行います。 306 * ここで指定できるのは、8文字(yyyyMMdd)、14文字(yyyyMMddHHmmss)のどちらかの 307 * 数字だけの日付文字列であり、それが、日付として正しいかどうかのチェックを行います。 308 * 正しければ、true を、間違っていれば、false を返します。 309 * ここでは、20120230(2月30日)などの日付や、20120101235960 なども false になります。 310 * 引数が、null および、空文字列の場合も、false を返しますので、避けたい場合は、事前に 311 * 判定しておいてください。 312 * 313 * 内部処理としては、DateFormat で、setLenient( false ) を設定することで、 314 * 日付/時刻解析を厳密に解析するにして、ParseException が発生しないかどうか判定しています。 315 * 316 * @og.rev 5.5.7.2 (2012/10/09) 新規作成 317 * 318 * @param value 数字だけで構成される日付け文字列 319 * 320 * @return true:日付として正しい場合/false:日付として間違っている場合 321 */ 322 public static boolean isStrict( final String value ) { 323 if( value == null || value.length() != 8 && value.length() != 14 ) { return false; } // 6.9.7.0 (2018/05/14) PMD Useless parentheses. 324 325 // 日付の厳密なチェック 326 final String form = (value.length() == 8) ? "yyyyMMdd" : "yyyyMMddHHmmss" ; 327 final DateFormat formatter = new SimpleDateFormat( form,Locale.JAPAN ); 328 formatter.setLenient( false ); // 日付/時刻解析を厳密に行う(false=厳密) 329 330 boolean flag ; 331 try { 332 formatter.parse( value ); 333 flag = true; 334 } 335 catch( final ParseException ex ) { 336 flag = false; 337 } 338 339 return flag; 340 } 341 342 /** 343 * 日付関係の情報を簡易的に処理します。 344 * 345 * 引数に与えるのは、{@DATE.XXXX} の XXXX 文字列になります。 346 * この "XXXX" 文字列は、"key prmA prmB prmC" 形式を取ることができます。 347 * 各文字列をスペースで分割して、先頭から変数に割り当てます。 348 * また、prmA の日付文字判定と、数値変換と、prmC の数値変換も行います。 349 * ただし、リクエスト変数の @ 文字列変換は、出来ません。 350 * 351 * @og.rev 6.9.2.1 (2018/03/12) 新規追加 352 * 353 * @param value 日付引数のパラメータ 354 * 355 * @return メッセージ情報 356 * @og.rtnNotNull 357 * @see #getDateFormat( String , String ,String , int ) 358 */ 359 public static String getDateFormat( final String value ) { 360 // {@DATE.XXXX AA BB CC} を分割 361 final String[] vals = StringUtil.csv2Array( value,' ' ); // ダブルクオート内は保持される。 362 363 final String key = vals[0] ; 364 365 String prmA = vals.length >= 2 ? vals[1] : null ; 366 String prmB = vals.length >= 3 ? vals[2] : null ; 367 String prmC = vals.length >= 4 ? vals[vals.length-1] : null ; // 互換性。最後の値が、CC引数 368 369 // AA 引数のコマンド判定方法(先頭が数字以外) 370 if( StringUtil.isNotNull( prmA ) ) { 371 final char chA = prmA.charAt(0); 372 if( chA < '0' || chA > '9' ) { // 先頭が、数字以外の場合は、コマンドなので、一つずつずらす。 373 prmC = prmB; 374 prmB = prmA; 375 prmA = null; 376 } 377 } 378 379 // CC 引数を、"H" , "D" , "M" 以外でも使用できるように拡張します。 380 int intC = 0; 381 if( StringUtil.isNotNull( prmC ) ) { 382 try { 383 intC = Integer.parseInt( prmC ); 384 } 385 catch( final NumberFormatException ex ) { 386 final String errMsg = "CC引数が数字ではありません。value=[" + value + "]" 387 + ex.getMessage() ; 388 System.err.println( errMsg ); 389 } 390 } 391 392 // prmA が null か、isEmpty() の場合は、現在時刻が使用される。 393 return getDateFormat( key,prmA,prmB,intC ); 394 } 395 396 /** 397 * 日付関係の情報を簡易的に処理します。 398 * 399 * 処理コマンドと、CC引数の加減算パラメータを使用しないバージョンです。 400 * 401 * @og.rev 6.9.2.1 (2018/03/12) メソッドの引数を簡素化 402 * 403 * @param key フォーマットの予約語 404 * @param prmA 基準となる日付(nullの場合は、処理時刻) 405 * 406 * @return メッセージ情報 407 * @og.rtnNotNull 408 * @see #getDateFormat( String , String ,String , int ) 409 */ 410 public static String getDateFormat( final String key , final String prmA ) { 411 return getDateFormat( key,prmA,null,0 ); 412 } 413 414// /** 415// * 日付関係の情報を簡易的に処理します。 416// * 417// * CC引数の加減算パラメータは、0 です。 418// * 419// * @og.rev 5.7.4.1 (2014/03/14) CC 引数を拡張するため、旧メソッドを再現しておきます。 420// * @og.rev 6.9.2.1 (2018/03/12) 廃止 421// * 422// * @param key フォーマットの予約語 423// * @param prmA 基準となる日付(nullの場合は、処理時刻) 424// * @param prmB 処理コマンド 425// * 426// * @return メッセージ情報 427// * @og.rtnNotNull 428// * @see #getDateFormat( String , String ,String , int ) 429// */ 430// public static String getDateFormat( final String key ,final String prmA ,final String prmB ) { 431// return getDateFormat( key,prmA,prmB,0 ); 432// } 433 434 /** 435 * 日付関係の情報を簡易的に処理します。 436 * 437 * 第一引数(key) "XXXX" は、日付処理を行うフォーマットの予約語になっています。 438 * ・Y4 :4文字の年データ(yyyy)を扱います。 439 * ・YMD :8文字の4-2-2年月日データ(yyyyMMdd)を扱います。 440 * ・Y2MD :6文字の2-2-2年月日データ(yyMMdd)を扱います。 441 * ・YM :6文字の4-2年月データ(yyyyMM)を扱います。 442 * ・HMS :6文字の2-2-2時分秒データ(HHmmss)を扱います。 443 * ・HM :4文字の2-2時分データ(HHmm)を扱います。6.7.4.1 (2017/02/17) 444 * ・YMDHMS :14文字の4-2-2-2-2-2年月日時分秒データ(yyyyMMddHHmmss)を扱います。 445 * ・EEE :曜日をデフォルトロケールで表示します。 446 * 447 * F付きは、フォーマットされた日付を返します。 448 * ・YMDF :10文字の日付表現(yyyy/MM/dd)を扱います。 449 * ・Y2MDF :8文字の日付表現(yy/MM/dd)を扱います。 450 * ・YMF :7文字の日付表現(yyyy/MM)を扱います。 451 * ・HMSF :8文字の時刻表現(HH:mm:ss)を扱います。 452 * ・HMF :5文字の時刻表現(HH:mm)を扱います。6.7.4.1 (2017/02/17) 453 * ・YMDHMSF:19文字の日付表現(yyyy/MM/dd HH:mm:ss)を扱います。 454 * ・MDF :5文字の月日表現(MM/dd)を扱います。 455 * ・MDEF :5文字+曜日の月日表現(MM/dd(EEE))を扱います。 456 * ・MDHMF :11文字の月日時分表現(MM/dd HH:mm)を扱います。 (7.0.0.1 (2018/10/09) 追加) 457 * ・MD2F :漢字の月日表現(MM月dd日)を扱います。(5.5.5.2 追加) 458 * ・HM2F :漢字の時分表現(HH時mm分)を扱います。(7.0.0.1 (2018/10/09) 追加) 459 * ・MDHM2F :漢字の月日時分表現(MM月dd日 HH時mm分)を扱います。(7.0.0.1 (2018/10/09) 追加) 460 * ・GYMDF :和暦の年月日表現(GGGGyyyy年MM月dd日)を扱います。 461 * ・G2YMDF :和暦の日付表現(Gyyyy/MM/dd)を扱います。 462 * ・GYMF :和暦の年月表現(GGGGyyyy年MM月)を扱います。 463 * ・GYF :和暦の年表現(GGGGyyyy)を扱います。 464 * 465 * ・DIFF :日付の差分を求めます。(7.0.1.1 (2018/10/22) 追加) 466 * AA - BB を求め、CCの数値で単位を指定します。 467 * 468 * なお、上記以外のフォーマットを指定する場合は、XXXX部分に直接記述できます。(5.5.5.2 追加) 469 * ただし、基本的には、自由フォーマットは、エラーチェックがない為、使わないでください。 470 * 471 * 第二引数(prmA) AA は、基準となる日付を、yyyyMMdd形式で指定します。nullの場合は、現在時刻を使用します。 472 * 指定できる日付は、yyyyMMdd形式を推奨しますが、'/' , '-' , ' ' , ':' を削除して使います。 473 * 6桁の場合は、yyyyMM + 01 とし、8ケタの場合は、yyyyMMdd とし、14ケタ以上の場合は、前半14文字を 474 * yyyyMMddHHmmss として処理します。それ以外の桁数の場合は、エラーになります。 475 * たとえば、"2012/09/05 16:52:36" のようなフォーマットデータの場合、'/' , '-' , ' ' , ':' を削除して 476 * "20120905165236" に変換後、日付オブジェクトに変換されます。 477 * 478 * 第三引数(prmB) BB は、日付についての加減算処理を行うためのコマンドを指定します。 479 * nullの場合は、なにも加減算処理を行いません。 480 * ・SY :当年の最初の日付にセットします。(当年1月1日)。CC引数は、-N:N年前、0:当年(=SY)、N:N年後 6.9.2.1 (2018/03/12) 481 * ・SD :当月の最初の日付にセットします。(当月1日)。CC引数は、-N:N月前、0:当月(=SD)、N:N月後、-1:BSD と同じ、1:ASD と同じ 482 * ・SW :日付処理の週初め(月曜日)にセットします。日付は当日より前に移動します。CC引数は、-N:N週前、0:今週(=SW)、N:N週後 483 * ・SH :指定の最初の時にセットします。(分秒を0000にする)。CC引数は、時の倍数(4と指定すれば、4時間単位に前に戻る) 6.7.4.1 (2017/02/17) 484 * ・SM :指定の最初の分にセットします。(秒を00にする)。CC引数は、分の倍数(15と指定すれば、15分単位に前に戻る) 6.7.4.1 (2017/02/17) 485 * ・SS :指定の最初の秒にセットします。CC引数は、秒の倍数(15と指定すれば、15秒単位に前に戻る) 6.7.4.1 (2017/02/17) 486 * ・EY :当年の最後の日付にセットします。(当年年末)。CC引数は、-N:N年前、0:当年(=EY)、N:N年後 6.9.2.1 (2018/03/12) 487 * ・ED :当月の最後の日付にセットします。(当月月末)。CC引数は、-N:N月前、0:当月(=ED)、N:N月後、-1:BED と同じ、1:AED と同じ 488 * ・EW :日付処理の週末(日曜日)にセットします。日付は当日より後ろに移動します。CC引数は、-N:N週前、0:今週(=EW)、N:N週後 489 * ・EH :指定の次の時にセットします。(分秒を0000にした次の時)。CC引数は、時の倍数(4と指定すれば、4時間単位に前に進む) 6.7.4.1 (2017/02/17) 490 * ・EM :指定の次の分にセットします。(秒を00にした次の分)。CC引数は、分の倍数(15と指定すれば、15分単位に前に進む) 6.7.4.1 (2017/02/17) 491 * ・ES :指定の次の秒にセットします。CC引数は、秒の倍数(15と指定すれば、15秒単位に前に進む) 6.7.4.1 (2017/02/17) 492 * ・M1 ~ MXXX :月を指定の分だけ進めます。M1なら翌月、M6 なら半年後 493 * ・D1 ~ DXXX :日を指定の分だけ進めます。D1なら翌日、D200 なら200日後 494 * ・H1 ~ HXXX :時を指定の分だけ進めます。H1なら1時間後、H24 なら24時間後(5.5.5.6 (2012/08/31) 追加) 495 * ・MI :分を指定の分だけ進めます。第四引数(intC) で、時間を指定します。(6.8.4.1 (2017/12/18) 追加) 496 * ・YMD :CC 引数のYMD表記の日付を加減算します。6.8.4.1 (2017/12/18) 追加 497 * ・HM :CC 引数のHM表記の時刻を加減算します。6.8.4.1 (2017/12/18) 追加 498 * ・NO :AA 引数がnullの場合、現在時刻ではなく空文字列にします。 7.0.1.3 (2018/11/12) 追加 499 * ・(有閑)BSD :先月の最初の日付にセットします。(先月1日)(5.5.5.2 追加)。SD -1 と同等 500 * ・(有閑)BED :先月の最後の日付にセットします。(先月月末)(5.5.5.2 追加)。ED -1 と同等 501 * ・(有閑)ASD :翌月の最初の日付にセットします。(翌月1日)(5.5.5.2 追加)。SD 1 と同等 502 * ・(有閑)AED :翌月の最後の日付にセットします。(翌月月末)(5.5.5.2 追加)。ED 1 と同等 503 * 504 * 7.0.1.1 (2018/10/22) 505 * DATE.DIFF の場合、BB 引数は、日付データになります。AA-BB の関係です。 506 * 507 * CC 引数は、特別な処理で、BB 引数に対して、加算、減算のための数字を指定できます。(5.7.4.1 (2014/03/14) 追加) 508 * 従来は、BB 引数が、"H" , "D" , "M" の 1文字パラメータの場合のみ利用可能でした。 509 * これは、"H15" と指定するのと、"H" "15" と指定するのと同じ意味になります。 510 * 異なるのは、CC 引数も、(@CC)指定で、リクエストパラメータが使用できます。 511 * 従来は、文字列として結合された状態でしか、BB 引数を渡せませんでしたが、この、CC 引数の 512 * 追加で、日付の加減算を、パラメータ指定できるようになります。 513 * 数字以外の文字が指定されたり、パラメータの解析結果が NULL の場合には、BB引数自体も無視されます。 514 * 注意点は、各 BB 引数に応じて、数字の意味が異なるという事です。 515 * 516 * HXXX,DXXX,MXXX 形式に、CC 引数を付けた場合は、XXX にさらに加算されます。 517 * prmB に、数字を使用した場合、(コマンドでない場合)にも、CC 引数は、加算されます。 518 * 519 * 7.0.1.1 (2018/10/22) 520 * DATE.DIFF の場合、CC 引数は、差分の単位を指定するキーワードになります。AA-BB の結果を、 521 * 1:年 2:月 3:日 4:時 5:分 6:秒 に換算 して返します。端数は切り捨てで整数で返します。 522 * 523 * @og.rev 5.5.7.2 (2012/10/09) 新規作成 524 * @og.rev 5.6.1.1 (2013/02/08) prmB処理を、calendarCalc メソッドへ移動 525 * @og.rev 5.7.4.1 (2014/03/14) CC 引数を拡張 526 * @og.rev 6.4.3.3 (2016/03/04) Map#getOrDefault で対応する。 527 * @og.rev 7.0.1.1 (2018/10/22) DATE.DIFF 追加 528 * @og.rev 7.0.1.3 (2018/11/12) BB 引数に NO追加 529 * 530 * @param key フォーマットの予約語 531 * @param prmA 基準となる日付(nullの場合は、処理時刻) 532 * @param prmB 処理コマンド 533 * @param intC 加減算処理を行うための数字。0 は、BB引数の従来計算のまま。 534 * 535 * @return メッセージ情報 536 * @og.rtnNotNull 537 * @see #getDateFormat( String ) 538 * @see #getDateFormat( String , String ) 539 * @see #getCalendar( String ) AA 引数 からカレンダオブジェクトを作成します。 540 * @see #calendarCalc( Calendar , String , int ) BB 引数、CC 引数を元に、日付計算します。 541 */ 542 public static String getDateFormat( final String key ,final String prmA ,final String prmB ,final int intC ) { 543 // 7.0.1.3 (2018/11/12) prmA が null の場合で、prmB が "NO" の場合は、ゼロ文字列を返します。 544 if( StringUtil.isEmpty( prmA ) && "NO".equalsIgnoreCase( prmB ) ) { 545 return ""; 546 } 547 548 // prmA が null の場合は、そのまま、現在時刻が使われます。 549 final Calendar now = getCalendar( prmA ); 550 551 // 7.0.1.1 (2018/10/22) DATE.DIFF 追加 552 if( "DIFF".equalsIgnoreCase( key ) ) { 553 return calendarDiff( now,prmB,intC ); 554 } 555 556 // 5.6.1.1 (2013/02/08) getDateFormat( String ,String ,String ) から分離。 557 calendarCalc( now,prmB,intC ); // 5.7.4.1 (2014/03/14) CC 引数を拡張 558 559 // DATE_FORMAT に存在しないフォーマットを指定しても、エラーにしません。 560 // ただし、後処理でフォーマットエラーになる可能性は残ります。 561 // 6.4.3.3 (2016/03/04) Map#getOrDefault を使用します。 562 final String format = DATE_FORMAT.getOrDefault( key,key ); // 後ろの key は、値が null のときの初期値 563 564 //5.5.0.2 先頭Gの場合は和暦なのでformatterのLocaleを変更する 565 DateFormat formatter = null; 566 if( key.indexOf('G') == 0 ){ 567 formatter = new SimpleDateFormat( format, new Locale("ja","JP","JP")); 568 } 569 else{ 570 formatter = new SimpleDateFormat( format,Locale.JAPAN ); 571 } 572 573 return formatter.format( now.getTime() ); 574 } 575 576 /** 577 * 開始前設定値、または 終了後設定値の文字列から、オプション文字列を合成します。 578 * 基準となる日付に計算した結果を反映させます。 579 * 580 * CC引数の加減算パラメータは、0 です。 581 * 582 * @og.rev 5.7.4.1 (2014/03/14) CC 引数を拡張するため、旧メソッドを再現しておきます。 583 * 584 * @param now 基準となる日付(Calendarオブジェクト) 585 * @param prmB 処理コマンド 586 */ 587 public static void calendarCalc( final Calendar now,final String prmB ) { 588 calendarCalc( now,prmB,0 ); 589 } 590 591 /** 592 * 開始前設定値、または 終了後設定値の文字列から、オプション文字列を合成します。 593 * 基準となる日付に計算した結果を反映させます。 594 * 595 * prmB は、日付についての加減算処理を行うためのコマンドを指定します。 596 * ・SY :当年の最初の日付にセットします。(当年1月1日)。CC引数は、-N:N年前、0:当年(=SY)、N:N年後 6.9.2.1 (2018/03/12) 597 * ・SD :当月の最初の日付にセットします。(当月1日)。CC引数は、-N:N月前、0:当月(=SD)、N:N月後、-1:BSD と同じ、1:ASD と同じ 598 * ・SW :日付処理の週初め(月曜日)にセットします。日付は当日より前に移動します。CC引数は、-N:N週前、0:今週(=SW)、N:N週後 599 * ・SH :指定の最初の時にセットします。(分秒を0000にする)。CC引数は、時の倍数(4と指定すれば、4時間単位に前に戻る) 6.7.4.1 (2017/02/17) 600 * ・SM :指定の最初の分にセットします。(秒を00にする)。CC引数は、分の倍数(15と指定すれば、15分単位に前に戻る) 6.7.4.1 (2017/02/17) 601 * ・SS :指定の最初の秒にセットします。CC引数は、秒の倍数(15と指定すれば、15秒単位に前に戻る) 6.7.4.1 (2017/02/17) 602 * ・EY :当年の最後の日付にセットします。(当年年末)。CC引数は、-N:N年前、0:当年(=EY)、N:N年後 6.9.2.1 (2018/03/12) 603 * ・ED :当月の最後の日付にセットします。(当月月末)。CC引数は、-N:N月前、0:当月(=ED)、N:N月後、-1:BED と同じ、1:AED と同じ 604 * ・EW :日付処理の週末(日曜日)にセットします。日付は当日より後ろに移動します。CC引数は、-N:N週前、0:今週(=EW)、N:N週後 605 * ・EH :指定の次の時にセットします。(分秒を0000にした次の時)。CC引数は、時の倍数(4と指定すれば、4時間単位に前に進む) 6.7.4.1 (2017/02/17) 606 * ・EM :指定の次の分にセットします。(秒を00にした次の分)。CC引数は、分の倍数(15と指定すれば、15分単位に前に進む) 6.7.4.1 (2017/02/17) 607 * ・ES :指定の次の秒にセットします。CC引数は、秒の倍数(15と指定すれば、15秒単位に前に進む) 6.7.4.1 (2017/02/17) 608 * ・M1 ~ MXXX :月を指定の分だけ進めます。M1なら翌月、M6 なら半年後 609 * ・D1 ~ DXXX :日を指定の分だけ進めます。D1なら翌日、D200 なら200日後 610 * ・H1 ~ HXXX :時を指定の分だけ進めます。H1なら1時間後、H24 なら24時間後(5.5.5.6 (2012/08/31) 追加) 611 * ・MI :分を指定の分だけ進めます。第四引数(intC) で、時間を指定します。(6.8.4.1 (2017/12/18) 追加) 612 * ・YMD :CC 引数のYMD表記の日付を加減算します。6.8.4.1 (2017/12/18) 追加 613 * ・HM :CC 引数のHM表記の時刻を加減算します。6.8.4.1 (2017/12/18) 追加 614 * ・NO :AA 引数がnullの場合、現在時刻ではなく空文字列にします。 7.0.1.3 (2018/11/12) 追加 615 * ・(有閑)BSD :先月の最初の日付にセットします。(先月1日)(5.5.5.2 追加)。SD-1 と同等 616 * ・(有閑)BED :先月の最後の日付にセットします。(先月月末)(5.5.5.2 追加)。ED-1 と同等 617 * ・(有閑)ASD :翌月の最初の日付にセットします。(翌月1日)(5.5.5.2 追加)。SD1 と同等 618 * ・(有閑)AED :翌月の最後の日付にセットします。(翌月月末)(5.5.5.2 追加)。ED1 と同等 619 * ・数字:日を指定の分だけ進めます。D1 ~ DXXX の簡略系 620 * 621 * CC 引数は、特別な処理で、BB 引数に対して、加算、減算のための数字を指定できます。(5.7.4.1 (2014/03/14) 追加) 622 * HXXX,DXXX,MXXX 形式に、CC 引数を付けた場合は、XXX にさらに加算されます。 623 * prmB に、数字を使用した場合、(コマンドでない場合)にも、CC 引数は、加算されます。 624 * 6.8.4.1 (2017/12/18) BB 引数に、HM を指定した場合、時分のデータを加算します。この場合、時に関しては、24時間を越える 625 * 場合は、日付に加算されます。例えば、翌朝の6時を指定する場合、3000 と指定することで、1日+0600 となります。 626 * 627 * @og.rev 5.6.1.1 (2013/02/08) getDateFormat( String ,String ,String ) から分離。 628 * @og.rev 5.7.4.1 (2014/03/14) H1 ~ HXXX :時を指定の分だけ進める処理が実装されていなかった。 629 * @og.rev 5.7.4.1 (2014/03/14) CC 引数追加 630 * @og.rev 6.7.4.1 (2017/02/17) SH,SM,SS,EH,EM,ES 追加 631 * @og.rev 6.8.4.1 (2017/12/18) YMD , HM , MI 追加 632 * @og.rev 6.9.2.1 (2018/03/12) 年関連の機能(Y4,SY,EY)追加 633 * @og.rev 7.0.1.3 (2018/11/12) BB 引数に NO追加 634 * 635 * @param now 基準となる日付(Calendarオブジェクト) 636 * @param prmB 処理コマンド 637 * @param intC 加減算処理を行うための数字。0 は、BB引数の従来計算のまま。 638 */ 639 public static void calendarCalc( final Calendar now , final String prmB , final int intC ) { 640 641 // 基準は、intC == 0 の場合 642 if( prmB != null && prmB.length() > 0 ) { // 6.9.2.1 (2018/03/12) 年関連の機能(Y4,SY,EY)追加 643 if( "SY".equals( prmB ) ) { // (当年1月1日) 644 if( intC != 0 ) { now.add( Calendar.YEAR,intC ); } 645 now.set( Calendar.DAY_OF_YEAR,1 ); // 現在の年の何日目かで指定します。 646 } 647 else if( "EY".equals( prmB ) ) { // (当年年末) 648 if( intC != 0 ) { now.add( Calendar.YEAR,intC ); } 649 now.set( Calendar.MONTH,11 ); // 月は、0~11 の値で指定。 650 now.set( Calendar.DATE,now.getActualMaximum( Calendar.DATE ) ); 651 } 652 else if( "SD".equals( prmB ) ) { // (当月1日) 653 if( intC != 0 ) { now.add( Calendar.MONTH,intC ); } // 5.7.4.1 (2014/03/14) CC 引数追加 654 now.set( Calendar.DATE,1 ); 655 } 656 else if( "ED".equals( prmB ) ) { // (当月月末) 657 if( intC != 0 ) { now.add( Calendar.MONTH,intC ); } // 5.7.4.1 (2014/03/14) CC 引数追加 658 now.set( Calendar.DATE,now.getActualMaximum( Calendar.DATE ) ); 659 } 660 else if( "SH".equals( prmB ) ) { // (時戻し) 6.7.4.1 (2017/02/17) SH,SM,SS,EH,EM,ES 追加 661 // 6.9.8.0 (2018/05/28) FindBugs:整数の除算の結果を double または float にキャストしている 662// final int hh = intC == 0 ? 0 : (int)Math.floor( now.get( Calendar.HOUR_OF_DAY ) / intC ) * intC ; // 切捨て 663 final int hh = intC == 0 ? 0 : ( now.get( Calendar.HOUR_OF_DAY ) / intC ) * intC ; // 切捨て 664 now.set( Calendar.HOUR_OF_DAY,hh ); 665 now.set( Calendar.MINUTE ,0 ); 666 now.set( Calendar.SECOND ,0 ); 667 now.set( Calendar.MILLISECOND,0 ); 668 } 669 else if( "SM".equals( prmB ) ) { // (分戻し) 6.7.4.1 (2017/02/17) SH,SM,SS,EH,EM,ES 追加 670 // 6.9.8.0 (2018/05/28) FindBugs:整数の除算の結果を double または float にキャストしている 671// final int mm = intC == 0 ? 0 : (int)Math.floor( now.get( Calendar.MINUTE ) / intC ) * intC ; // 切捨て 672 final int mm = intC == 0 ? 0 : ( now.get( Calendar.MINUTE ) / intC ) * intC ; // 切捨て 673 now.set( Calendar.MINUTE ,mm ); 674 now.set( Calendar.SECOND ,0 ); 675 now.set( Calendar.MILLISECOND,0 ); 676 } 677 else if( "SS".equals( prmB ) ) { // (秒戻し) 6.7.4.1 (2017/02/17) SH,SM,SS,EH,EM,ES 追加 678 // 6.9.8.0 (2018/05/28) FindBugs:整数の除算の結果を double または float にキャストしている 679// final int ss = intC == 0 ? 0 : (int)Math.floor( now.get( Calendar.SECOND ) / intC ) * intC ; // 切捨て 680 final int ss = intC == 0 ? 0 : ( now.get( Calendar.SECOND ) / intC ) * intC ; // 切捨て 681 now.set( Calendar.SECOND ,ss ); 682 now.set( Calendar.MILLISECOND,0 ); 683 } 684 else if( "EH".equals( prmB ) ) { // (時進め) 6.7.4.1 (2017/02/17) SH,SM,SS,EH,EM,ES 追加 685 final int hh = intC == 0 ? 0 : (int)Math.ceil( (double)now.get( Calendar.HOUR_OF_DAY ) / intC ) * intC ; // 切り上げ 686 if( hh == 0 || hh >= 24 ) { 687 now.add( Calendar.DATE ,1 ); // 日を加算 688 now.set( Calendar.HOUR_OF_DAY,0 ); 689 } 690 else { 691 now.set( Calendar.HOUR_OF_DAY,hh ); 692 } 693 now.set( Calendar.MINUTE ,0 ); 694 now.set( Calendar.SECOND ,0 ); 695 now.set( Calendar.MILLISECOND,0 ); 696 } 697 else if( "EM".equals( prmB ) ) { // (分進め) 6.7.4.1 (2017/02/17) SH,SM,SS,EH,EM,ES 追加 698 final int mm = intC == 0 ? 0 : (int)Math.ceil( (double)now.get( Calendar.MINUTE ) / intC ) * intC ; // 切切り上げ 699 if( mm == 0 || mm >= 60 ) { 700 now.add( Calendar.HOUR_OF_DAY,1 ); // 時を加算 701 now.set( Calendar.MINUTE ,0 ); 702 } 703 else { 704 now.set( Calendar.MINUTE,mm ); 705 } 706 now.set( Calendar.SECOND ,0 ); 707 now.set( Calendar.MILLISECOND,0 ); 708 } 709 else if( "ES".equals( prmB ) ) { // (秒進め) 6.7.4.1 (2017/02/17) SH,SM,SS,EH,EM,ES 追加 710 final int ss = intC == 0 ? 0 : (int)Math.ceil( (double)now.get( Calendar.SECOND ) / intC ) * intC ; // 切り上げ 711 if( ss == 0 || ss >= 60 ) { 712 now.add( Calendar.MINUTE ,1 ); // 分を加算 713 now.set( Calendar.SECOND ,0 ); 714 } 715 else { 716 now.set( Calendar.SECOND,ss ); 717 } 718 now.set( Calendar.MILLISECOND,0 ); 719 } 720 else if( "BSD".equals( prmB ) ) { // (先月1日) 721 // 5.7.4.1 (2014/03/14) CC 引数追加 722 now.add( Calendar.MONTH,intC-1 ); now.set( Calendar.DATE,1 ); 723 } 724 else if( "BED".equals( prmB ) ) { // (先月月末) 725 // 5.7.4.1 (2014/03/14) CC 引数追加 726 now.add( Calendar.MONTH,intC-1 ); now.set( Calendar.DATE,now.getActualMaximum( Calendar.DATE ) ); 727 } 728 else if( "ASD".equals( prmB ) ) { // (翌月1日) 729 // 5.7.4.1 (2014/03/14) CC 引数追加 730 now.add( Calendar.MONTH,intC+1 ); now.set( Calendar.DATE,1 ); 731 } 732 else if( "AED".equals( prmB ) ) { // (翌月月末) 733 // 5.7.4.1 (2014/03/14) CC 引数追加 734 now.add( Calendar.MONTH,intC+1 ); now.set( Calendar.DATE,now.getActualMaximum( Calendar.DATE ) ); 735 } 736 else if( "SW".equals( prmB ) ) { // 週初め(月曜日)セット 737 // 5.7.4.1 (2014/03/14) CC 引数追加 738 if( intC != 0 ) { now.add( Calendar.DATE,intC*7 ); } // まず、基準の日付を週単位で加減算する。 739 740 // 日付型文字列入力データの開始日を月曜日にセットします。 741 // SUNDAY=1 , MONDAY=2 になります。月曜日との差だけ、前に戻します。 742 // 指定日が日曜日の場合は、月曜日まで戻します。 743 744 final int shu = now.get( Calendar.DAY_OF_WEEK ) - Calendar.MONDAY ; 745 746 if( shu > 0 ) { now.add( Calendar.DATE, -shu ); } 747 else if( shu < 0 ) { now.add( Calendar.DATE, -6 ); } 748 } 749 else if( "EW".equals( prmB ) ) { // 週末(日曜日)にセット 750 // 5.7.4.1 (2014/03/14) CC 引数追加 751 if( intC != 0 ) { now.add( Calendar.DATE,intC*7 ); } // まず、基準の日付を週単位で加減算する。 752 753 // 日付型文字列入力データの終了日を日曜日にセットします。 754 // SUNDAY=1 , MONDAY=2 になります。日曜日になるように、先に進めます。 755 final int shu = now.get( Calendar.DAY_OF_WEEK ) ; 756 if( shu != Calendar.SUNDAY ) { now.add( Calendar.DATE, 8-shu ); } 757 } 758 // 6.8.4.1 (2017/12/18) YMD 追加(年月日の繰り上げ表記を加味した加算) 759 else if( "YMD".equals( prmB ) ) { 760 final int year = intC / 10000; 761 final int month = ( intC / 100 ) % 100; 762 final int date = intC % 100; 763 764 now.add( Calendar.YEAR , year ); 765 now.add( Calendar.MONTH , month ); 766 now.add( Calendar.DATE , date ); 767 } 768 // 6.8.4.1 (2017/12/18) HM 追加(時分の24時間表記を加味した加算) 769 else if( "HM".equals( prmB ) ) { // 注意:prmB.charAt(0) == 'H' より先に判定しておきます。 770 final int hour = intC / 100; 771 final int minute = intC % 100; 772 773 now.add( Calendar.HOUR_OF_DAY , hour ); 774 now.add( Calendar.MINUTE , minute ); 775 } 776 // 6.8.4.1 (2017/12/18) HM 分を指定の分だけ進めます。 777 else if( "MI".equals( prmB ) ) { // 注意:prmB.charAt(0) == 'M' より先に判定しておきます。 778 now.add( Calendar.MINUTE , intC ); 779 } 780 // 7.0.1.3 (2018/11/12) AA 引数がnullの場合、現在時刻ではなく空文字列にします。 781 else if( "NO".equals( prmB ) ) { 782 // 何もしません。 783 } 784 // 6.9.2.1 (2018/03/12) Y1 ~ YXXX :年を指定の分だけ進める処理 785 else if( prmB.charAt(0) == 'Y' ) { 786 int year = intC ; 787 if( prmB.length() > 1 ) { year += Integer.parseInt( prmB.substring( 1 ) ); } 788 now.add( Calendar.YEAR , year ); 789 } 790 else if( prmB.charAt(0) == 'H' ) { // 6.1.0.0 (2014/12/26) refactoring 791 int hour = intC ; 792 if( prmB.length() > 1 ) { hour += Integer.parseInt( prmB.substring( 1 ) ); } 793 now.add( Calendar.HOUR_OF_DAY , hour ); 794 } 795 else if( prmB.charAt(0) == 'D' ) { // 6.1.0.0 (2014/12/26) refactoring 796 int day = intC ; 797 if( prmB.length() > 1 ) { day += Integer.parseInt( prmB.substring( 1 ) ); } 798 now.add( Calendar.DATE, day ); 799 } 800 else if( prmB.charAt(0) == 'M' ) { // 6.1.0.0 (2014/12/26) refactoring 801 int month = intC ; 802 if( prmB.length() > 1 ) { month += Integer.parseInt( prmB.substring( 1 ) ); } 803 now.add( Calendar.MONTH , month ); 804 } 805 else { 806 // 上記のパターン以外は、数字(加減算する日数)なので、変換できなければ、フォーマットエラー 807 try { 808 final int day = Integer.parseInt( prmB ) + intC ; // 5.7.4.1 (2014/03/14) CC 引数追加 809 now.add( Calendar.DATE, day ); 810 } 811 catch( final NumberFormatException ex ) { 812 final String errMsg = "日付変数パラメータに、不正な値が指定されました。以下の中から指定しなおしてください。" 813// + "指定可能:[SD,ED,SW,SH,SM,SS,EW,EH,EM,ES,H1~HXXX,D1~DXXX,M1~MXXX,HMS,BSD,BED,ASD,AED]" 814 + "指定可能:[SY,SD,SW,SH,SM,SS,EY,ED,EW,EH,EM,ES,M1~MXXX,D1~DXXX,H1~HXXX,MI,YMD,HM,NO]" 815 + " prmB=[" + prmB + "]" ; 816 throw new OgRuntimeException( errMsg,ex ); 817 } 818 } 819 } 820 } 821 822 /** 823 * 日付の差分を求めます。 824 * timeA - timeB を求め、intCの数値で単位を指定します。 825 * 826 * intC は、1:年 2:月 3:日 4:時 5:分 6:秒 に換算した単位での差分になります。 827 * 端数は出ません。 828 * 829 * @og.rev 7.0.1.1 (2018/10/22) DATE.DIFF 追加 830 * 831 * @param timeA 基準となる日付(Calendarオブジェクト) 832 * @param prmB 日付データになります。AA-BB の関係です。 833 * @param intC 差分の単位を指定するキーワード(1:年 2:月 3:日 4:時 5:分 6:秒 に換算) 834 * @return 指定の時間の差(timeA - timeB マイナスもある) 835 */ 836 public static String calendarDiff( final Calendar timeA , final String prmB , final int intC ) { 837 final Calendar timeB = getCalendar( prmB ); 838 839 final int diff ; 840 if( intC == 1 ) { // 差分年 841 diff = timeA.get( Calendar.YEAR ) - timeB.get( Calendar.YEAR ); 842 } 843 else if( intC == 2 ) { // 差分月数 844 diff = ( timeA.get( Calendar.YEAR ) - timeB.get( Calendar.YEAR ) ) * 12 845 + ( timeA.get( Calendar.MONTH ) - timeB.get( Calendar.MONTH ) ) ; 846 847 // // 月の計算ロジック( http://javatechnology.net/java/date-diff-month/ ) 848 // // マイナス計算も必要なので、今回は不採用 849 // timeA.set( Calendar.DATE, 1 ); // 端数を無視するため、1日にセットします。 850 // timeB.set( Calendar.DATE, 1 ); // 同上 851 852 // int count = 0; 853 // while( timeA.before( timeB ) ) { 854 // timeA.add( Calendar.MONTH, 1 ); 855 // count++; 856 // } 857 // diff = count; 858 } 859 else { 860 final long diffSec = timeA.getTimeInMillis() - timeB.getTimeInMillis() ; // 時間の差(ミリ秒) 861 862 switch( intC ) { 863 case 3: diff = (int)( diffSec / DD ); break; // 日単位 864 case 4: diff = (int)( diffSec / HH ); break; // 時単位 865 case 5: diff = (int)( diffSec / MM ); break; // 分単位 866 case 6: diff = (int)( diffSec / SS ); break; // 秒単位 867 default: diff = (int)( diffSec / DD ); break; // (初期値)日単位 868 } 869 } 870 871 return Integer.toString( diff ) ; 872 } 873 874 /** 875 * 指定の引数の日付け文字列より、カレンダオブジェクトを作成します。 876 * 引数は、数字以外の文字を削除した状態に変換後、処理に回します。 877 * 不要な文字を削除した状態で、8文字以上になるように指定してください。 878 * 例外的に、6文字の場合は、yyyyMM01 とみなして、"01" 文字列を付与します。 879 * 引数に null を指定すると、現在時刻のカレンダを返します。 880 * それ以外のデータで、8ケタ以下の場合は、RuntimeException が発生します。 881 * 8ケタ以上14ケタ未満の場合は、8ケタ分を、年月日に分離したカレンダ 882 * オブジェクトを作成します。14ケタ以上で初めて、時分秒を含むカレンダ 883 * を作成します。 884 * 885 * @og.rev 5.5.7.2 (2012/10/09) 新規作成 886 * @og.rev 5.5.8.2 (2012/11/09) value の判定に、null と ゼロ文字列を判定する。 887 * 888 * @param value 日付け文字列 889 * 890 * @return カレンダオブジェクト(引数がnullの場合は、現在時刻) 891 * @og.rtnNotNull 892 */ 893 public static Calendar getCalendar( final String value ) { 894 final Calendar cal = Calendar.getInstance(); 895 896 if( value == null || value.isEmpty() ) { return cal; } // 5.5.8.2 (2012/11/09) null と ゼロ文字列を判定する。 897 898 // 日付表記に不要な文字を削除します。 899 String dateStr = parseNumber( value ) ; 900 901 if( dateStr.length() == 6 ) { dateStr = dateStr + "01"; } // yyyyMM01 形式に無理やり合わせる。 902 else if( dateStr.length() < 8 ) { 903 final String errMsg = "日付指定パラメータに、不正な値が指定されました。value=[" + value + "]" ; 904 throw new OgRuntimeException( errMsg ); 905 } 906 907 cal.clear(); // 日付文字列が存在するので、カレンダをリセット 908 909 final int year = Integer.parseInt( dateStr.substring( 0,4 ) ); 910 final int month = Integer.parseInt( dateStr.substring( 4,6 ) ) - 1; 911 final int date = Integer.parseInt( dateStr.substring( 6,8 ) ); 912 913 // 6.3.9.0 (2015/11/06) Use one line for each declaration, it enhances code readability.(PMD) 914 int hour =0; 915 int minute=0; 916 int second=0; 917 if( dateStr.length() >= 14 ) { 918 hour = Integer.parseInt( dateStr.substring( 8,10 ) ); 919 minute = Integer.parseInt( dateStr.substring( 10,12 ) ); 920 second = Integer.parseInt( dateStr.substring( 12,14 ) ); 921 } 922 923 cal.set( year,month,date,hour,minute,second ); 924 925 return cal; 926 } 927 928 /** 929 * 指定の引数の日付け文字列(yyyyMMdd)より、日付を加算して返します。 930 * マイナスを与えると、減算します。 931 * 日付以上の精度の文字列を渡しても、日付のみの計算となります。 932 * 結果は、引数の日付フォーマットとは全く別で、yyyyMMdd の8文字形式になります。 933 * 引数に null を渡すと、実行時の日付をベースとして処理します。 934 * 935 * @og.rev 5.5.7.2 (2012/10/09) 新規作成 936 * 937 * @param baseDate 日付け文字列(yyyyMMdd) 938 * @param plus 加算する日数(過去にするにはマイナス値を指定する) 939 * 940 * @return 結果の日付(yyyyMMdd) 941 * @og.rtnNotNull 942 */ 943 public static String getDatePlus( final String baseDate,final int plus ) { 944 final Calendar cal = getCalendar( baseDate ); 945 cal.add( Calendar.DATE,plus ); 946 947 return DateSet.getDate( cal.getTimeInMillis() , "yyyyMMdd" ); 948 } 949 950 /** 951 * 現在の月に、指定の月数をプラスした日付文字列を返します。 952 * 日付文字列のフォーマットは、"yyyyMM" です。 953 * 指定する月数にマイナスを指定すると、減算できます。 954 * 955 * @og.rev 5.5.7.2 (2012/10/09) 新規作成 956 * 957 * @param baseDate 日付け文字列(yyyyMM) 958 * @param plus 加算する月数(過去にするにはマイナス値を指定する) 959 * 960 * @return 指定の月数をプラスした日付文字列(yyyyMM) 961 * @og.rtnNotNull 962 */ 963 public static String getMonthPlus( final String baseDate,final int plus ) { 964 final Calendar cal = getCalendar( baseDate ); 965 cal.set( Calendar.DATE, 1 ); // 当月の 1 日に設定 966 cal.add( Calendar.MONTH , plus ); 967 968 return DateSet.getDate( cal.getTimeInMillis() , "yyyyMM" ); 969 } 970 971 /** 972 * 指定の引数の日付け文字列(yyyyMMdd、yyyyMMddHHmmss)に、日付を加算して返します。 973 * マイナスを与えると、減算します。 974 * 975 * 指定する日付には、単位を付与することが可能です。 976 * 単位は、yyyyMMddHHmmss 形式の1文字を指定します。大文字、小文字も識別します。 977 * plus="5M" とすれば、5か月、plus="5d" とすれば、5日 追加します。 978 * plus に単位を付けない場合は、tani に指定の単位を使います。 979 * plus そのものが、null か、isEmpty の場合は、加算は、1 になります。 980 * 981 * baseDate 文字列を日付文字列に変換後、Calendar で計算し、結果を、format 形式に変換します。 982 * 引数に null を渡すと、実行時の日付をベースとして処理します。 983 * 984 * @og.rev 5.6.1.0 (2013/02/01) 新規作成 985 * 986 * @param baseDate 日付け文字列(yyyyMMdd、yyyyMMddHHmmss 形式の日付文字列) 987 * @param plus 加算する日数(日付単位を含む。単位は、y,M,d,H,m,s の文字で、大文字小文字の区別があります) 988 * @param defTani 日付単位が未指定の場合の初期単位('y','M','d','H','m','s' のどれか) 989 * @param format 返す日付文字列のフォーマット(yyyyMMdd、yyyyMMddHHmmss) 990 * 991 * @return 結果の日付(yyyyMMdd) 992 * @throws NumberFormatException 加算する日数の単位が('y','M','d','H','m','s')以外の場合。 993 * @og.rtnNotNull 994 */ 995 public static String getDatePlus( final String baseDate,final String plus,final int defTani,final String format ) { 996 997 int addSu = 1; // 初期値(plus が null や Empty の場合は、+1となる) 998 int tani = defTani; 999 1000 if( plus != null && !plus.isEmpty() ) { 1001 boolean flag = true; // 日付単位を持っているかどうか。持っている場合は、true 1002 final char ch = plus.charAt( plus.length()-1 ); // 最後の一文字を取得(単位か、数字本体) 1003 switch( ch ) { 1004 case 'y' : tani = Calendar.YEAR; break ; 1005 case 'M' : tani = Calendar.MONTH; break ; 1006 case 'd' : tani = Calendar.DATE; break ; 1007 case 'H' : tani = Calendar.HOUR_OF_DAY; break ; 1008 case 'm' : tani = Calendar.MINUTE; break ; 1009 case 's' : tani = Calendar.SECOND; break ; 1010 default : flag = false; break ; // 日付単位を持っていない。 1011 } 1012 if( flag ) { 1013 addSu = Integer.parseInt( plus.substring( 0,plus.length()-1 ) ); // 日付単位 あり 1014 } 1015 else { 1016 addSu = Integer.parseInt( plus ) ; // 日付単位 なし 1017 } 1018 } 1019 1020 final Calendar cal = getCalendar( baseDate ); 1021 cal.add( tani,addSu ); 1022 1023 return DateSet.getDate( cal.getTimeInMillis() , format ); 1024 } 1025 1026 /** 1027 * 指定の日付文字列を指定の形式の文字列に変換します。 1028 * 入力の日付文字列は、"yyyyMMddHHmmss" または、 "yyyyMMdd" です。 1029 * 記号が入っている場合でも、一応処理します。 1030 * 指定のフォーマットも、同様に、yyyyMMddHHmmss 形式で指定します。 1031 * 1032 * @og.rev 7.0.1.2 (2018/11/04) 新規登録 1033 * 1034 * @param ymd 日付け文字列(yyyyMM) 1035 * @param format 加算する月数(過去にするにはマイナス値を指定する) 1036 * 1037 * @return 指定の日付文字列を指定の形式の文字列に変換 1038 * @og.rtnNotNull 1039 */ 1040 public static String toYmd( final String ymd,final String format ) { 1041 final Calendar cal = getCalendar( ymd ); 1042 1043 return DateSet.getDate( cal.getTimeInMillis() , format ); 1044 } 1045 1046 /** 1047 * 指定の年月文字列のFrom-Toを、文字列の配列で返します。 1048 * "yyyyMM" 形式の範囲内の文字列配列を作成します。 1049 * 1050 * ※ endがnullの場合は、beginそのものを配列にセットして返します。 1051 * つまり、1件のみの配列になります。 1052 * ※ end == beginの場合は、配列は、ゼロ個の配列になります。 1053 * 1054 * @og.rev 7.0.5.0 (2019/09/09) 新規追加 1055 * 1056 * @param begin 開始の年月(含む) 1057 * @param end 終了の年月(含まない) 1058 * @param step 加算する月数 1059 * 1060 * @return 指定の年月文字列のFrom-Toを、文字列の配列で返す 1061 * @og.rtnNotNull 1062 */ 1063 public static String[] stepYM( final String begin,final String end,final int step ) { 1064 final List<String> list = new ArrayList<>(); 1065 1066 if( StringUtil.isNull( end ) ) { 1067 list.add( begin ); 1068 } 1069 else { 1070 final Calendar timeA = getCalendar( begin ); 1071 final Calendar timeB = getCalendar( end ); 1072 1073 while( timeA.before( timeB ) ) { 1074 list.add( DateSet.getDate( timeA.getTimeInMillis() , "yyyyMM" ) ); 1075 timeA.add( Calendar.MONTH, 1 ); 1076 } 1077 } 1078 1079 return list.toArray( new String[list.size()] ) ; 1080 } 1081}