CVTDAT (日付形式変換) は以前にも紹介したように非常に役に立つコマンドであるが
実際の運用面となると日付の加算または減算機能も欲しいところである。
納期がつねに 14日間とわかっているならば今日、発注すると納入予定日はいつになるのだろうか ?
それを算出したい、という日付計算の用途は数多くあるはずである。
ここで紹介する ADDDAT (日付の加算)コマンドは日付に加算または減算を行うことができるコマンドである。
ADDDAT は CVTDAT コマンドの高度な使い方を学習できるのと同時に
*JUL (JULIAN DATE)の意味も学習することができるようになっている。
実は ADDDAT は QUSRTOOL の中に紹介されているコマンドであるが、
ここではそれをもっとわかりやすく肝心なところにスポットを当てて解説するようにした。
ADDDAT の処理が理解できれば
ユーザーはより高度な日付の処理プログラムも自作することができるはずである。
本当は CLPやRPGの組み込み関数として ADDDAT が提供されていてもおかしくはない。
それほど ADDDAT は適用業務では重要なのである。
| 加算または減算日数 | 日数を指定する。 減算日数の場合は -15 のように前にマイナス符号をつけて指定する。 |
|---|---|
| 結果の日付 | 結果の日付を受け取るための文字変数(6桁または8桁の文字)を指定する。 |
| 開始の日付 (SYS FMT) | 演算元となる日付の変数または具体的な日付を6桁または8桁の数値で指定する。 *TODAY は本日の日付を意味する。 |
| 結果の日付の様式 | 結果の日付の日付形式を指定する。 西暦20YYMMDD の場合は *YYMD と指定する。 |
| 日付の様式 | 開始の日付の日付形式を指定する。 |
| 結果の日付その2 | 未使用 |
0001.00 /* TAADATA - ADD DATE - 日付加算 */
0002.00 /*PARMS ALLOW((*IPGM)(*BPGM)) PGM(TAADATAC) PRDLIB(TAATOOL) */
0003.00 /* */
0004.00 /* ADD DATE コマンドは */
0005.00 /* 日付の加算または減算の結果を戻します。 */
0006.00 /* */
0007.00 /* TAA CPP IS TAADATAC */
0008.00 /* */
0009.00 CMD PROMPT(' 日付の加算 ')
0010.00 PARM KWD(DAYS) TYPE(*DEC) LEN(5) RANGE(-35000 +
0011.00 35000) MIN(1) PROMPT(' 加算または減算日数 ')
0012.00 PARM KWD(TOVAR) TYPE(*CHAR) LEN(6) RTNVAL(*YES) +
0013.00 PROMPT(' 結果の日付 ')
0014.00 PARM KWD(DATE) TYPE(*DEC) LEN(6 0) DFT(*TODAY) +
0015.00 RANGE(000000 999999) SPCVAL((*TODAY 0) +
0016.00 (*JOB -1)) PROMPT(' 開始の日付 (SYS FMT)')
0017.00 PARM KWD(TOVARFMT) TYPE(*CHAR) LEN(7) RSTD(*YES) +
0018.00 DFT(*JOB) VALUES('*SYSVAL' '*MDY' '*DMY' +
0019.00 '*YMD' '*JUL' '*JOB' '*CYMD' '*MDYY' +
0020.00 '*DMYY' '*YYMD' '*ISO' '*USA' '*EUR' +
0021.00 '*JIS') PROMPT(' 結果の日付の様式 ')
0022.00 PARM KWD(DATEFMT) TYPE(*CHAR) LEN(7) RSTD(*YES) +
0023.00 DFT(*JOB) VALUES('*SYSVAL' '*MDY' '*DMY' +
0024.00 '*YMD' '*JUL' '*JOB') PROMPT(' 日付の様式 ')
0025.00 PARM KWD(TOVAR2) TYPE(*CHAR) LEN(10) RTNVAL(*YES) +
0026.00 PROMPT(' 結果の日付その2 ')
値の受け取りが必要なのでバッチ処理環境のみの実行を次のように指定してコンパイルする。
CRTCMD CMD(MYLIB/ADDDAT) PGM(MYLIB/ADDDATCL) SRCFILE(MYSRCLIB/QCMDSRC) ALOW(*IPGM *BPGM) AUT(*ALL)
0001.00 PGM PARM(&DAYS &TOVAR &DATE &TOVARFMT &DATEFMT +
0002.00 &TOVAR2)
0003.00 /*-------------------------------------------------------------------*/
0004.00 /* ADDDATCL : 日付の加算 */
0005.00 /* TAADATAC - add or sub N days from a given date - CPP for ADDDAT */
0006.00 /* */
0007.00 /* 2015/11/02 修正 */
0008.00 /* 動作原理は *JUL 形式の日付に変換してから加算または減算を */
0009.00 /* 行ってから元の日付形式に CVTDAT で戻すことである。 */
0010.00 /* *JUL 形式 = 年号 (2 桁 )+ 年初からの通算日 (3 桁 ) を */
0011.00 /* 理解しておけば処理の内容を理解することができる。 */
0012.00 /*-------------------------------------------------------------------*/
0013.00 DCL &DAYS *DEC LEN(5 0)
0014.00 DCL &TOVAR *CHAR LEN(6)
0015.00 DCL &TOVAR2 *CHAR LEN(10)
0016.00 DCL &DATE *DEC LEN(6 0)
0017.00 DCL &TOVARFMT *CHAR LEN(7)
0018.00 DCL &DATEFMT *CHAR LEN(7)
0019.00 DCL &WRKDAT *CHAR LEN(6)
0020.00 DCL &WRKDAT2 *CHAR LEN(10)
0021.00 DCL &WRKDAT5 *CHAR LEN(5)
0022.00 DCL &JULIANA *CHAR LEN(5)
0023.00 DCL &YRD *DEC LEN(2 0)
0024.00 DCL &DAYSD *DEC LEN(3 0)
0025.00 DCL &LEAP *DEC LEN(2 0)
0026.00 DCL &DAYSINYEAR *DEC LEN(3 0)
0027.00 DCL &NUM5 *DEC LEN(5)
0028.00 DCL &NUM2 *DEC LEN(2)
0029.00 DCL &NOTOVAR *CHAR LEN(1)
0030.00 DCL &NOTOVAR2 *CHAR LEN(1)
0031.00 DCL &ERRORSW *LGL /* Standard error */
0032.00 DCL &MSGID *CHAR LEN(7) /* Standard error */
0033.00 DCL &MSG *CHAR LEN(512) /* Standard error */
0034.00 DCL &MSGDTA *CHAR LEN(512) /* Standard error */
0035.00 DCL &MSGF *CHAR LEN(10) /* Standard error */
0036.00 DCL &MSGFLIB *CHAR LEN(10) /* Standard error */
0037.00 DCL &KEYVAR *CHAR LEN(4) /* Standard error */
0038.00 DCL &KEYVAR2 *CHAR LEN(4) /* Standard error */
0039.00 DCL &RTNTYPE *CHAR LEN(2) /* Standard error */
0040.00 MONMSG MSGID(CPF0000) EXEC(GOTO STDERR1) /* Std err */
0041.00 /* Check if requested format agrees with */
0042.00 /* the TODAT variable that has been */
0043.00 /* been specified */
0044.00 /* Invalid to use new format and 6 char */
0045.00 /* return variable */
0046.00 IF ((&TOVARFMT *EQ '*MDYY') *OR +
0047.00 (&TOVARFMT *EQ '*DMYY') *OR +
0048.00 (&TOVARFMT *EQ '*YYMD') *OR +
0049.00 (&TOVARFMT *EQ '*ISO') *OR +
0050.00 (&TOVARFMT *EQ '*USA') *OR +
0051.00 (&TOVARFMT *EQ '*EUR') *OR +
0052.00 (&TOVARFMT *EQ '*CYMD') *OR +
0053.00 (&TOVARFMT *EQ '*JIS')) DO /* New fmt */
0054.00 CHGVAR &TOVAR2 ' '
0055.00 MONMSG MSGID(MCH3601) EXEC(DO) /* No variable spec */
0056.00 SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) +
0057.00 MSGDTA('If one of the new date formats +
0058.00 is used for TOVARFMT, you cannot use +
0059.00 a return variable for TOVAR. You must +
0060.00 use a return variable for TOVAR2')
0061.00 ENDDO /* No variable specified */
0062.00 ENDDO /* New fmt */
0063.00 /* Check to see that at least one return */
0064.00 /* variable is specified */
0065.00 CHGVAR &TOVAR ' '
0066.00 MONMSG MSGID(MCH3601) EXEC(CHGVAR &NOTOVAR 'X')
0067.00 CHGVAR &TOVAR2 ' '
0068.00 MONMSG MSGID(MCH3601) EXEC(CHGVAR &NOTOVAR2 'X')
0069.00 IF ((&NOTOVAR *EQ 'X') *AND +
0070.00 (&NOTOVAR2 *EQ 'X')) DO /* Both missing */
0071.00 SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) +
0072.00 MSGDTA('You must specify a return +
0073.00 variable for either TOVAR or TOVAR2')
0074.00 ENDDO /* Both missing */
0075.00 /* &DATE=0 is special value *TODAY */
0076.00 IF (&DATE *EQ 0) DO /* Get todays date */
0077.00 RTVSYSVAL SYSVAL(QDATE) RTNVAR(&WRKDAT)
0078.00 GOTO CVTDAT
0079.00 ENDDO /* Get todays date */
0080.00 IF (&DATE *EQ -1) DO /* Get job date */
0081.00 RTVJOBA DATE(&WRKDAT)
0082.00 GOTO CVTDAT
0083.00 ENDDO /* Get job date */
0084.00 /* Use the date from the command */
0085.00 CHGVAR &WRKDAT &DATE
0086.00 IF (&DATEFMT *NE '*JUL') DO /* Not julian input */
0087.00 CVTDAT: CVTDAT DATE(&WRKDAT) TOVAR(&JULIANA) +
0088.00 FROMFMT(&DATEFMT) TOFMT(*JUL) +
0089.00 TOSEP(*NONE) /* Convert to Julian */
0090.00 MONMSG MSGID(CPF0555) EXEC(SNDPGMMSG +
0091.00 MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) +
0092.00 MSGDTA('DATE parameter value cannot +
0093.00 be converted'))
0094.00 ENDDO /* Not julian input */
0095.00 IF (&DATEFMT *EQ '*JUL') DO /* Julian input */
0096.00 CHGVAR &WRKDAT5 %SST(&WRKDAT 2 5)
0097.00 CVTDAT DATE(&WRKDAT5) TOVAR(&JULIANA) +
0098.00 FROMFMT(&DATEFMT) TOFMT(*JUL) +
0099.00 TOSEP(*NONE) /* Convert to Julian */
0100.00 MONMSG MSGID(CPF0555) EXEC(SNDPGMMSG +
0101.00 MSGID(CPF9898) MSGF(QCPFMSG) MSGTYPE(*ESCAPE) +
0102.00 MSGDTA('DATE parameter value cannot +
0103.00 be converted'))
0104.00 ENDDO /* Julian input */
0105.00 /* Substring for year and day */
0106.00
0107.00 /* *JULIANA から先頭 2 桁の年号と 3 桁の通算日を取り出す */
0108.00 CHGVAR &YRD %SST(&JULIANA 1 2)
0109.00 CHGVAR &DAYSD %SST(&JULIANA 3 3)
0110.00 /* 通算日に加算または減算を行う */
0111.00 CHGVAR VAR(&NUM5) VALUE(&DAYSD + &DAYS) /* Add days */
0112.00
0113.00 /* 以下の演算は通常の日付形式に戻すための汎用的な演算である */
0114.00 /* 日数部分がマイナスなら年から減算する */
0115.00 CHKPLUS: IF (&NUM5 *GT 0) GOTO CHKLEAP /* If positive */
0116.00 IF (&YRD *EQ 00) CHGVAR &YRD 99 /* Year 2000 */
0117.00 ELSE CHGVAR &YRD (&YRD -1) /* Decrement year */
0118.00 CHKLEAP: CHGVAR &NUM2 (&YRD / 4) /* Chk leap year */
0119.00 CHGVAR &LEAP (&YRD - (&NUM2 * 4))
0120.00 /* LEAP は年号を 4 で割った余り */
0121.00 IF (&LEAP *GT 0) CHGVAR &DAYSINYEAR 365
0122.00 ELSE CHGVAR &DAYSINYEAR 366 /* Leap year */
0123.00 /* DAYINYEAR は年間の日数 */
0124.00 /* 日数部分がマイナスか ? */
0125.00 IF (&NUM5 *LE 0) DO /* Days are negative */
126.00 CHGVAR &NUM5 (&NUM5 + &DAYSINYEAR)
127.00 GOTO CHKPLUS /* Check for positive days */
128.00 ENDDO /* End negative days */
129.00 /* 日数部分が年間日数をオーバーするか ? */
130.00 IF (&NUM5 *GT &DAYSINYEAR) DO /* Ovfl */
131.00 IF (&YRD *EQ 99) CHGVAR &YRD -1 /* Year 2000 */
132.00 CHGVAR &YRD (&YRD + 1) /* Bump year */
133.00 CHGVAR &NUM5 (&NUM5 - &DAYSINYEAR) /* Subtract */
134.00 GOTO CHKLEAP /* Test for next year */
135.00 ENDDO /* End days greater than days-in-year */
136.00
137.00 /* 正常な *JUL になったところで元の日付形式に戻す */
138.00 CHGVAR &DAYSD &NUM5 /* Chg to 3 digits */
139.00 /* Substring back into Julian date */
140.00 CHGVAR %SST(&JULIANA 1 2) &YRD
141.00 CHGVAR %SST(&JULIANA 3 3) &DAYSD
142.00 /* Convert to 8 character format */
143.00 CVTDAT DATE(&JULIANA) TOVAR(&WRKDAT2) FROMFMT(*JUL) +
144.00 TOFMT(&TOVARFMT) TOSEP(*NONE) /* Convert +
145.00 to sys fmt */
146.00 /* If last 2 postions are blank, use 6 */
0147.00 /* character format */
0148.00 IF (%SST(&WRKDAT2 7 4) *EQ ' ') DO /* 6 char */
0149.00 CHGVAR &TOVAR &WRKDAT2
0150.00 MONMSG MSGID(MCH3601) /* Ignore if not specified */
0151.00 ENDDO /* 6 char */
0152.00 /* Always move to the 10 char value */
0153.00 CHGVAR &TOVAR2 &WRKDAT2
0154.00 MONMSG MSGID(MCH3601) /* Ignore if not specified */
0155.00 /* Clear all messages */
0156.00 RMVMSG CLEAR(*ALL)
0157.00 RETURN /* Normal end of program */
0158.00 STDERR1: /* Standard error handling routine */
0159.00 IF &ERRORSW SNDPGMMSG MSGID(CPF9999) +
0160.00 MSGF(QCPFMSG) MSGTYPE(*ESCAPE)
0161.00 CHGVAR &ERRORSW '1' /* Set to fail on error */
0162.00 RCVMSG MSGTYPE(*EXCP) RMV(*NO) KEYVAR(&KEYVAR)
0163.00 STDERR2: RCVMSG MSGTYPE(*PRV) MSGKEY(&KEYVAR) RMV(*NO) +
0164.00 KEYVAR(&KEYVAR2) MSG(&MSG) +
0165.00 MSGDTA(&MSGDTA) MSGID(&MSGID) +
0166.00 RTNTYPE(&RTNTYPE) MSGF(&MSGF) +
0167.00 SNDMSGFLIB(&MSGFLIB)
0168.00 IF (&RTNTYPE *NE '02') GOTO STDERR3
0169.00 IF (&MSGID *NE ' ') SNDPGMMSG +
0170.00 MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) +
0171.00 MSGDTA(&MSGDTA) MSGTYPE(*DIAG)
0172.00 IF (&MSGID *EQ ' ') SNDPGMMSG +
0173.00 MSG(&MSG) MSGTYPE(*DIAG)
0174.00 RMVMSG MSGKEY(&KEYVAR2)
0175.00 STDERR3: RCVMSG MSGKEY(&KEYVAR) MSGDTA(&MSGDTA) +
0176.00 MSGID(&MSGID) MSGF(&MSGF) +
0177.00 SNDMSGFLIB(&MSGFLIB)
0178.00 SNDPGMMSG MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) +
0179.00 MSGDTA(&MSGDTA) MSGTYPE(*ESCAPE)
0180.00 ENDPGM
CRTCLPGM PGM(MYLIB/ADDDATCL) SRCFILE(MYSRCLIB/QCLSRC) AUT(*ALL)
このCLPの理解するためのコアは *JUL の日付形式の理解である。
*JUL 形式の日付とは
2桁年号 + 3桁の年間通算日 = 5桁の *JUL形式
となっていることである。
例えば 2016年 1月 1日は年度の始まりの日であるので *JUL 形式に直すと
2016年 1月 1日 = 16001
となる。
これが理解できれば
2015/12/31 は 15365
であることがわかるだろう。
しかし 2016年はうるう年であるので
2016/12/31 は 16366
となる。
ところで 2015/12/31 に +1 すると *JUL ; 15365 の下3桁の通算日にだけ+1加算して 366 となるが
これは 2015年の年間日数(&DAYSINYEAR=265)を超えるので
365 を引いてかつ年に +1 して*JUL は 16001 となる。
この *JUL を元の日付形式に CVTDAT で戻すと 2016/01/01 となるわけである。
このように *JUL に変換して計算してからまたもとの日付形式に戻す、ということを計算している。
これが ADDDAT の動作原理である。
これが理解できれば自社のカレンダーや日祝日、連休を考慮した日付の計算もできるはずである。
カレンダーはすでにwebサービスとしてインターネットで公開されているので
それを利用してさらに一般化したADDDAT をWebサービスとして公開することができるかもしれない。
一般化された日付計算が公開されれば、どのようなプログラムからでも利用することができる。
※ ADDDAT コマンドは SPOOLライターや EnterpriseServer にも組み込まれているので
ユーザーであれば使用することができる。