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 java.util.Calendar;
019import java.util.Date;
020import java.util.Locale;
021import java.util.Map;
022import java.util.HashMap;
023import java.text.DateFormat;
024import java.text.SimpleDateFormat;
025import java.text.ParseException;
026
027/**
028 * HybsDateUtil.java は、共通的に使用される Date,Calender関連メソッドを集約した、staticメソッドのみで構成されるクラスです。
029 *
030 * @og.rev 5.5.7.2 (2012/10/09) 新規作成
031 *
032 * @og.group ユーティリティ
033 *
034 * @version  5.5
035 * @author       Kazuhiko Hasegawa
036 * @since    JDK7.0,
037 */
038public final class HybsDateUtil {
039
040        /** 各種フォーマットを簡易的に表した文字列 */
041        private static final Map<String,String> DATE_FORMAT = new HashMap<String,String>();
042        static {
043                DATE_FORMAT.put( "YMD"          ,"yyyyMMdd"                             );
044                DATE_FORMAT.put( "Y2MD"         ,"yyMMdd"                               );
045                DATE_FORMAT.put( "YM"           ,"yyyyMM"                               );
046                DATE_FORMAT.put( "MD"           ,"MMdd"                                 );      // 5.5.5.2 (2012/08/18)
047                DATE_FORMAT.put( "HMS"          ,"HHmmss"                               );
048                DATE_FORMAT.put( "YMDHMS"       ,"yyyyMMddHHmmss"               );
049                DATE_FORMAT.put( "EEE"          ,"EEE"                                  );
050                DATE_FORMAT.put( "YMDF"         ,"yyyy/MM/dd"                   );
051                DATE_FORMAT.put( "Y2MDF"        ,"yy/MM/dd"                     );
052                DATE_FORMAT.put( "YMF"          ,"yyyy/MM"                              );
053                DATE_FORMAT.put( "HMSF"         ,"HH:mm:ss"                     );
054                DATE_FORMAT.put( "YMDHMSF"      ,"yyyy/MM/dd HH:mm:ss"  );
055                DATE_FORMAT.put( "MDF"          ,"MM/dd"                                ); // 5.5.0.2 (2012/03/09) 和暦
056                DATE_FORMAT.put( "MDEF"         ,"MM/dd(EEE)"                   ); // 5.5.0.2 (2012/03/09) 和暦
057                DATE_FORMAT.put( "MD2F"         ,"MM月dd日"                               ); //                                                           5.5.5.2 (2012/08/18) 和暦
058                DATE_FORMAT.put( "GYMDF"        ,"GGGGyyyy年MM月dd日"      ); // 5.5.0.2 (2012/03/09) 和暦
059                DATE_FORMAT.put( "G2YMDF"       ,"Gyyyy/MM/dd"                  ); // 5.5.0.2 (2012/03/09) 和暦
060                DATE_FORMAT.put( "GYMF"         ,"GGGGyyyy年MM月"         ); // 5.5.0.2 (2012/03/09) 和暦
061                DATE_FORMAT.put( "GYF"          ,"GGGGyyyy"                     ); // 5.5.0.2 (2012/03/09) 和暦
062        }
063
064        /**
065         *      デフォルトコンストラクターをprivateにして、
066         *      オブジェクトの生成をさせないようにする。
067         *
068         * @og.rev 5.5.7.2 (2012/10/09) 新規作成
069         *
070         */
071        private HybsDateUtil() {}
072
073        /**
074         * 現在日付、時刻を指定のフォーマットで文字列に変換して返します。
075         * 出力フォーマットは、"yyyy/MM/dd HH:mm:ss" 固定です。
076         *
077         * @og.rev 5.5.7.2 (2012/10/09) 新規作成
078         *
079         * @return      現在日付、時刻 ( 例 2012/09/05 18:10:24 )
080         */
081        public static String getDate() {
082                DateFormat formatter = new SimpleDateFormat( "yyyy/MM/dd HH:mm:ss",Locale.JAPAN );
083                return formatter.format(new Date());
084        }
085
086        /**
087         * 現在時刻を指定のフォーマットで文字列に変換して返します。
088         * フォーマットの指定方法は、java.text.SimpleDateFormat の指定方法と同一です。
089         * 変換時のロケーションは、Locale.JAPAN です。
090         * 現在時刻は、new Date() で求めます。
091         *
092         * @param       form フォーム文字列 ( 例 "yyyy/MM/dd HH:mm:ss.SSS" )
093         *
094         * @og.rev 5.5.7.2 (2012/10/09) 新規作成
095         *
096         * @return      現在日付、時刻
097         * @see         java.text.SimpleDateFormat
098         */
099        public static String getDate( final String form ) {
100                DateFormat formatter = new SimpleDateFormat( form,Locale.JAPAN );
101                return formatter.format( new Date() );
102        }
103
104        /**
105         * 指定時刻を指定のフォーマットで文字列に変換して返します。
106         * フォーマットの指定方法は、java.text.SimpleDateFormat の指定方法と同一です。
107         * 変換時のロケーションは、Locale.JAPAN です。
108         * 指定時刻は、new Date( time ) で求めます。
109         *
110         * @param       time 指定のカレントタイムのロング値
111         * @param       form フォーム文字列 ( 例 "yyyy/MM/dd HH:mm:ss.SSS" )
112         *
113         * @og.rev 5.5.7.2 (2012/10/09) 新規作成
114         *
115         * @return      現在日付、時刻( 例 2001/04/17 15:48:22 )
116         */
117        public static String getDate( final long time,final String form ) {
118                DateFormat formatter = new SimpleDateFormat( form,Locale.JAPAN );
119                return formatter.format( new Date( time ) );
120        }
121
122        /**
123         * 指定の文字列から、以下の文字を削除した文字列を返します。
124         * '/' , '-' , ' ' , ':' の数字以外の文字を含むフォーマットされた
125         * 日付文字列を、日付データだけに変換する場合に利用することを想定しています。
126         * よって、マイナス記号や、小数点、コンマなども削除されます。
127         * このメソッドでは、日付としての整合性や桁チェックは行いません。
128         * 
129         * 引数が、null の場合は、ゼロ文字列に、変換します。
130         *
131         * @og.rev 5.5.7.2 (2012/10/09) 新規作成
132         * @og.rev 5.5.8.3 (2012/11/17) 数字のみ返す仕様だったが、対象以外の文字入力はそのまま返すよう変更
133         *
134         * @param       value 任意の文字列(例:2001/04/17 15:48:22)
135         *
136         * @return      数字だけで構成される文字列(例:20010417154822)(nullはゼロ文字列を返します)
137         */
138        public static String parseNumber( final String value ) {
139                if( value == null ) { return ""; }
140
141                StringBuilder buf = new StringBuilder();
142                for( int i=0; i<value.length(); i++ ) {
143                        char ch = value.charAt(i);
144                        if( ch == '/' || ch == '-' || ch == ' ' || ch == ':'){} // 5.5.8.3 (2012/11/17) 何もしない
145                        else {
146                                buf.append( ch );
147                        }
148                }
149
150                return buf.toString();
151        }
152
153        /**
154         * 指定の文字列から、yyyy-mm-dd hh:mm:ss[.f...] 形式の文字列を作成します。
155         * これは、java.sql.Timestamp オブジェクトを文字列から作成するに当たり、
156         * Timestamp の文字列形式にしなければならないためです。
157         * 桁数は、8桁 または、14桁以外の場合は、変換エラーとします。
158         *
159         * @og.rev 5.5.8.5 (2012/11/27) 新規作成
160         *
161         * @param       value 任意の文字列(例:20010417 or 20010417154822)
162         *
163         * @return      Timestampの文字列形式(例:2001-04-17 00:00:00 or 2001-04-17 15:48:22)
164         */
165        public static String parseTimestamp( final String value ) {
166                if( value == null || ( value.length() != 8 && value.length() != 14 ) ) {
167                        String errMsg = "日付文字列に、不正な値が指定されました。8桁 または、14桁で指定してください。"
168                                                + " value=[" + value + "]" ;
169                        throw new RuntimeException( errMsg );
170                }
171
172                StringBuilder buf = new StringBuilder();
173                buf.append( value.substring( 0,4 ) ).append( "-" );
174                buf.append( value.substring( 4,6 ) ).append( "-" );
175                buf.append( value.substring( 6,8 ) ).append( " " );
176                if( value.length() == 8 ) {
177                        buf.append( "00:00:00" );
178                }
179                else {
180                        buf.append( value.substring( 8,10  ) ).append( ":" );
181                        buf.append( value.substring( 10,12 ) ).append( ":" );
182                        buf.append( value.substring( 12,14 ) );
183                }
184
185                return buf.toString();
186        }
187
188        /**
189         * 日付文字列の桁数の整合性を取ります。
190         * これは、内部で、parseNumber(String) 処理により、不要なフォーマット記号を削除します。
191         * ここでは、基本的には、6文字(yyyyMM)、8文字(yyyyMMdd)、14文字(yyyyMMddHHmmss)
192         * の日付文字列を作成することを想定していますが、指定の桁数以外は、エラーになります。
193         * 
194         * 引数が、null         ⇒ 桁数に無関係に、空文字列を返す。
195         * 引数の桁数が一致     ⇒ その値を返す。
196         * 引数の桁数が不一致   ⇒ エラー
197         * ただし、引数の最大長は、14ケタに制限しています。
198         * 
199         * このメソッドでは、日付として成立しているかどうか(99999999など)は判定していません。
200         *
201         * @og.rev 5.6.6.0 (2013/07/05) メソッドの内容を移す。
202         *
203         * @param       value   任意の日付け文字列
204         * @param       size    変換したい桁数
205         *
206         * @return      数字だけで構成される文字列(例:20010417154822)(nullはゼロ文字列を返します)
207         */
208        public static String parseDate( final String value , final int size ) {
209                return parseDate( value , size , size );                // 最小と最大を同じ値にする。
210        }
211
212        /**
213         * 日付文字列の桁数の整合性を取ります。
214         * これは、内部で、parseNumber(String) 処理により、不要なフォーマット記号を削除します。
215         * ここでは、基本的には、6文字(yyyyMM)、8文字(yyyyMMdd)、14文字(yyyyMMddHHmmss)
216         * の日付文字列を作成することを想定していますが、それ以外の桁数でも下記のルールに従って
217         * 処理されます。
218         * 
219         * 引数が、null         ⇒ 桁数に無関係に、空文字列を返す。
220         * 引数の桁数が範囲内   ⇒ 以下の処理を実行する。
221         * 引数の桁数を同じ     ⇒ そのまま返す。
222         * 引数の桁数より大きい ⇒ 余をカットして、引数の最大長にそろえる。
223         * 引数の桁数に足りない ⇒ "20000101000000" の文字列の部分文字列を結合させて、引数の最大長にそろえる。
224         * ただし、引数の最大長は、14ケタに制限しています。
225         * 
226         * このメソッドでは、日付として成立しているかどうか(99999999など)は判定していません。
227         *
228         * @og.rev 5.5.7.2 (2012/10/09) 新規作成
229         * @og.rev 5.6.1.1 (2013/02/08) 桁数チェック導入。6桁以下だとエラーにする。
230         * @og.rev 5.6.6.0 (2013/07/05) 桁数チェックの最小-最大指定
231         *
232         * @param       value   任意の日付け文字列
233         * @param       minSize 変換したい桁数の最小値
234         * @param       maxSize 変換したい桁数の最大値
235         *
236         * @return      数字だけで構成される文字列(例:20010417154822)(nullはゼロ文字列を返します)
237         */
238        public static String parseDate( final String value , final int minSize , final int maxSize ) {
239                if( value == null ) { return ""; }
240
241                String rtn = parseNumber( value );
242
243                // 引数の最大長は、14ケタに制限しています。
244                if( maxSize > 14 ) {
245                        String errMsg = "日付登録に許可できる最大桁数は、14ケタです。"
246                                                + " maxSize=[" + maxSize + "]" ;
247                        throw new RuntimeException( errMsg );
248                }
249
250                int len     = rtn.length() ;
251
252                if(      len == maxSize ) { rtn = value; }
253                // 5.6.1.1 (2013/02/08) 桁数チェック導入。6桁以下だとエラーにする。
254                // 5.6.6.0 (2013/07/05) 桁数チェックの最小-最大指定で、範囲外はエラー
255                else if( len < minSize || len > maxSize ) {
256                        String errMsg = "日付文字列に、不正な値が指定されました。最小["
257                                                + minSize + "] から、最大[" + maxSize + "]の範囲で指定してください。"
258                                                + " value=[" + value + "]" ;
259                        throw new RuntimeException( errMsg );
260                }
261                else {
262                        rtn = rtn + "20000101000000".substring( len,maxSize ) ; // 中間文字列を加える。
263                }
264
265                return rtn ;
266        }
267
268        /**
269         * 日付文字列の厳密な整合性チェックを行います。
270         * ここで指定できるのは、8文字(yyyyMMdd)、14文字(yyyyMMddHHmmss)のどちらかの
271         * 数字だけの日付文字列であり、それが、日付として正しいかどうかのチェックを行います。
272         * 正しければ、true を、間違っていれば、false を返します。
273         * ここでは、20120230(2月30日)などの日付や、20120101235960 なども false になります。
274         * 引数が、null および、空文字列の場合も、false を返しますので、避けたい場合は、事前に
275         * 判定しておいてください。
276         *
277         * 内部処理としては、DateFormat で、setLenient( false ) を設定することで、
278         * 日付/時刻解析を厳密に解析するにして、ParseException が発生しないかどうか判定しています。
279         *
280         * @og.rev 5.5.7.2 (2012/10/09) 新規作成
281         *
282         * @param       value  数字だけで構成される日付け文字列
283         *
284         * @return      true:日付として正しい場合/false:日付として間違っている場合
285         */
286        public static boolean isStrict( final String value ) {
287                if( value == null || ( value.length() != 8 && value.length() != 14 ) ) { return false; }
288
289                // 日付の厳密なチェック
290                String form = (value.length() == 8) ? "yyyyMMdd" : "yyyyMMddHHmmss" ;
291                DateFormat formatter = new SimpleDateFormat( form,Locale.JAPAN );
292                formatter.setLenient( false );          // 日付/時刻解析を厳密に行う(false=厳密)
293
294                boolean flag ;
295                try {
296                        formatter.parse( value );
297                        flag = true;
298                }
299                catch( ParseException ex ) {
300                        flag = false;
301                }
302
303                return flag;
304        }
305
306        /**
307         * 日付関係の情報を簡易的に処理します。
308         *
309         * CC引数の加減算パラメータは、0 です。
310         *
311         * @og.rev 5.7.4.1 (2014/03/14) CC 引数を拡張するため、旧メソッドを再現しておきます。
312         *
313         * @param   key         フォーマットの予約語
314         * @param   prmA        基準となる日付(nullの場合は、処理時刻)
315         * @param   prmB        処理コマンド
316         *
317         * @return   メッセージ情報
318         * @see         #getDateFormat( String , String ,String , int )
319         */
320        public static String getDateFormat( final String key ,final String prmA ,final String prmB ) {
321                return getDateFormat( key,prmA,prmB,0 );
322        }
323
324        /**
325         * 日付関係の情報を簡易的に処理します。
326         *
327         * 第一引数 "XXXX" は、日付処理を行うフォーマットの予約語になっています。
328         * ・YMD  :8文字の4-2-2年月日データ(yyyyMMdd)を扱います。
329         * ・Y2MD  :6文字の2-2-2年月日データ(yyMMdd)を扱います。
330         * ・YM   :6文字の4-2年月データ(yyyyMM)を扱います。
331         * ・HMS  :6文字の2-2-2時分秒データ(HHmmss)を扱います。
332         * ・YMDHMS :14文字の4-2-2-2-2-2年月日時分秒データ(yyyyMMddHHmmss)を扱います。
333         * ・EEE  :曜日をデフォルトロケールで表示します。
334         *
335         * F付きは、フォーマットされた日付を返します。
336         * ・YMDF  :10文字の日付表現(yyyy/MM/dd)を扱います。
337         * ・Y2MDF :8文字の日付表現(yy/MM/dd)を扱います。
338         * ・YMF  :7文字の日付表現(yyyy/MM)を扱います。
339         * ・HMSF  :8文字の時刻表現(HH:mm:ss)を扱います。
340         * ・YMDHMSF:19文字の日付表現(yyyy/MM/dd HH:mm:ss)を扱います。
341         * ・MDF  :5文字の月日表現(MM/dd)を扱います。
342         * ・MDEF  :5文字+曜日の月日表現(MM/dd(EEE))を扱います。
343         * ・MD2F  :和暦の月日表現(MM月dd日)を扱います。(5.5.5.2 追加)
344         * ・GYMDF :和暦の年月日表現(GGGGyyyy年MM月dd日)を扱います。
345         * ・G2YMDF :和暦の日付表現(Gyyyy/MM/dd)を扱います。
346         * ・GYMF  :和暦の年月表現(GGGGyyyy年MM月)を扱います。
347         * ・GYF  :和暦の年表現(GGGGyyyy)を扱います。
348         *
349         * なお、上記以外のフォーマットを指定する場合は、XXXX部分に直接記述できます。(5.5.5.2 追加)
350         * ただし、基本的には、自由フォーマットは、エラーチェックがない為、使わないでください。
351         *
352         * 第二引数 AA は、基準となる日付を、yyyyMMdd形式で指定します。nullの場合は、現在時刻を使用します。
353         * 指定できる日付は、yyyyMMdd形式を推奨しますが、'/' , '-' , ' ' , ':' を削除して使います。
354         * 6桁の場合は、yyyyMM + 01 とし、8ケタの場合は、yyyyMMdd とし、14ケタ以上の場合は、前半14文字を
355         * yyyyMMddHHmmss として処理します。それ以外の桁数の場合は、エラーになります。
356         * たとえば、"2012/09/05 16:52:36" のようなフォーマットデータの場合、'/' , '-' , ' ' , ':' を削除して
357         * "20120905165236" に変換後、日付オブジェクトに変換されます。
358         *
359         * 第三引数 BB は、日付についての加減算処理を行うためのコマンドを指定します。
360         * nullの場合は、なにも加減算処理を行いません。
361         * ・SD :当月の最初の日付にセットします。(当月1日)。CC引数は、-N:N月前、0:当月(=SD)、N:N月後、-1:BSD と同じ、1:ASD と同じ
362         * ・ED :当月の最後の日付にセットします。(当月月末)。CC引数は、-N:N月前、0:当月(=ED)、N:N月後、-1:BED と同じ、1:AED と同じ
363         * ・SW :日付処理の週初め(月曜日)にセットします。日付は当日より前に移動します。CC引数は、-N:N週前、0:今週(=SW)、N:N週後
364         * ・EW :日付処理の週末(日曜日)にセットします。日付は当日より後ろに移動します。CC引数は、-N:N週前、0:今週(=EW)、N:N週後
365         * ・H1 ~ HXXX :時を指定の分だけ進めます。H1なら1時間後、H24 なら24時間後(5.5.5.6 (2012/08/31) 追加)
366         * ・D1 ~ DXXX :日を指定の分だけ進めます。D1なら翌日、D200 なら200日後
367         * ・M1 ~ MXXX :月を指定の分だけ進めます。M1なら翌月、M6 なら半年後
368         * ・BSD :(有閑)先月の最初の日付にセットします。(先月1日)(5.5.5.2 追加)。SD -1 と同等
369         * ・BED :(有閑)先月の最後の日付にセットします。(先月月末)(5.5.5.2 追加)。ED -1 と同等
370         * ・ASD :(有閑)翌月の最初の日付にセットします。(翌月1日)(5.5.5.2 追加)。SD 1  と同等
371         * ・AED :(有閑)翌月の最後の日付にセットします。(翌月月末)(5.5.5.2 追加)。ED 1  と同等
372         *
373         * CC 引数は、特別な処理で、BB 引数に対して、加算、減算のための数字を指定できます。(5.7.4.1 (2014/03/14) 追加)
374         * 従来は、BB 引数が、"H" , "D" , "M" の 1文字パラメータの場合のみ利用可能でした。
375         *
376         * @og.rev 5.5.7.2 (2012/10/09) 新規作成
377         * @og.rev 5.6.1.1 (2013/02/08) prmB処理を、calendarCalc メソッドへ移動
378         * @og.rev 5.7.4.1 (2014/03/14) CC 引数を拡張
379         *
380         * @param   key         フォーマットの予約語
381         * @param   prmA        基準となる日付(nullの場合は、処理時刻)
382         * @param   prmB        処理コマンド
383         * @param   intC        加減算処理を行うための数字。0 は、BB引数の従来計算のまま。
384         *
385         * @return   メッセージ情報
386         * @see         #getDateFormat( String , String ,String )
387         * @see         #getCalendar( String )                                          AA 引数 からカレンダオブジェクトを作成します。
388         * @see         #calendarCalc( Calendar , String , int )        BB 引数、CC 引数を元に、日付計算します。
389         */
390        public static String getDateFormat( final String key ,final String prmA ,final String prmB ,final int intC ) {
391
392                // prmA が null の場合は、そのまま、現在時刻が使われます。
393                Calendar now = getCalendar( prmA );
394
395                // 5.6.1.1 (2013/02/08) getDateFormat( String ,String ,String ) から分離。
396                calendarCalc( now,prmB,intC );          // 5.7.4.1 (2014/03/14) CC 引数を拡張
397
398                String format = DATE_FORMAT.get( key );
399                if( format == null ) {
400                        // DATE_FORMAT に存在しないフォーマットを指定しても、エラーにしません。
401                        // ただし、後処理でフォーマットエラーになる可能性は残ります。
402                        format = key;   // 5.5.5.2 (2012/08/18) 自由フォーマット指定
403                }
404
405                //5.5.0.2 先頭Gの場合は和暦なのでformatterのLocaleを変更する
406                DateFormat formatter = null;
407                if( key.indexOf('G') == 0 ){
408                        formatter = new SimpleDateFormat( format, new Locale("ja","JP","JP"));
409                }
410                else{
411                        formatter = new SimpleDateFormat( format,Locale.JAPAN );
412                }
413
414                return formatter.format( now.getTime() );
415        }
416
417        /**
418         * 開始前設定値、または 終了後設定値の文字列から、オプション文字列を合成します。
419         * 基準となる日付に計算した結果を反映させます。
420         *
421         * CC引数の加減算パラメータは、0 です。
422         *
423         * @og.rev 5.7.4.1 (2014/03/14) CC 引数を拡張するため、旧メソッドを再現しておきます。
424         *
425         * @param   now     基準となる日付(Calendarオブジェクト)
426         * @param   prmB        処理コマンド
427         */
428        public static void calendarCalc( final Calendar now,final String prmB ) {
429                calendarCalc( now,prmB,0 );
430        }
431
432        /**
433         * 開始前設定値、または 終了後設定値の文字列から、オプション文字列を合成します。
434         * 基準となる日付に計算した結果を反映させます。
435         *
436         * prmB は、日付についての加減算処理を行うためのコマンドを指定します。
437         * ・SD :当月の最初の日付にセットします。(当月1日)。CC引数は、-N:N月前、0:当月(=SD)、N:N月後、-1:BSD と同じ、1:ASD と同じ
438         * ・ED :当月の最後の日付にセットします。(当月月末)。CC引数は、-N:N月前、0:当月(=ED)、N:N月後、-1:BED と同じ、1:AED と同じ
439         * ・SW :日付処理の週初め(月曜日)にセットします。日付は当日より前に移動します。CC引数は、-N:N週前、0:今週(=SW)、N:N週後
440         * ・EW :日付処理の週末(日曜日)にセットします。日付は当日より後ろに移動します。CC引数は、-N:N週前、0:今週(=EW)、N:N週後
441         * ・H1 ~ HXXX :時を指定の分だけ進めます。H1なら1時間後、H24 なら24時間後(5.5.5.6 (2012/08/31) 追加)
442         * ・D1 ~ DXXX :日を指定の分だけ進めます。D1なら翌日、D200 なら200日後
443         * ・M1 ~ MXXX :月を指定の分だけ進めます。M1なら翌月、M6 なら半年後
444         * ・BSD :先月の最初の日付にセットします。(先月1日)(5.5.5.2 追加)。SD-1 と同等
445         * ・BED :先月の最後の日付にセットします。(先月月末)(5.5.5.2 追加)。ED-1 と同等
446         * ・ASD :翌月の最初の日付にセットします。(翌月1日)(5.5.5.2 追加)。SD1  と同等
447         * ・AED :翌月の最後の日付にセットします。(翌月月末)(5.5.5.2 追加)。ED1  と同等
448         * ・数字:日を指定の分だけ進めます。D1 ~ DXXX の簡略系
449         *
450         * CC 引数は、特別な処理で、BB 引数に対して、加算、減算のための数字を指定できます。(5.7.4.1 (2014/03/14) 追加)
451         * HXXX,DXXX,MXXX 形式に、CC 引数を付けた場合は、XXX にさらに加算されます。
452         * prmB に、数字を使用した場合、(コマンドでない場合)にも、CC 引数は、加算されます。
453         *
454         * @og.rev 5.6.1.1 (2013/02/08) getDateFormat( String ,String ,String ) から分離。
455         * @og.rev 5.7.4.1 (2014/03/14) H1 ~ HXXX :時を指定の分だけ進める処理が実装されていなかった。
456         * @og.rev 5.7.4.1 (2014/03/14) CC 引数追加
457         *
458         * @param   now     基準となる日付(Calendarオブジェクト)
459         * @param   prmB        処理コマンド
460         * @param   intC        加減算処理を行うための数字。0 は、BB引数の従来計算のまま。
461         */
462        public static void calendarCalc( final Calendar now , final String prmB , final int intC ) {
463
464                // 基準は、intC == 0 の場合
465                if( prmB != null ) {
466                        if( "SD".equals( prmB ) ) {                                                     // (当月1日)
467                                if( intC != 0 ) { now.add( Calendar.MONTH,intC ); }     // 5.7.4.1 (2014/03/14) CC 引数追加
468                                now.set( Calendar.DATE,1 );
469                        }
470                        else if( "ED".equals( prmB ) ) {                                        // (当月月末)
471                                if( intC != 0 ) { now.add( Calendar.MONTH,intC ); }     // 5.7.4.1 (2014/03/14) CC 引数追加
472                                now.set( Calendar.DATE,now.getActualMaximum( Calendar.DATE ) );
473                        }
474                        else if( "BSD".equals( prmB ) ) {                                       // (先月1日)
475                                // 5.7.4.1 (2014/03/14) CC 引数追加
476                                now.add( Calendar.MONTH,intC-1 ); now.set( Calendar.DATE,1 );
477                        }
478                        else if( "BED".equals( prmB ) ) {                                       // (先月月末)
479                                // 5.7.4.1 (2014/03/14) CC 引数追加
480                                now.add( Calendar.MONTH,intC-1 ); now.set( Calendar.DATE,now.getActualMaximum( Calendar.DATE ) );
481                        }
482                        else if( "ASD".equals( prmB ) ) {                                       // (翌月1日)
483                                // 5.7.4.1 (2014/03/14) CC 引数追加
484                                now.add( Calendar.MONTH,intC+1 ); now.set( Calendar.DATE,1 );
485                        }
486                        else if( "AED".equals( prmB ) ) {                                       // (翌月月末)
487                                // 5.7.4.1 (2014/03/14) CC 引数追加
488                                now.add( Calendar.MONTH,intC+1 ); now.set( Calendar.DATE,now.getActualMaximum( Calendar.DATE ) );
489                        }
490                        else if( "SW".equals( prmB ) ) {                                        // 週初め(月曜日)セット
491                                // 5.7.4.1 (2014/03/14) CC 引数追加
492                                if( intC != 0 ) { now.add( Calendar.DATE,intC*7 ); }    // まず、基準の日付を週単位で加減算する。
493
494                                // 日付型文字列入力データの開始日を月曜日にセットします。
495                                // SUNDAY=1 , MONDAY=2 になります。月曜日との差だけ、前に戻します。
496                                // 指定日が日曜日の場合は、月曜日まで戻します。
497
498                                int shu = now.get( Calendar.DAY_OF_WEEK ) - Calendar.MONDAY ;
499
500                                if(      shu > 0 ) { now.add( Calendar.DATE, -shu ); }
501                                else if( shu < 0 ) { now.add( Calendar.DATE, -6 );   }
502                        }
503                        else if( "EW".equals( prmB ) ) {                                        // 週末(日曜日)にセット
504                                // 5.7.4.1 (2014/03/14) CC 引数追加
505                                if( intC != 0 ) { now.add( Calendar.DATE,intC*7 ); }    // まず、基準の日付を週単位で加減算する。
506
507                                // 日付型文字列入力データの終了日を日曜日にセットします。
508                                // SUNDAY=1 , MONDAY=2 になります。日曜日になるように、先に進めます。
509                                int shu = now.get( Calendar.DAY_OF_WEEK ) ;
510                                if( shu != Calendar.SUNDAY ) { now.add( Calendar.DATE, 8-shu ); }
511                        }
512                        // 5.7.4.1 (2014/03/14) H1 ~ HXXX :時を指定の分だけ進める処理が実装されていなかった。
513                        else if( prmB.startsWith( "H" ) ) {
514                                int hour = intC ;
515                                if( prmB.length() > 1 ) { hour += Integer.parseInt( prmB.substring( 1 ) ); }
516                                now.add( Calendar.HOUR_OF_DAY , hour );
517                        }
518                        else if( prmB.startsWith( "D" ) ) {
519                                int day = intC ;
520                                if( prmB.length() > 1 ) { day += Integer.parseInt( prmB.substring( 1 ) ); }
521                                now.add( Calendar.DATE, day );
522                        }
523                        else if( prmB.startsWith( "M" ) ) {
524                                int month = intC ;
525                                if( prmB.length() > 1 ) { month += Integer.parseInt( prmB.substring( 1 ) ); }
526                                now.add( Calendar.MONTH , month );
527                        }
528                        else {
529                                // 上記のパターン以外は、数字(加減算する日数)なので、変換できなければ、フォーマットエラー
530                                try {
531                                        int day = Integer.parseInt( prmB ) + intC ;     // 5.7.4.1 (2014/03/14) CC 引数追加
532                                        now.add( Calendar.DATE, day );
533                                }
534                                catch( NumberFormatException ex ) {
535                                        String errMsg = "日付変数パラメータに、不正な値が指定されました。以下の中から指定しなおしてください。"
536                                                                + "指定可能:[SD,ED,BSD,BED,ASD,AED,SW,EW,H1~HXXX,D1~DXXX,M1~MXXX]"
537                                                                + " prmB=[" + prmB + "]" ;
538                                        throw new RuntimeException( errMsg,ex );
539                                }
540                        }
541                }
542        }
543
544        /**
545         * 指定の引数の日付け文字列より、カレンダオブジェクトを作成します。
546         * 引数は、数字以外の文字を削除した状態に変換後、処理に回します。
547         * 不要な文字を削除した状態で、8文字以上になるように指定してください。
548         * 例外的に、6文字の場合は、yyyyMM01 とみなして、"01" 文字列を付与します。
549         * 引数に null を指定すると、現在時刻のカレンダを返します。
550         * それ以外のデータで、8ケタ以下の場合は、RuntimeException が発生します。
551         * 8ケタ以上14ケタ未満の場合は、8ケタ分を、年月日に分離したカレンダ
552         * オブジェクトを作成します。14ケタ以上で初めて、時分秒を含むカレンダ
553         * を作成します。
554         *
555         * @og.rev 5.5.7.2 (2012/10/09) 新規作成
556         * @og.rev 5.5.8.2 (2012/11/09) value の判定に、null と ゼロ文字列を判定する。
557         *
558         * @param value 日付け文字列
559         *
560         * @return      カレンダオブジェクト(引数がnullの場合は、現在時刻)
561         */
562        public static Calendar getCalendar( final String value ) {
563                Calendar cal = Calendar.getInstance();
564
565                if( value == null || value.isEmpty() ) { return cal; }          // 5.5.8.2 (2012/11/09) null と ゼロ文字列を判定する。
566
567                // 日付表記に不要な文字を削除します。
568                String dateStr = parseNumber( value ) ;
569
570                if( dateStr.length() == 6 ) { dateStr = dateStr + "01"; }       // yyyyMM01 形式に無理やり合わせる。
571                else if( dateStr.length() < 8 ) {
572                        String errMsg = "日付指定パラメータに、不正な値が指定されました。value=[" + value + "]" ;
573                        throw new RuntimeException( errMsg );
574                }
575
576                cal.clear();    // 日付文字列が存在するので、カレンダをリセット
577
578                int year   = Integer.parseInt( dateStr.substring( 0,4 ) );
579                int month  = Integer.parseInt( dateStr.substring( 4,6 ) ) - 1;
580                int date   = Integer.parseInt( dateStr.substring( 6,8 ) );
581
582                int hour=0, minute=0, second=0;
583                if( dateStr.length() >= 14 ) {
584                        hour   = Integer.parseInt( dateStr.substring( 8,10 ) );
585                        minute = Integer.parseInt( dateStr.substring( 10,12 ) );
586                        second = Integer.parseInt( dateStr.substring( 12,14 ) );
587                }
588
589                cal.set( year,month,date,hour,minute,second );
590
591                return cal;
592        }
593
594        /**
595         * 指定の引数の日付け文字列(yyyyMMdd)より、日付を加算して返します。
596         * マイナスを与えると、減算します。
597         * 日付以上の精度の文字列を渡しても、日付のみの計算となります。
598         * 結果は、引数の日付フォーマットとは全く別で、yyyyMMdd の8文字形式になります。
599         * 引数に null を渡すと、実行時の日付をベースとして処理します。
600         *
601         * @og.rev 5.5.7.2 (2012/10/09) 新規作成
602         *
603         * @param baseDate 日付け文字列(yyyyMMdd)
604         * @param plus     加算する日数(過去にするにはマイナス値を指定する)
605         *
606         * @return      結果の日付(yyyyMMdd)
607         */
608        public static String getDatePlus( final String baseDate,final int plus ) {
609                Calendar cal = getCalendar( baseDate );
610                cal.add( Calendar.DATE,plus );
611
612                return getDate( cal.getTimeInMillis() , "yyyyMMdd" );
613        }
614
615        /**
616         * 現在の月に、指定の月数をプラスした日付文字列を返します。
617         * 日付文字列のフォーマットは、"yyyyMM" です。
618         * 指定する月数にマイナスを指定すると、減算できます。
619         *
620         * @og.rev 5.5.7.2 (2012/10/09) 新規作成
621         *
622         * @param baseDate 日付け文字列(yyyyMM)
623         * @param plus     加算する月数(過去にするにはマイナス値を指定する)
624         *
625         * @return      指定の月数をプラスした日付文字列(yyyyMM)
626         */
627        public static String getMonthPlus( final String baseDate,final int plus ) {
628                Calendar cal = getCalendar( baseDate );
629                cal.set( Calendar.DATE, 1 );            // 当月の 1 日に設定
630                cal.add( Calendar.MONTH , plus );
631
632                return getDate( cal.getTimeInMillis() , "yyyyMM" );
633        }
634
635        /**
636         * 指定の引数の日付け文字列(yyyyMMdd、yyyyMMddHHmmss)に、日付を加算して返します。
637         * マイナスを与えると、減算します。
638         *
639         * 指定する日付には、単位を付与することが可能です。
640         * 単位は、yyyyMMddHHmmss 形式の1文字を指定します。大文字、小文字も識別します。
641         * plus="5M" とすれば、5か月、plus="5d"  とすれば、5日 追加します。
642         * plus に単位を付けない場合は、tani に指定の単位を使います。
643         * plus そのものが、null か、isEmpty の場合は、加算は、1 になります。
644         *
645         * baseDate 文字列を日付文字列に変換後、Calendar で計算し、結果を、format 形式に変換します。
646         * 引数に null を渡すと、実行時の日付をベースとして処理します。
647         *
648         * @og.rev 5.6.1.0 (2013/02/01) 新規作成
649         *
650         * @param baseDate 日付け文字列(yyyyMMdd、yyyyMMddHHmmss 形式の日付文字列)
651         * @param plus     加算する日数(日付単位を含む。単位は、y,M,d,H,m,s の文字で、大文字小文字の区別があります)
652         * @param defTani  日付単位が未指定の場合の初期単位('y','M','d','H','m','s' のどれか)
653         * @param format   返す日付文字列のフォーマット(yyyyMMdd、yyyyMMddHHmmss)
654         *
655         * @return      結果の日付(yyyyMMdd)
656         * @throws      NumberFormatException 加算する日数の単位が('y','M','d','H','m','s')以外の場合。
657         */
658        public static String getDatePlus( final String baseDate,final String plus,final int defTani,final String format ) {
659
660                int addSu = 1;                          // 初期値(plus が null や Empty の場合は、+1となる)
661                int tani  = defTani;
662
663                if( plus != null && !plus.isEmpty() ) {
664                        boolean flag = true;    // 日付単位を持っているかどうか。持っている場合は、true
665                        char ch = plus.charAt( plus.length()-1 );               // 最後の一文字を取得(単位か、数字本体)
666                        switch( ch ) {
667                                case 'y' : tani = Calendar.YEAR;                break ;
668                                case 'M' : tani = Calendar.MONTH;               break ;
669                                case 'd' : tani = Calendar.DATE;                break ;
670                                case 'H' : tani = Calendar.HOUR_OF_DAY; break ;
671                                case 'm' : tani = Calendar.MINUTE;              break ;
672                                case 's' : tani = Calendar.SECOND;              break ;
673                                default  : flag = false;        break ;         // 日付単位を持っていない。
674                        }
675                        if( flag ) {
676                                addSu = Integer.parseInt( plus.substring( 0,plus.length()-1 ) );        // 日付単位 あり
677                        }
678                        else {
679                                addSu = Integer.parseInt( plus ) ;                                                                      // 日付単位 なし
680                        }
681                }
682
683                Calendar cal = getCalendar( baseDate );
684                cal.add( tani,addSu );
685
686                return getDate( cal.getTimeInMillis() , format );
687        }
688}