    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.system;                                                            // 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system
    017
    018import java.io.FileInputStream;
    019import java.io.FileOutputStream;
    020import java.util.Date;
    021import java.util.Locale;
    022import java.text.DateFormat;
    023import java.text.SimpleDateFormat;
    024
    025import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.1.0.0 (2014/12/26) refactoring
    026
    027/**
    028 * DateSet.java は、入力ファイルの日付，時刻キーワードを実行時の日時で変換して,出力します。
    029 *
    030 * 変換には,$(yyyy)の形式で指定し,カッコの文字列は,java.text.SimpleDateFormat で使用する，
    031 * 時刻フォーマット構文を用います。
    032 * また、引数に keys,vals を渡すことで、$(KEY1) 文字列を VAL1 文字列と置き換えます。
    033 *
    034 *  サンプルファイル
    035 *  $(yyyy/MM/dd)        年／月／日を表します。
    036 *  $(yy)                年だけを２桁で表します。
    037 *  $(MM)                月を２桁 (02,03など)で表します。
    038 *  $(dd)                日を２桁 (02,03など)で表します。
    039 *  $(HH:mm:ss)          時：分：秒を表します。
    040 *  $(MMMMMMMM)          月をフルスペルで表します。
    041 *  $(MMM)               月を３桁固定(Mar，Aplなど)で表します。
    042 *  $(EEEEEEEE)          曜日をフルスペルで表します。
    043 *  $(EEE)               曜日を３桁固定(Sun,Monなど)で表します。
    044 *
    045 *   時刻フォーマット構文
    046 *
    047 *   記号     意味                    表示                例
    048 *   ------   -------                 ------------        -------
    049 *   G        年号                    (テキスト)          AD
    050 *   y        年                      (数値)              1996
    051 *   M        月                      (テキスト &amp; 数値)  July &amp; 07
    052 *   d        日                      (数値)              10
    053 *   h        午前/午後の時 (1~12)    (数値)              12
    054 *   H        一日における時 (0~23)   (数値)              0
    055 *   m        分                      (数値)              30
    056 *   s        秒                      (数値)              55
    057 *   S        ミリ秒                  (数値)              978
    058 *   E        曜日                    (テキスト)          火曜日
    059 *   D        年における日            (数値)              189
    060 *   F        月における曜日          (数値)              2 (7月の第2水曜日)
    061 *   w        年における週            (数値)              27
    062 *   W        月における週            (数値)              2
    063 *   a        午前/午後               (テキスト)          PM
    064 *   k        一日における時 (1~24)   (数値)              24
    065 *   K        午前/午後の時 (0~11)    (数値)              0
    066 *   z        時間帯                  (テキスト)          PDT
    067 *   '        テキスト用エスケープ
    068 *   ''       単一引用符                                  '
    069 *
    070 *  パターン文字のカウントによって、そのフォーマットが決まります。
    071 *  (テキスト): 4以上: フル形式を使用します。4以下: 短いまたは省力された形式があれば、それを使用します。
    072 *
    073 *  (数値): 最小桁数。これより短い数値は、この桁数までゼロが追加されます。年には特別な処理があります。
    074 *  つまり、'y'のカウントが2なら、年は2桁に短縮されます。
    075 *
    076 *  (テキスト ＆ 数値): 3以上ならテキストを、それ以外なら数値を使用します。
    077 *
    078 *  パターンの文字が['a'..'z']と['A'..'Z']の範囲になければ、その文字は引用テキストとして扱われます。
    079 *  たとえば、':'、'.'、' '、'#'、'@'などの文字は、単一引用符に囲まれていなくても、
    080 *  結果の時刻テキストに使用されます。
    081 *
    082 *  無効なパターン文字がパターンに入っていると、フォーマットや解析で例外がスローされます。
    083 *
    084 *  USロケールを使った例:
    085 *
    086 *   フォーマットパターン                   結果
    087 *   --------------------                   ----
    088 *   "yyyy.MM.dd G 'at' hh:mm:ss z"    ⇒  1996.07.10 AD at 15:08:56 PDT
    089 *   "EEE, MMM d, ''yy"                ⇒  Wed, July 10, '96
    090 *   "h:mm a"                          ⇒  12:08 PM
    091 *   "hh 'o''''clock' a, zzzz"         ⇒  12 o'clock PM, Pacific Daylight Time
    092 *   "K:mm a, z"                       ⇒  0:00 PM, PST
    093 *   "yyyyy.MMMMM.dd GGG hh:mm aaa"    ⇒  1996.July.10 AD 12:08 PM
    094 *
    095 * @og.rev 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system
    096 *
    097 * @version  0.9.0  1999/03/09
    098 * @author   Kazuhiko Hasegawa
    099 * @since    JDK1.1,
    100 */
    101public class DateSet {
    102        private String[] keys ;
    103        private String[] vals ;
    104
    105        /**
    106         * フォーマット解析時に置き換える キーと値の配列を設定します。
    107         *
    108         * $(KEY1) 文字列を VAL1 文字列と置き換える処理を行います。これにより日付以外の
    109         * 文字列を置き換える処理を実行できます。
    110         *
    111         * @param       inkeys  置き換え元キー配列
    112         * @param       invals  置き換え元値配列
    113         */
    114        public void setKeysVals( final String[] inkeys, final String[] invals ) {
    115                if( inkeys != null && invals != null && inkeys.length == invals.length ) {
    116                        final int size = inkeys.length ;
    117                        keys = new String[size];
    118                        vals = new String[size];
    119                        System.arraycopy( inkeys,0,keys,0,size );
    120                        System.arraycopy( invals,0,vals,0,size );
    121                }
    122        }
    123
    124        /**
    125         * 現在日付、時刻をフォーマット指定個所に埋め込みます。
    126         * フォーマットの指定方法は、java.text.SimpleDateFormat の指定方法と同一です。
    127         *
    128         * @og.rev 6.3.6.0 (2015/08/16) System.arraycopy が使える箇所は、置き換えます。
    129         * @og.rev 6.4.2.0 (2016/01/29) fukurou.util.StringUtil → fukurou.system.HybsConst に変更
    130         *
    131         * @param       inByte  変換元バイト配列(可変長引数)
    132         * @return      変換後のバイト配列
    133         */
    134        public byte[] change( final byte... inByte ) {
    135                byte[] outByte = new byte[inByte.length+100];
    136                int add = 0;
    137                for( int i=0; i<inByte.length; i++ ) {
    138                        if( inByte[i] == '$' && i<inByte.length-1 && inByte[i+1] == '(' ) {
    139                                int j = 0;
    140                                while( inByte[i+j+2] != ')') { j++; }
    141                                final String str = changeForm( new String( inByte,i+2,j,HybsConst.DEFAULT_CHARSET ) );          // 6.4.2.0 (2016/01/29)
    142                                final byte[] byteDate = str.getBytes( HybsConst.DEFAULT_CHARSET ) ;                                                     // 6.4.2.0 (2016/01/29)
    143                                // 6.3.6.0 (2015/08/16) System.arraycopy が使える箇所は、置き換えます。
    144                                System.arraycopy( byteDate,0,outByte,add,byteDate.length );             // 6.3.6.0 (2015/08/16)
    145                                add += byteDate.length ;                                                                                // 6.3.6.0 (2015/08/16)
    146                                i   += j+2;
    147                        }
    148                        else {
    149                                outByte[add] = inByte[i];
    150                                add++;
    151                        }
    152                }
    153                final byte[] rtnByte = new byte[add];
    154                System.arraycopy( outByte,0,rtnByte,0,add );
    155                return rtnByte;
    156        }
    157
    158        /**
    159         * keys,vals の変換、および、現在日付、時刻のフォーマット変換を行います。
    160         *
    161         * 先に、keys,vals の変換を行います。form が、keys にマッチすれば、vals を
    162         * 返します。最後までマッチしなければ、時刻のフォーマット変換を行います。
    163         * フォーマットの指定方法は、java.text.SimpleDateFormat の指定方法と同一です。
    164         *
    165         * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。
    166         * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
    167         *
    168         * @param       form フォーム文字列 ( 例 "yyyy/MM/dd HH:mm:ss" )
    169         *
    170         * @return      フォーマット変換結果
    171         */
    172        public String changeForm( final String form ) {
    173                // 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
    174                if( keys != null && vals != null ) {
    175                        for( int i=0; i<keys.length; i++ ) {
    176                                if( form.equals( keys[i] ) ) {
    177                                        return vals[i];
    178                                }
    179                        }
    180                }
    181
    182                return DateSet.getDate( form );
    183        }
    184
    185        /**
    186         * keys,vals の変換、および、現在日付、時刻のフォーマット変換を行います。
    187         *
    188         * 先に、keys,vals の変換を行います。form が、keys にマッチすれば、vals を
    189         * 返します。最後までマッチしなければ、時刻のフォーマット変換を行います。
    190         * フォーマットの指定方法は、java.text.SimpleDateFormat の指定方法と同一です。
    191         *
    192         * @param       form フォーム文字列 ( 例 "yyyy/MM/dd HH:mm:ss" )
    193         *
    194         * @return      フォーマット変換結果
    195         * @og.rtnNotNull
    196         */
    197        public String changeString( final String form ) {
    198                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
    199                int bkst = 0;
    200                int st = form.indexOf( "$(" );
    201                while( st >= 0 ) {
    202                        buf.append( form.substring( bkst,st ) );
    203                        final int ed = form.indexOf( ')',st+2 );                                // 6.0.2.5 (2014/10/31) refactoring
    204                        buf.append( changeForm( form.substring( st+2,ed ) ) );
    205                        bkst = ed + 1;
    206                        st = form.indexOf( "$(",bkst );
    207                }
    208                buf.append( form.substring( bkst ) );
    209
    210                return buf.toString();
    211        }
    212
    213        /**
    214         * 現在日付、時刻を指定のフォーマットで文字列に変換して返します。
    215         * 出力フォーマットは、"yyyy/MM/dd HH:mm:ss" 固定です。
    216         *
    217         * @og.rev 5.5.7.2 (2012/10/09) 新規作成
    218         * @og.rev 6.4.2.0 (2016/01/29) fukurou.util.HybsDateUtil → fukurou.system.DateSet に変更
    219         *
    220         * @return      現在日付、時刻 ( 例 2012/09/05 18:10:24 )
    221         * @og.rtnNotNull
    222         */
    223        public static String getDate() {
    224                final DateFormat formatter = new SimpleDateFormat( "yyyy/MM/dd HH:mm:ss",Locale.JAPAN );
    225                return formatter.format( new Date() );
    226        }
    227
    228        /**
    229         * 現在時刻を指定のフォーマットで文字列に変換して返します。
    230         * フォーマットの指定方法は、java.text.SimpleDateFormat の指定方法と同一です。
    231         * 変換時のロケーションは、Locale.JAPAN です。
    232         * 現在時刻は、new Date() で求めます。
    233         *
    234         * @param       form フォーム文字列 ( 例 "yyyy/MM/dd HH:mm:ss.SSS" )
    235         *
    236         * @og.rev 5.5.7.2 (2012/10/09) 新規作成
    237         * @og.rev 6.4.2.0 (2016/01/29) fukurou.util.HybsDateUtil → fukurou.system.DateSet に変更
    238         *
    239         * @return      現在日付、時刻
    240         * @og.rtnNotNull
    241         * @see         java.text.SimpleDateFormat
    242         */
    243        public static String getDate( final String form ) {
    244                final DateFormat formatter = new SimpleDateFormat( form,Locale.JAPAN );
    245                return formatter.format( new Date() );
    246        }
    247
    248        /**
    249         * 指定時刻を指定のフォーマットで文字列に変換して返します。
    250         * フォーマットの指定方法は、java.text.SimpleDateFormat の指定方法と同一です。
    251         * 変換時のロケーションは、Locale.JAPAN です。
    252         * 指定時刻は、new Date( time ) で求めます。
    253         *
    254         * @param       time 指定のカレントタイムのロング値
    255         * @param       form フォーム文字列 ( 例 "yyyy/MM/dd HH:mm:ss.SSS" )
    256         *
    257         * @og.rev 5.5.7.2 (2012/10/09) 新規作成
    258         * @og.rev 6.4.2.0 (2016/01/29) fukurou.util.HybsDateUtil → fukurou.system.DateSet に変更
    259         *
    260         * @return      現在日付、時刻( 例 2001/04/17 15:48:22 )
    261         * @og.rtnNotNull
    262         */
    263        public static String getDate( final long time,final String form ) {
    264                final DateFormat formatter = new SimpleDateFormat( form,Locale.JAPAN );
    265                return formatter.format( new Date( time ) );
    266        }
    267
    268        /**
    269         * 入力ファイルの時刻フォーマットを変換して出力ファイルに書き込みます。
    270         *
    271         * 引数に &lt;key1&gt; &lt;val1&gt; のペア情報を渡すことが可能です。
    272         * 先に、keys,vals の変換を行います。form が、keys にマッチすれば、vals を
    273         * 返します。最後までマッチしなければ、時刻のフォーマット変換を行います。
    274         * フォーマットの指定方法は、java.text.SimpleDateFormat の指定方法と同一です。
    275         * フォーム文字列例 (  "yyyy/MM/dd HH:mm:ss" )
    276         *
    277         * @param       args 引数配列( 入力ファイル 出力ファイル キー１ 値１ ･･･
    278         * @throws Throwable なんらかのエラーが発生した場合。
    279         */
    280        public static void main( final String[] args ) throws Throwable {
    281                if( args.length > 2 && ( args.length % 2 != 0 ) ) {
    282                        System.err.println( "Usage: java org.opengion.fukurou.system.DateSet <inputFile> <outputFile> [<key1> <val1> ･･･]" );
    283                        return ;
    284                }
    285
    286                String[] keys = new String[ (args.length-2)/2 ];
    287                String[] vals = new String[ (args.length-2)/2 ];
    288                for( int i=1; i<=keys.length; i++ ) {
    289                        keys[i-1] = args[i*2];
    290                        vals[i-1] = args[i*2+1];
    291                }
    292
    293                final FileInputStream filein = new FileInputStream( args[0] );
    294                final byte[] byteIn = new byte[ filein.available() ];
    295                final int len = filein.read( byteIn );
    296                if( len != byteIn.length ) {
    297                        final String errMsg = "読み取りファイルのデータが切り捨てられました。" +
    298                                                        "File=" + args[0] + " Length=" + len  + " Input=" + byteIn.length ;
    299                        System.err.println( errMsg );
    300                }
    301                filein.close();
    302
    303                final DateSet dateSet = new DateSet();
    304                dateSet.setKeysVals( keys,vals );
    305                final byte[] byteout = dateSet.change( byteIn );
    306
    307                final FileOutputStream fileout = new FileOutputStream( args[1] );
    308                fileout.write( byteout );
    309                fileout.close();
    310        }
    311}