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