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 018// import java.io.FileInputStream; // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidFileStream 対応 019// import java.io.FileOutputStream; // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidFileStream 対応 020import java.io.BufferedInputStream; // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidFileStream 対応 021import java.io.BufferedOutputStream; // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidFileStream 対応 022import java.io.InputStream; // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidFileStream 対応 023import java.io.OutputStream; // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidFileStream 対応 024import java.nio.file.Paths; // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidFileStream 対応 025import java.nio.file.Files; // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidFileStream 対応 026 027import java.util.Date; 028import java.util.Locale; 029// import java.util.Calendar; // 7.0.1.4 (2018/11/26) 030import java.util.concurrent.ConcurrentMap; // 7.0.1.3 (2018/11/12) 031import java.util.concurrent.ConcurrentHashMap; // 7.0.1.3 (2018/11/12) 032import java.text.DateFormat; 033import java.text.SimpleDateFormat; 034 035// import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE; // 6.1.0.0 (2014/12/26) refactoring 036 037/** 038 * DateSet.java は、入力ファイルの日付、時刻キーワードを実行時の日時で変換して出力します。 039 * 040 * 変換には、$(yyyy)の形式で指定し、カッコの文字列は java.text.SimpleDateFormat で使用する 041 * 時刻フォーマット構文を用います。 042 * また、引数に keys,vals を渡すことで、$(KEY1) 文字列を VAL1 文字列と置き換えます。 043 * 044 * サンプルファイル 045 * $(yyyy/MM/dd) 年/月/日を表します。 046 * $(yy) 年だけを2桁で表します。 047 * $(MM) 月を2桁 (02,03など)で表します。 048 * $(dd) 日を2桁 (02,03など)で表します。 049 * $(HH:mm:ss) 時:分:秒を表します。 050 * $(MMMMMMMM) 月をフルスペルで表します。 051 * $(MMM) 月を3桁固定(Mar,Aplなど)で表します。 052 * $(EEEEEEEE) 曜日をフルスペルで表します。 053 * $(EEE) 曜日を3桁固定(Sun,Monなど)で表します。 054 * 055 * // 7.0.1.3 (2018/11/12) 2019/01/01 2030/01/01 056 * $(ATIME) 通算秒数( new Date().getTime()/1000 ) の 10桁文字列 1546268400 1893423600 057 * $(BTIME) 通算分数( new Date().getTime()/60000 ) の 8桁文字列 25771140 31557060 058 * ※ BTIME が桁あふれするのは、2160/02/18 です。 059 * 060 * 時刻フォーマット構文 061 * 062 * 記号 意味 表示 例 063 * ------ ------- ------------ ------- 064 * G 年号 (テキスト) AD 065 * y 年 (数値) 1996 066 * M 月 (テキスト & 数値) July & 07 067 * d 日 (数値) 10 068 * h 午前/午後の時 (1~12) (数値) 12 069 * H 一日における時 (0~23) (数値) 0 070 * m 分 (数値) 30 071 * s 秒 (数値) 55 072 * S ミリ秒 (数値) 978 073 * E 曜日 (テキスト) 火曜日 074 * D 年における日 (数値) 189 075 * F 月における曜日 (数値) 2 (7月の第2水曜日) 076 * w 年における週 (数値) 27 077 * W 月における週 (数値) 2 078 * a 午前/午後 (テキスト) PM 079 * k 一日における時 (1~24) (数値) 24 080 * K 午前/午後の時 (0~11) (数値) 0 081 * z 時間帯 (テキスト) PDT 082 * ' テキスト用エスケープ 083 * '' 単一引用符 ' 084 * 085 * パターン文字のカウントによって、そのフォーマットが決まります。 086 * (テキスト): 4以上: フル形式を使用します。4以下: 短いまたは省力された形式があれば、それを使用します。 087 * 088 * (数値): 最小桁数。これより短い数値は、この桁数までゼロが追加されます。年には特別な処理があります。 089 * つまり、'y'のカウントが2なら、年は2桁に短縮されます。 090 * 091 * (テキスト & 数値): 3以上ならテキストを、それ以外なら数値を使用します。 092 * 093 * パターンの文字が['a'..'z']と['A'..'Z']の範囲になければ、その文字は引用テキストとして扱われます。 094 * たとえば、':'、'.'、' '、'#'、'@'などの文字は、単一引用符に囲まれていなくても、 095 * 結果の時刻テキストに使用されます。 096 * 097 * 無効なパターン文字がパターンに入っていると、フォーマットや解析で例外がスローされます。 098 * 099 * USロケールを使った例: 100 * 101 * フォーマットパターン 結果 102 * -------------------- ---- 103 * "yyyy.MM.dd G 'at' hh:mm:ss z" ⇒ 1996.07.10 AD at 15:08:56 PDT 104 * "EEE, MMM d, ''yy" ⇒ Wed, July 10, '96 105 * "h:mm a" ⇒ 12:08 PM 106 * "hh 'o''''clock' a, zzzz" ⇒ 12 o'clock PM, Pacific Daylight Time 107 * "K:mm a, z" ⇒ 0:00 PM, PST 108 * "yyyyy.MMMMM.dd GGG hh:mm aaa" ⇒ 1996.July.10 AD 12:08 PM 109 * 110 * @og.rev 6.4.2.0 (2016/01/29) package変更 fukurou.util → fukurou.system 111 * @og.rev 7.0.1.3 (2018/11/12) keys,valsをMapに変更。BUILD_TYPEのビルド番号を100秒単位に変更。 112 * 113 * @version 0.9.0 1999/03/09 114 * @author Kazuhiko Hasegawa 115 * @since JDK1.1, 116 */ 117public class DateSet { 118 private final ConcurrentMap<String,String> prmMap = new ConcurrentHashMap<>(); // 7.0.1.3 (2018/11/12) 119 120// private String[] keys ; 121// private String[] vals ; 122 123 /** 124 * デフォルトコンストラクター 125 * 126 * Mapの初期値を設定しておきます。 127 * 128 * @og.rev 7.0.1.3 (2018/11/12) KeysValsは、Mapに置き換え 129 * @og.rev 7.0.1.4 (2018/11/26) ATIME,BTIME 追加 130 */ 131 public DateSet() { 132 prmMap.put( "ATIME" , Long.toString( new Date().getTime()/1_000 ) ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 UseUnderscoresInNumericLiterals 133 prmMap.put( "BTIME" , Long.toString( new Date().getTime()/60_000 ) ); // 8.5.4.2 (2024/01/12) PMD 7.0.0 UseUnderscoresInNumericLiterals 134 135 // final Calendar now = Calendar.getInstance(); 136 // final int tm = ( now.get( Calendar.HOUR_OF_DAY ) * 60 + now.get( Calendar.MINUTE ) ) / 15 ; // 0 ~ 96未満の数値になる 137 138 // final String CTIME = getDate( "yyDDD" ) + String.format( "%02d" , tm ) ; 139 // prmMap.put( "CTIME" , CTIME ); 140 } 141 142 /** 143 * フォーマット解析時に置き換える キーと値のMapを設定します。 144 * 145 * $(KEY1) 文字列を VAL1 文字列と置き換える処理を行います。これにより日付以外の 146 * 文字列を置き換える処理を実行できます。 147 * 148 * @og.rev 7.0.1.3 (2018/11/12) KeysValsは、Mapに置き換え 149 * 150 * @param pMap 置き換え元のMap 151 */ 152 public void setParamMap( final ConcurrentMap<String,String> pMap ) { 153 if( pMap != null ) { 154 prmMap.putAll( pMap ); 155 } 156 } 157 158// /** 159// * フォーマット解析時に置き換える キーと値の配列を設定します。 160// * 161// * $(KEY1) 文字列を VAL1 文字列と置き換える処理を行います。これにより日付以外の 162// * 文字列を置き換える処理を実行できます。 163// * 164// * @og.rev 7.0.1.3 (2018/11/12) KeysValsは、Mapに置き換え 165// * 166// * @param inkeys 置き換え元キー配列 167// * @param invals 置き換え元値配列 168// */ 169// public void setKeysVals( final String[] inkeys, final String[] invals ) { 170// if( inkeys != null && invals != null && inkeys.length == invals.length ) { 171// final int size = inkeys.length ; 172// keys = new String[size]; 173// vals = new String[size]; 174// System.arraycopy( inkeys,0,keys,0,size ); 175// System.arraycopy( invals,0,vals,0,size ); 176// } 177// } 178 179 /** 180 * 現在日付、時刻をフォーマット指定個所に埋め込みます。 181 * フォーマットの指定方法は、java.text.SimpleDateFormat の指定方法と同一です。 182 * 183 * @og.rev 6.3.6.0 (2015/08/16) System.arraycopy が使える箇所は、置き換えます。 184 * @og.rev 6.4.2.0 (2016/01/29) fukurou.util.StringUtil → fukurou.system.HybsConst に変更 185 * @og.rev 8.5.3.2 (2023/10/13) JDK21注意。JDK17までは、Windows-31J だったが、JDK21から、UTF-8 に変更されている。 186 * 187 * @param inByte 変換元バイト配列(可変長引数) 188 * @return 変換後のバイト配列 189 */ 190 public byte[] change( final byte... inByte ) { 191 final byte[] outByte = new byte[inByte.length+100]; // 8.5.4.2 (2024/01/12) PMD 7.0.0 LocalVariableCouldBeFinal 192 int add = 0; 193 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidReassigningLoopVariables 194// for( int i=0; i<inByte.length; i++ ) { 195// if( inByte[i] == '$' && i<inByte.length-1 && inByte[i+1] == '(' ) { 196// // 8.5.4.2 (2024/01/12) PMD 7.0.0 ShortVariable j ⇒ jj に変更 197// int jj = 0; 198// while( inByte[i+jj+2] != ')') { jj++; } 199// final String str = changeForm( new String( inByte,i+2,jj,HybsConst.DEFAULT_CHARSET ) ); // 6.4.2.0 (2016/01/29) 200// final byte[] byteDate = str.getBytes( HybsConst.DEFAULT_CHARSET ) ; // 6.4.2.0 (2016/01/29) 201// // 6.3.6.0 (2015/08/16) System.arraycopy が使える箇所は、置き換えます。 202// System.arraycopy( byteDate,0,outByte,add,byteDate.length ); // 6.3.6.0 (2015/08/16) 203// add += byteDate.length ; // 6.3.6.0 (2015/08/16) 204// i += jj+2; 205// } 206// else { 207// outByte[add] = inByte[i]; 208// add++; 209// } 210// } 211 final int inLen = inByte.length; 212 int ii = 0; 213 while( ii<inLen ) { 214 if( inByte[ii] == '$' && ii<inLen-1 && inByte[ii+1] == '(' ) { 215 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ShortVariable j ⇒ jj に変更 216 int jj = 0; 217 while( inByte[ii+jj+2] != ')') { jj++; } 218 final String str = changeForm( new String( inByte,ii+2,jj,HybsConst.DEFAULT_CHARSET ) ); // 6.4.2.0 (2016/01/29) 219 final byte[] byteDate = str.getBytes( HybsConst.DEFAULT_CHARSET ) ; // 6.4.2.0 (2016/01/29) 220 // 6.3.6.0 (2015/08/16) System.arraycopy が使える箇所は、置き換えます。 221 System.arraycopy( byteDate,0,outByte,add,byteDate.length ); // 6.3.6.0 (2015/08/16) 222 add += byteDate.length ; // 6.3.6.0 (2015/08/16) 223 ii += jj+2; 224 } 225 else { 226 outByte[add] = inByte[ii]; 227 add++; 228 } 229 ii++ ; 230 } 231 final byte[] rtnByte = new byte[add]; 232 System.arraycopy( outByte,0,rtnByte,0,add ); 233 return rtnByte; 234 } 235 236 /** 237 * パラメータの変換、および、現在日付、時刻のフォーマット変換を行います。 238 * 239 * 先に、パラメータの変換を行います。form が、Mapのkey にマッチすれば、 240 * その値を返します。マッチしなければ、時刻のフォーマット変換を行います。 241 * フォーマットの指定方法は、java.text.SimpleDateFormat の指定方法と同一です。 242 * 243 * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。 244 * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs) 245 * @og.rev 7.0.1.3 (2018/11/12) KeysValsは、Mapに置き換え 246 * 247 * @param form フォーム文字列 ( 例 "yyyy/MM/dd HH:mm:ss" ) 248 * 249 * @return フォーマット変換結果 250 */ 251 public String changeForm( final String form ) { 252 // Map#getOrDefault( key,defVal )はdefValも評価が必ず実行されるため、使えない。 253 // return prmMap.getOrDefault( form,DateSet.getDate( form ) ; 254 255 final String rtn = prmMap.get( form ); 256 257// return rtn == null ? DateSet.getDate( form ) : rtn ; 258 return rtn == null ? getDate( form ) : rtn ; // 8.5.4.2 (2024/01/12) PMD 7.0.0 UnnecessaryFullyQualifiedName 259 260// // 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs) 261// if( keys != null && vals != null ) { 262// for( int i=0; i<keys.length; i++ ) { 263// if( form.equals( keys[i] ) ) { 264// return vals[i]; 265// } 266// } 267// } 268// 269// return DateSet.getDate( form ); 270 } 271 272 /** 273 * パラメータの変換、および、現在日付、時刻のフォーマット変換を行います。 274 * 275 * 先に、パラメータの変換を行います。form が、Mapのkey にマッチすれば、 276 * その値を返します。マッチしなければ、時刻のフォーマット変換を行います。 277 * フォーマットの指定方法は、java.text.SimpleDateFormat の指定方法と同一です。 278 * 279 * @param form フォーム文字列 ( 例 "yyyy/MM/dd HH:mm:ss" ) 280 * 281 * @return フォーマット変換結果 282 * @og.rtnNotNull 283 */ 284 public String changeString( final String form ) { 285 final StringBuilder buf = new StringBuilder( HybsConst.BUFFER_MIDDLE ); 286 int bkst = 0; 287 int st = form.indexOf( "$(" ); 288 while( st >= 0 ) { 289 buf.append( form.substring( bkst,st ) ); 290 final int ed = form.indexOf( ')',st+2 ); // 6.0.2.5 (2014/10/31) refactoring 291 buf.append( changeForm( form.substring( st+2,ed ) ) ); 292 bkst = ed + 1; 293 st = form.indexOf( "$(",bkst ); 294 } 295 buf.append( form.substring( bkst ) ); 296 297 return buf.toString(); 298 } 299 300 /** 301 * 現在日付、時刻を指定のフォーマットで文字列に変換して返します。 302 * 出力フォーマットは、"yyyy/MM/dd HH:mm:ss" 固定です。 303 * 304 * @og.rev 5.5.7.2 (2012/10/09) 新規作成 305 * @og.rev 6.4.2.0 (2016/01/29) fukurou.util.HybsDateUtil → fukurou.system.DateSet に変更 306 * 307 * @return 現在日付、時刻 ( 例 2012/09/05 18:10:24 ) 308 * @og.rtnNotNull 309 */ 310 public static String getDate() { 311 final DateFormat formatter = new SimpleDateFormat( "yyyy/MM/dd HH:mm:ss",Locale.JAPAN ); 312 return formatter.format( new Date() ); 313 } 314 315 /** 316 * 現在時刻を指定のフォーマットで文字列に変換して返します。 317 * フォーマットの指定方法は、java.text.SimpleDateFormat の指定方法と同一です。 318 * 変換時のロケーションは、Locale.JAPAN です。 319 * 現在時刻は、new Date() で求めます。 320 * 321 * @param form フォーム文字列 ( 例 "yyyy/MM/dd HH:mm:ss.SSS" ) 322 * 323 * @og.rev 5.5.7.2 (2012/10/09) 新規作成 324 * @og.rev 6.4.2.0 (2016/01/29) fukurou.util.HybsDateUtil → fukurou.system.DateSet に変更 325 * 326 * @return 現在日付、時刻 327 * @og.rtnNotNull 328 * @see java.text.SimpleDateFormat 329 */ 330 public static String getDate( final String form ) { 331 final DateFormat formatter = new SimpleDateFormat( form,Locale.JAPAN ); 332 return formatter.format( new Date() ); 333 } 334 335 /** 336 * 指定時刻を指定のフォーマットで文字列に変換して返します。 337 * フォーマットの指定方法は、java.text.SimpleDateFormat の指定方法と同一です。 338 * 変換時のロケーションは、Locale.JAPAN です。 339 * 指定時刻は、new Date( time ) で求めます。 340 * 341 * @param time 指定のカレントタイムのロング値 342 * @param form フォーム文字列 ( 例 "yyyy/MM/dd HH:mm:ss.SSS" ) 343 * 344 * @og.rev 5.5.7.2 (2012/10/09) 新規作成 345 * @og.rev 6.4.2.0 (2016/01/29) fukurou.util.HybsDateUtil → fukurou.system.DateSet に変更 346 * 347 * @return 現在日付、時刻( 例 2001/04/17 15:48:22 ) 348 * @og.rtnNotNull 349 */ 350 public static String getDate( final long time,final String form ) { 351 final DateFormat formatter = new SimpleDateFormat( form,Locale.JAPAN ); 352 return formatter.format( new Date( time ) ); 353 } 354 355 /** 356 * 入力ファイルの時刻フォーマットを変換して出力ファイルに書き込みます。 357 * 358 * 引数に <key1> <val1> のペア情報を渡すことが可能です。 359 * 先に、keys,vals の変換を行います。form が、keys にマッチすれば、vals を 360 * 返します。最後までマッチしなければ、時刻のフォーマット変換を行います。 361 * フォーマットの指定方法は、java.text.SimpleDateFormat の指定方法と同一です。 362 * フォーム文字列例 ( "yyyy/MM/dd HH:mm:ss" ) 363 * 364 * @og.rev 7.0.1.3 (2018/11/12) KeysValsは、Mapに置き換え 365 * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 366 * 367 * @param args 引数配列( 入力ファイル 出力ファイル キー1 値1 ・・・ 368 * @throws Throwable なんらかのエラーが発生した場合。 369 */ 370 public static void main( final String[] args ) throws Throwable { 371 if( args.length > 2 && ( args.length % 2 != 0 ) ) { 372 System.err.println( "Usage: java org.opengion.fukurou.system.DateSet <inputFile> <outputFile> [<key1> <val1> ・・・]" ); 373 return ; 374 } 375 376 final ConcurrentMap<String,String> prmMap = new ConcurrentHashMap<>(); // 7.0.1.3 (2018/11/12) 377 378 for( int i=2; i<args.length; i+=2 ) { 379 prmMap.put( args[i] , args[i+1] ); 380 } 381 382// String[] keys = new String[ (args.length-2)/2 ]; 383// String[] vals = new String[ (args.length-2)/2 ]; 384// for( int i=1; i<=keys.length; i++ ) { 385// keys[i-1] = args[i*2]; 386// vals[i-1] = args[i*2+1]; 387// } 388 389 // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 390// final FileInputStream filein = new FileInputStream( args[0] ); 391 byte[] byteIn = null; 392 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidFileStream 対応 393// try ( FileInputStream filein = new FileInputStream( args[0] ) ) { 394 try ( InputStream filein = new BufferedInputStream( Files.newInputStream(Paths.get(args[0])) ) ) { 395// final byte[] byteIn = new byte[ filein.available() ]; 396 byteIn = new byte[ filein.available() ]; 397 final int len = filein.read( byteIn ); 398 if( len != byteIn.length ) { 399 final String errMsg = "読み取りファイルのデータが切り捨てられました。" + 400 "File=" + args[0] + " Length=" + len + " Input=" + byteIn.length ; 401 System.err.println( errMsg ); 402 } 403 } 404// filein.close(); 405 406 if( byteIn != null ) { 407 final DateSet dateSet = new DateSet(); 408 // dateSet.setKeysVals( keys,vals ); 409 dateSet.setParamMap( prmMap ); 410 final byte[] byteout = dateSet.change( byteIn ); 411 412 // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 413 // final FileOutputStream fileout = new FileOutputStream( args[1] ); 414 // fileout.write( byteout ); 415 // fileout.close(); 416 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidFileStream 対応 417// try ( FileOutputStream fileout = new FileOutputStream( args[1] ) ) { 418 try ( OutputStream fileout = new BufferedOutputStream( Files.newOutputStream(Paths.get(args[1])) ) ) { 419 fileout.write( byteout ); 420 } 421 } 422 } 423}