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