22
Sun, Dec
3 New Articles

Practical RPG: Manipulating Timestamps

RPG
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

Timestamps support flexible date arithmetic, which you can see in this practical example of a task scheduler.

Today's article is going to be very practical indeed! We're going to use timestamps to perform some common business functions and, in so doing, learn a number of simple but effective techniques for working with these versatile variables. This is a real-world example; I am currently running a scheduler using exactly this design.

Do I Really Need to Rewrite the IBM i Job Scheduler?

No, of course you don't, and if that's what I was doing here, I wouldn't waste your time. But powerful though it may be, the default job scheduler provided with the IBM i (using command WRKJOBSCDE) just doesn't have the features that I need.

In fact, before we go any further, let's identify my actual business requirements here. What I'm looking for is the ability to run a specific program on a schedule. Simple enough, but there's an immediate twist: I need two different types of scheduling. First, I need to be able to schedule a program that runs multiple times a day, say every 30 minutes or every four hours. Next, I need to schedule a program that runs once a day at a specific time, but only on certain days of the month.

That's the project. I could try to do this a couple of ways. First, for the looping programs, I'd just set up a CL program. You've probably done one or two of these yourself. It looks like this:

LOOP:      CALL       PROCESS &ENDJOB

             IF         (&ENDJOB = '1') RETURN

             DLYJOB     DLY(3600)

             GOTO       LOOP

Done! The program named PROCESS will run once every 60 minutes! Of course, this is a bit of a problem because I'd have to have different programs for different delays. But there's a more subtle problem as well. This doesn't run once an hour. It runs once every 60 minutes plus the time it takes to run the called program. That is, the delay doesn't start until after the program runs. So if it takes 10 minutes to run the process, then it will actually run again 70 minutes after the time it was previously called. This is an inherent problem with a hard delay.

What We Need Is a "Next Run Time"

Let's say the program ran at 9:00 a.m. We'd want to store the fact that the next runtime would be at 10:00 AM. Then, when it was time to delay, we'd only delay until 10:00 AM. Now, there is a way to do this using DLYJOB; you can delay until a specific time. But remember, we want to be able to schedule multiple jobs, and they may have different delay times. I think we're going to have to go to a database file. Here is file AUTCFG:

R AUTCFGR                  TEXT('Autorun Configuration')

   ACRUTP       10         COLHDG('Run Type')

   ACRUN1         6S 0       COLHDG('Run Value 1')

   ACRUN2      32         COLHDG('Run Value 2')

   ACPGM         10         COLHDG('Program')

   ACLRTS         Z         COLHDG('Last Run')

   ACNRTS         Z         COLHDG('Next Run')

   ACFORC         1         COLHDG('Force')

It's a simple file. It has three values that determine the loop type: run type, run value 1, and run value 2. It has a program name that simply specifies the program to call. And finally, it has the last run and next run timestamps. Because you’re sharp-eyed, I'm sure you also noticed the force field; we'll get into that at the end of all of this.

Anyway, the way this file works is simple. There are two types: *LOOP and *DAILY. Feel free to add more as needed, but this is what we're starting with. *LOOP uses only ACRUN1, which is the number of minutes to delay between calls to ACPGM. *DAILY uses both fields. ACRUN1 is the 6-digit time of day the program will run, while ACRUN2 is an array of Ys and Ns; a Y in position 15 indicates that ACPGM should run on the 15th of every month. Simple enough, right?

So what are the other fields? Ah, this is where we solve the business problem! The Last Run field simply gets updated whenever ACPGM is run; this is just an informational field used to tell us the last run time. That's important when you want to track down the last time a scheduled job actually ran. But it's the Next Run field, ACNRTS, that does the magic. You see, what we're going to do is write a master scheduler that runs through this file once a minute. That program will compare the current timestamp to the next run timestamp, and if it's greater, we're going to run the program! But here's where we get geeky: as soon as we run the program, we're going to then bump the time to what should be the next run. We don't just wait another hour (or whatever); we bump the next run field by an hour, thus making sure that we continue to run consistently.

This Is How We Rock Around the Clock

read AUTCFGP;                

dow not %eof(AUTCFGP);        

                              

if ACFORC = 'Y';            

   run();                    

   update AUTCFGR;          

elseif %timestamp() > ACNRTS;

   run();                    

   bump();                  

   update AUTCFGR;          

endif;                      

                              

read AUTCFGP;              

enddo;

This is the code that is called every minute. We read every record in the file and see if it needs to be run. Notice the first IF statement that checks ACFORC (I bet you were wondering about that field!). This field allows you to force an immediate run of a scheduled job without disrupting the normal scheduling. It will run the scheduled program but not bump it to the next time field. So say you want to run something every hour, but you need to run it halfway in. You can just set ACFORC to Y for that entry and the scheduler will run it, but the regularly scheduled iteration will still run 30 minutes later.

Now we finally come down to the fun of timestamps. First, you'll see that we decide whether or not to run the entry with a very simple comparison: We just compare the current timestamp to the next run timestamp. The beauty of timestamps is that they allow comparison of a complete date and time; this gets around the issues of comparing 12:01 a.m. to 11:59 p.m. of the previous day. Because the date is included in the comparison, it always works.

So we have two cases: If it's a force, then just run the program, but if we've passed the next scheduled run time, then run the program and bump the next scheduled run time.

I won't include the code for the run procedure. You don't need it. In fact, you'll end up writing your own that may submit a job, or send a message to a log file, or create a command string with parameters, or check for and report errors. What the run procedure does is actually irrelevant to the scheduling process!

Things That Go Bump in the Job

The bump procedure, on the other hand, is very important. That's actually the heart of the scheduler, the part that shows just how versatile timestamps can be. It has two pieces: first is a bit of code that will initialize a new or stale record. Records may get stale if the scheduler is held for some length of time. Anyway, the code is straightforward:

// If next run is in past, then initialize to 12AM today

if %date(ACNRTS) < %date();                            

ACNRTS = %timestamp(%date());                        

endif;                                                  

All we're doing is checking the date portion of the next run time. You can easily extract either the date portion or the time portion from a timestamp as shown. If the extracted is earlier than today's date, the timestamp is stale and we initialize it. That's the next cool thing: You can initialize a timestamp using a date. The timestamp will be initialized to 12:00 a.m. (time 00:00:00) of the specified date.

Now we have a valid next run timestamp; we simply need to bump that timestamp until it's past the current time. Why are we doing this? Well, let's take the situation before, where it takes 10 minutes to run the job. If we last ran at 9:00 a.m. and the job took 10 minutes, and then we added 60 minutes to the current time, we'd end up with the next run being at 10:10 a.m. This way, we add 60 minutes to the last scheduled time, so the next scheduled time is 10:00 a.m. But here's the tricky part: What if it takes an hour and 30 minutes, and it's already 10:30 a.m.? Well, if we just add 60 minutes, we'll get 10:00 a.m., so we'll run it immediately. Is that what we want? I don't think so, since we've already just run. So let's see how I handle this for type *LOOP:

// Increment next until past current                    

if ACRUTP = '*LOOP';                                    

dow ACNRTS < ACLRTS;                                  

   ACNRTS += %minutes(ACRUN1);                        

enddo;                                                

In the run procedure, I set ACLRTS, the last run timestamp, to the current timestamp. Now we want to set ACNRTS to some value later than that. What I do is add the number of minutes in ACRUN1 to the next run timestamp until it's greater than the last run timestamp. The += syntax is very nice here; it allows me to get to the timestamp field directly. And even if the process took long enough to skip an entire iteration, this loop will get me to the next valid run time. Work it out yourself: If my loop is 30 minutes and the next run time was 9:00 a.m., and if the program for whatever reason took an hour and 45 minutes, then ACLRTS would be 10:45. If I add 30 minutes to 9:00 a.m., I get 9:30 a.m. Still not big enough. I add 30 more minutes, I get 10:00 a.m. Then 10:30 a.m. Finally, I get to 11:00 a.m. and that becomes the next run time.

And please note that since this is a timestamp, not just a time field, it will automatically work as you cross over the midnight boundary. I'll leave that exercise to you. Instead, I want to focus on the other scheduler function: schedule days of the month. In this case, the second run parameter is a list of the days that something should run. It would look like this:

YNNNNNNNNNNNNNYNNNNNNNNNNNNNNNN

This says that the process should run on the 1st and 15th day of every month. The ACRUN1 parameter is the time of day the process should run on those days, in HHMMSS format. Here's the code:

elseif ACRUTP = '*DAILY';                              

nextDate = %date;                                    

dow ACNRTS < ACLRTS;                                  

   nextDate += %days(1);                              

   if %subst( ACRUN2: %subdt(nextDate:*days): 1) = 'Y';

     ACNRTS = nextDate + %time(ACRUN1:*iso);          

   endif;                                              

enddo;                                                

endif;                                                  

The field nextDate is just a work field of type date. We start with today's date. Then we sit in a loop once again, waiting for the value ACNRTS to be greater than the last run stored in ACLRTS. What we do is add a day to the nextDate field and then see if it's a day that we run the process. We do that by checking the position within ACRUN2 for that day. This is where date arithmetic gets fun; you can easily extract the day number (or any other part of the date) as an integer by using the %subdt BIF. In this case, I get the day number by specifying *days and use that as the offset into the ACRUN2 variable. If that position is a Y, then it's a potential date.

Finally, I build the timestamp by combining the date selected and the time from ACRUN1. Note that you can always create a timestamp just by adding a date and a time together. I use the %time BIF to convert the decimal value in ACRUN1 into an actual time data type, and then I add that to nextDate to get the next timestamp. If it's not big enough, just keep adding a day until it is.

Additional Features

Clearly, there are plenty of other features that could be added here. We could definitely support blackout periods when the process cannot run (for example, from 11:00 p.m. until 2:00 a.m.). We could add a special flag for "the last day of the month". We could add support for days of the week rather than days of the month. But the point is, this design with just one file and a couple of dozen lines of code can provide you a pretty full-featured scheduler.

Have fun with it!

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: