
In a program I wrote recently I needed to return which day of the week today is. My first thought was to use SQL, as it would only be one line of code. But is that the fastest way to return today's day name?
I thought about writing my own RPG routine to calculate the day name, but decided to use Google to see if there was a better example. I found an article by Rafael Victoria-Pereira for MC Press from 2015 that gave what I think is an efficient way to do this, you can read his article here.
Using his example I refactored the RPG code up to 2025 standards and created this procedure:
01 dcl-proc GetTodayRPG export reqproto(*no) ; 02 dcl-pi *n char(10) ; 03 end-pi ; 04 dcl-s DayNbr packed(1 : 0) ; 05 DayNbr = %rem(%diff(%date() : d'0001-01-01' : *days) : 7) ; 06 select DayNbr ; 07 when-is 0 ; 08 return 'Monday' ; 09 when-is 1 ; 10 return 'Tuesday' ; 11 when-is 2 ; 12 return 'Wednesday' ; 13 when-is 3 ; 14 return 'Thursday' ; 15 when-is 4 ; 16 return 'Friday' ; 17 when-is 5 ; 18 return 'Saturday' ; 19 when-is 6 ; 20 return 'Sunday' ; 21 endsl ; 22 end-proc ; |
Line 1: I believe in giving my procedure meaningful names, hence this one is called GetTodayRPG. The REQPROTO(*NO) means that I do not need a procedure prototype definition in this source member.
Lines 2 and 3: This is the procedure interface. No parameters are passed to this procedure, and one of ten characters is returned.
Line 4: I only need a variable for the calculated day number, which is defined here.
Line 5: There are three built in functions in this line. The %DIFF built in function, or BiF, calculates the difference between two dates. In this calculation it is calculating the difference between the current date, %DATE(), and the earliest possible date. The difference is returned in days. The %REM BiF returns the remainer when the days difference is divided by 7. The result of the %REM is placed in the variable DayNbr.
Lines 6 – 21: I am using the updated version of the Select operation code as I think it is easier to understand then the old. On line 6, next to the SELECT, I give the variable that is to be compared to. Thereafter, I use the WHEN-IS and the value to compare to. The day name is returned to the calling program.
My second procedure uses SQL to get the day's name:
01 dcl-proc GetTodaySQL export reqproto(*no) ; 02 dcl-pi *n char(10) ; 03 end-pi ; 04 dcl-s DayName char(10) ; 05 exec sql SET :DayName = DAYNAME(CURRENT_DATE) ; 06 return DayName ; 07 end-proc ; |
Lines 1 – 3: Are the same as the previous procedure.
Line 4: This variable will contain the day's name.
Line 5: I am using the DAYNAME scalar function to return the day's name.
Line 7: I return the value in DayName to the calling program.
Both of these procedures were in the same source member, that I compiled into a module. I added the module's name to the binding directory TESTBNDDIR.
Now I needed a program to perform the test:
01 **free 02 ctl-opt option(*srcstmt) dftactgrp(*no) bnddir('TESTBNDDIR') ; 03 dcl-pr GetTodayRPG char(10) ; 04 end-pr ; 05 dcl-pr GetTodaySQL char(10) ; 06 end-pr ; 07 dcl-s Start timestamp ; 08 dcl-s Difference packed(30 : 0) ; 09 dcl-s Counter uns(10) ; 10 dcl-s Today char(10) ; 11 Start = %timestamp() ; 12 for Counter = 1 to 1000000 ; 13 Today = GetTodayRPG ; 14 endfor ; 15 Difference = %diff(%timestamp : Start : *ms) ; 16 dsply ('RPG diff = ' + %trim(%editc(Difference : 'J'))) ; 17 Start = %timestamp() ; 18 for Counter = 1 to 1000000 ; 19 Today = GetTodaySQL ; 20 endfor ; 21 Difference = %diff(%timestamp : Start : *ms) ; 22 dsply ('SQL diff = ' + %trim(%editc(Difference : 'J'))) ; 23 *inlr = *on ; |
Line 2: I use the BNDDIR control option so that the module I created will be bound to this program.
Lines 3 – 5: These are the procedure prototypes for the two procedures I created.
Lines 7 – 10: The variables this program will be using.
Line 11: Capture the current timestamp.
Lines 12 – 14: This For group is performed one million times. It calls the RPG procedure, returning the day's name to the variable Today.
Line 21: I am using the %DIFF BiF to calculate the difference between the current timestamp and the timestamp before the For group. This is the length of time it took the RPG procedure to execute one million times. I want my result in milliseconds.
Line 22: I display the difference using the Display operation code, DSPLY.
Lines 17 – 22: I do the same as above, except I call the SQL procedure.
I ran this program ten times on a dedicated partition running IBM i 7.5 with Technology Refresh 5. The average of my results was:
- RPG: 4,472,777 milliseconds
- SQL: 515,186,573 milliseconds
The SQL result is almost 8.5 minutes, compared to RPG's 4.5 seconds.
This result shows you need to test to see whether using SQL produces the fastest running programs.
This article was written for IBM i 7.5, and should work for some earlier releases too.
what about CEEDYWK function from the CEE Bindable API?
ReplyDelete//---------------------------------------------------
// CEEDYWK - Lilian Date To Day of the Week
//---------------------------------------------------
Dcl-PR CEEDYWK OpDesc;
@DteLil Int(10:0) Const;
@Day Int(10:0);
@FC Char(12) Options(*Omit);
End-PR;
If you wondering how to get the Lilian Date input, there's also a CEE function for that
DAYOFWEEK(CURRENT DATE)
ReplyDelete