// // File: hzCron.cpp // // Legal Notice: This file is part of the HadronZoo C++ Class Library. Copyright 2025 HadronZoo Project (http://www.hadronzoo.com) // // The HadronZoo C++ Class Library is free software: You can redistribute it, and/or modify it under the terms of the GNU Lesser General Public License, as published by the Free // Software Foundation, either version 3 of the License, or any later version. // // The HadronZoo C++ Class Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR // A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License along with the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses. //
#include <iostream>
#include <sys/time.h>
#include <stdarg.h>
#include "hzChars.h" #include "hzErrcode.h" #include "hzTmplSet.h" #include "hzTmplMapM.h" #include "hzProcess.h" #include "hzCron.h"
/* ** Variables */
global const char* _hzGlobal_Periodicities [] = { "Never", "Every Day", "Mon - Sat", "Weekdays only", "Every Monday", "Every Tuesday", "Every Wednesday", "Every Thursday", "Every Friday", "Every Saturday", "Every Sunday", "Every other Monday", "Every other Tuesday", "Every other Wednesday", "Every other Thursday", "Every other Friday", "Every other Saturday", "Every other Sunday", "Monthly", "Bi-Monthly (odd)", "Bi-Monthly (even)", "Quarterly (1)", "Quarterly (2)", "Quarterly (3)", "Half-Yearly (Jan/Jul)", "Half-Yearly (Feb/Aug)", "Half-Yearly (Mar/Sep)", "Half-Yearly (Apr/Oct)", "Half-Yearly (May/Nov)", "Half-Yearly (Jun/Dec)", "Yearly (Jan)", "Yearly (Feb)", "Yearly (Mar)", "Yearly (Apr)", "Yearly (May)", "Yearly (Jun)", "Yearly (Jul)", "Yearly (Aug)", "Yearly (Sep)", "Yearly (Oct)", "Yearly (Nov)", "Yearly (Dec)", "Random (not periodic)", "" } ;
global const char* _hzGlobal_Monthrules [] = { "Not Applicable", "From Era Start Date", "First day of month", "First weekday of month", "First workday of month", "Last day of month", "Last weekday of month", "Last workday of month", "1st Monday", "1st Tuesday", "1st Wednesday", "1st Thursday", "1st Friday", "1st Saturday", "1st Sunday", "2nd Monday", "2nd Tuesday", "2nd Wednesday", "2nd Thursday", "2nd Friday", "2nd Saturday", "2nd Sunday", "3rd Monday", "3rd Tuesday", "3rd Wednesday", "3rd Thursday", "3rd Friday", "3rd Saturday", "3rd Sunday", "4th Monday", "4th Tuesday", "4th Wednesday", "4th Thursday", "4th Friday", "4th Saturday", "4th Sunday", "Last Monday", "Last Tuesday", "Last Wednesday", "Last Thursday", "Last Friday", "Last Saturday", "Last Sunday", "" } ;
global const char* _hzGlobal_Holidays [] = { "Newyear", "Good Friday", "Easter Sunday", "Easter Monday", "Mayday", "Spring", "Summer", "Christmas Day", "Boxing Day", "Newyear in lieu", "Christmas in lieu", "Boxing in lieu", "All UK public", "" } ;
// Actual or calculated holidays global const uint32_t HZ_PUBHOL_NEWYEAR = 0x00000001 ; // First working day of the year global const uint32_t HZ_PUBHOL_GOOD_FRIDAY = 0x00000002 ; // Friday before Easter global const uint32_t HZ_PUBHOL_EASTER_SUNDAY = 0x00000004 ; // Monday before Easter global const uint32_t HZ_PUBHOL_EASTER_MONDAY = 0x00000008 ; // Monday before Easter global const uint32_t HZ_PUBHOL_MAYDAY = 0x00000010 ; // First Monday in May global const uint32_t HZ_PUBHOL_SPRING = 0x00000020 ; // Last Monday in May global const uint32_t HZ_PUBHOL_SUMMER = 0x00000040 ; // Last Monday in August global const uint32_t HZ_PUBHOL_XMAS = 0x00000080 ; // Always Dec 25th global const uint32_t HZ_PUBHOL_BOXING = 0x00000100 ; // Always Dec 26th
// In liue days global const uint32_t HZ_PUBHOL_LUE_NEWYEAR = 0x00001000 ; // First working day of the year global const uint32_t HZ_PUBHOL_LUE_XMAS = 0x00002000 ; // First working day after Dec 25th if this date is not a working day global const uint32_t HZ_PUBHOL_LUE_BOXING = 0x00004000 ; // First working day after Dec 26th if this date is not a working day global const uint32_t HZ_PUBHOL_ALLUK = 0x000071ff ; // All UK public holidays
/* ** Section 1: Non-member periodicity/monthrule text conversion functions */
hzPeriodicity Str2Periodicity (const hzString& P) { // Category: Config // // Convert textual representation of a periodicity (eg from a config file) into a hzPeriodicity enum value. // // Arguments: 1) P String assumed to be the name of a valid hzPeriodicity value // // Returns: Enum hzPeriodicity
uint32_t nIndex ; // Periodicity iterator
if (!P) return HZPERIOD_INVALID ;
for (nIndex = 0 ; _hzGlobal_Periodicities[nIndex] ; nIndex++) { if (P.Equiv(_hzGlobal_Periodicities[nIndex])) return (hzPeriodicity) nIndex ; }
return HZPERIOD_INVALID ; }
hzMonthrule Str2Monthrule (const hzString& R) { // Category: Config // // Convert textual representation of a month-rule (eg from a config file) into a hzMonthrule enum value. // // Arguments: 1) R String assumed to be the name of a valid hzMonthrule value // // Returns: Enum hzMonthrule
uint32_t nIndex ; // Monthrule iterator
if (!R) return HZMONTHRULE_INVALID ;
for (nIndex = 0 ; _hzGlobal_Monthrules[nIndex] ; nIndex++) { if (R == _hzGlobal_Monthrules[nIndex]) return (hzMonthrule) nIndex ; }
return HZMONTHRULE_INVALID ; }
const char* Periodicity2Txt (const hzPeriodicity P) { // Category: Diagnostics // // Convert hzPeriodicity enum value into text (for diagnostic purposes) // // Arguments: 1) P The valid hzPeriodicity value // // Returns: Const reference to hzString as value being text form of the periodicity
if (P < 0 || P > HZPERIOD_INVALID) return _hzGlobal_Periodicities[HZPERIOD_INVALID] ; return _hzGlobal_Periodicities[P] ; }
const char* Monthrule2Txt (const hzMonthrule mrule) { // Category: Diagnostics // // Convert hzMonthrule enum value into text (for diagnostic purposes) // // Arguments: 1) mrule The valid hzMonthrule value // // Returns: Const reference to hzString as value being text form of the month rule
if (mrule < 0 || mrule > HZMONTHRULE_INVALID) return _hzGlobal_Monthrules[HZMONTHRULE_INVALID] ; return _hzGlobal_Monthrules[mrule] ; }
/* ** Holiday calculator */
// FnGrp: IsHoliday // // Category: Timer/Scheduling // // Establish if a given date is a public holiday or a public holiday in lieu. The function returns 0 if the date is not a public holiday or a positive value if // it is. The value is bitwise to identify the particularly holiday and so this function depends on there not being too many public holidays being declared. // // The returned value will normally have only one bit set (equate to only one holiday). However if the date is a holiday that could be in lieu but happens not // to be on this occasion, then the flags will have two bits set - ie it will represent two holidays, one for the holiday and one for it's lieu equivelent. // // There is a logic to this bizare approach. It allows for applications to differentiate between the two. For example one may have a schedule for weekdays and // another for Saturdays and yet another for Sundays and bank holidays. And a completely different schedule for particular holidays such as Christmas day. In // this instance the application would need to know the difference between Christmas day falling on a weekend and it's in lieu equivelent.
uint32_t IsHoliday (hzSDate& date, uint32_t holFlags) { // Arguments: 1) date Short form date // 2) holFlags By default, this will select UK public holidays // // Returns: >0 A bitwise value indicating which holiday the given date is // 0 If the given date is not a holiday
_hzfunc(__func__) ;
static hzMapM<hzSDate,uint32_t> hols ; // Known public holidays static hzSet<uint32_t> hol_year ; // Holidays calculated for year
hzXDate moon ; // Date & time of full moon hzXDate limt ; // Run-up to full moon date hzSDate test ; // Date to test if moon date is Good Friday/Easter Sunday uint32_t Lo ; // First possible holiday date uint32_t Hi ; // Last possible holiday date uint32_t theday = 0 ; // Holiday found uint32_t nIndex ; // Holiday iterator
if (!holFlags) holFlags = HZ_PUBHOL_ALLUK ;
if (!hol_year.Exists(date.Year())) { // If we have not yet calculated the holidays for the supplied year, do so now. hol_year.Insert(date.Year()) ;
// New year test.SetDate(date.Year(), 1, 1) ; hols.Insert(test, HZ_PUBHOL_NEWYEAR) ; if (test.Dow() == 5) test += 2 ; if (test.Dow() == 6) test += 1 ; hols.Insert(test, HZ_PUBHOL_LUE_NEWYEAR) ;
// Calc Easter. Sunday after full moon after spring Equinox (March 21st) moon.SetDateTime("20120109083006") ; limt.SetDate(date.Year(), 3, 21) ; limt.SetTime(0, 0, 0) ;
for (; moon <= limt ; moon.altdate(SECOND, 2541298)) ; for (; moon.Dow() != 6 ; moon.altdate(DAY, 1)) ; test.SetDate(date.Year(), moon.Month(), moon.Day()) ; test -= 2 ; hols.Insert(test, HZ_PUBHOL_GOOD_FRIDAY) ; test += 2 ; hols.Insert(test, HZ_PUBHOL_EASTER_SUNDAY) ; test += 1 ; hols.Insert(test, HZ_PUBHOL_EASTER_MONDAY) ;
// Mayday, First Monday in May test.SetDate(date.Year(), 5, 1) ; if (test.Dow() == 5) test += 2 ; if (test.Dow() == 6) test += 1 ; hols.Insert(test, HZ_PUBHOL_MAYDAY) ;
// Last Monday in May test.SetDate(date.Year(), 5, 31) ; for (; test.Dow() ; test -= 1) ; hols.Insert(test, HZ_PUBHOL_SPRING) ;
// Last Monday in August test.SetDate(date.Year(), 5, 31) ; for (; test.Dow() ; test -= 1) ; hols.Insert(test, HZ_PUBHOL_SUMMER) ;
// Always Dec 25th test.SetDate(date.Year(), 12, 25) ; hols.Insert(test, HZ_PUBHOL_XMAS) ; if (test.Dow() == 5) test += 2 ; if (test.Dow() == 6) test += 1 ; hols.Insert(test, HZ_PUBHOL_LUE_XMAS) ;
// Boxing day, always Dec 25th test.SetDate(date.Year(), 12, 26) ; hols.Insert(test, HZ_PUBHOL_BOXING) ; if (test.Dow() == 5) test += 2 ; if (test.Dow() == 6) test += 1 ; hols.Insert(test, HZ_PUBHOL_LUE_BOXING) ; }
Lo = hols.First(date) ; if (Lo < 0) return false ; Hi = hols.Last(date) ;
for (nIndex = Lo ; nIndex <= Hi ; nIndex++) theday |= hols.GetObj(nIndex) ;
return theday ; }
uint32_t IsHoliday (hzXDate& date, uint32_t holFlags) { // Arguments: 1) date Full form date // 2) holFlags By default, this will select UK public holidays // // Returns: >0 A bitwise value indicating which holiday the given date is // 0 If the given date is not a holiday
hzSDate sd ; // Short form date
sd.SetDate(date.NoDays()) ; return IsHoliday(sd, holFlags) ; }
/* ** Section 2: hzCron functions */
hzEcode hzCron::Initialize (hzSDate& start, hzPeriodicity period, hzMonthrule mrule) { // hzCron::Initialize // // Set up start of era date if Y, M and D are non-zero and if the periodicity requires them. // // Arguments: 1) start Ths short form date that marks the earliest date for which tasks can be generated // 2) period The applicable periodicity // 3) mrule The Monthrule augments the periodicity // // Returns: E_NOINIT If the settings have not been set up or set up correctly // E_OK If the periodicity and monthrule settings are a viable combination
_hzfunc("hzCron::Initialize") ;
m_Era = start ;
if (period == HZPERIOD_NEVER) { m_error = "Periodicity not set" ; return E_NOINIT ; }
m_Period = period ;
return Validate() ; }
void hzCron::Exclude (uint32_t phFlags) { // Exclude from this hzCron's schedule, one or more public holidays (that are represented as 'HZ_PUBHOL' flags) // // Arguments: 1) phFlags Public Holiday to be excluded // // Returns: None
m_exclhols |= phFlags ; }
void hzCron::Exclude (uint32_t month, uint32_t day) { // Exclude from this hzCron's schedule, dates in the year beyond the supplied month and day // // Arguments: 1) month The month part of a month/day exclusion // 2) day The day part // // Returns: None
m_frMth = 0 ; m_frDay = 0 ; m_toMth = month ; m_toDay = day ; }
void hzCron::Exclude (uint32_t fromMonth, uint32_t fromDay, uint32_t toMonth, uint32_t toDay) { // Exclude from this hzCron's schedule, dates in the year before the supplied month and day (args 1 and 2) and after the supplied month and // day (args 3 and 4) // // Arguments: 1) fromMonth The month part of a month/day specifying start of annual exclusion period // 2) fromDay The day part of the start // 3) toMonth The month part of a month/day specifying end of the annual exclusion period // 4) toDay The day part of the end // // Returns: None
m_frMth = fromMonth ; m_frDay = fromDay ; m_toMth = toMonth ; m_toDay = toDay ; }
hzEcode hzCron::Exclude (const hzString& arg) { // Exclude from this hzCron's schedule, dates described in the supplied control string. (Can be used directly to interpret config files). // // Arguments: 1) arg Exclude directive eg "New Year" // // Returns: E_ARGUMENT If the supplied control string is empty // E_BADVALUE If the month is not 1-12 or day is not valid for the month // E_OK If the operation was successful
_hzfunc("hzCron::Exclude") ;
hzString S ; // Temp string
if (!arg) return E_ARGUMENT ;
// Deal with arg of the form mm:dd if (IsDigit(arg[0])) { if (IsDigit(arg[1]) && arg[1] == CHAR_COLON && IsDigit(arg[1]) && IsDigit(arg[1])) { m_toMth = (10 * (arg[0] - '0')) + (arg[1] - '0') ; m_toDay = (10 * (arg[3] - '0')) + (arg[4] - '0') ;
if (!m_toMth || m_toMth > 12) return E_BADVALUE ; if (m_toMth == 2 && m_toDay > 29) return E_BADVALUE ;
if ((m_toMth == 4 || m_toMth == 6 || m_toMth == 9 || m_toMth == 11) && m_toDay > 30) return E_BADVALUE ; if (m_toMth > 31) return E_BADVALUE ;
return E_OK ; }
return E_FORMAT ; }
// Deal with specific holidays S = arg ; S.ToLower() ;
if (arg == "new year") m_exclhols |= HZ_PUBHOL_NEWYEAR ; else if (arg == "good friday") m_exclhols |= HZ_PUBHOL_GOOD_FRIDAY ; else if (arg == "easter sunday") m_exclhols |= HZ_PUBHOL_EASTER_SUNDAY ; else if (arg == "easter monday") m_exclhols |= HZ_PUBHOL_EASTER_MONDAY ; else if (arg == "mayday") m_exclhols |= HZ_PUBHOL_MAYDAY ; else if (arg == "spring") m_exclhols |= HZ_PUBHOL_SPRING ; else if (arg == "summer") m_exclhols |= HZ_PUBHOL_SUMMER ; else if (arg == "xmas day") m_exclhols |= HZ_PUBHOL_XMAS ; else if (arg == "boxing day") m_exclhols |= HZ_PUBHOL_BOXING ; else if (arg == "new year lieu") m_exclhols |= HZ_PUBHOL_LUE_NEWYEAR ; else if (arg == "xmas lieu") m_exclhols |= HZ_PUBHOL_LUE_XMAS ; else if (arg == "boxing lieu") m_exclhols |= HZ_PUBHOL_LUE_BOXING ; else if (arg == "all uk public") m_exclhols |= HZ_PUBHOL_ALLUK ; else return E_BADVALUE ;
return E_OK ; }
hzEcode hzCron::Exclude (const hzString& from, const hzString& to) { // Exclude from this hzCron's schedule, dates described in the supplied control strings 'from' and 'to'. (Can be used directly to interpret // config files). // // Arguments: 1) from Early Exclude directive eg "New Year" // 2) to Later Exclude directive // // Returns: E_ARGUMENT If the supplied control string is empty // E_BADVALUE If the month is not 1-12 or day is not valid for the month // E_OK If the operation was successful
_hzfunc("hzCron::Exclude") ;
uint32_t M ; // Month uint32_t D ; // Day
if (!from && !to) return E_NODATA ; if (!from || !to) return E_BADVALUE ; if (from.Length() != 5 || to.Length() != 5) return E_BADVALUE ;
if (IsDigit(from[0]) && IsDigit(from[1]) && from[2] == CHAR_COLON && IsDigit(from[3]) && IsDigit(from[4]) && IsDigit(to[0]) && IsDigit(to[1]) && to[2] == CHAR_COLON && IsDigit(to[3]) && IsDigit(to[4])) { // The 'from' date M = (10 * (from[0] - '0')) + (from[1] - '0') ; D = (10 * (from[3] - '0')) + (from[4] - '0') ;
if (!M || M > 12) return E_BADVALUE ; if (M == 2 && D > 29) return E_BADVALUE ;
if ((M == 4 || M == 6 || M == 9 || M == 11) && D > 30) return E_BADVALUE ; if (D > 31) return E_BADVALUE ;
m_frMth = M ; m_frDay = D ;
// The 'to' date M = (10 * (to[0] - '0')) + (to[1] - '0') ; D = (10 * (to[3] - '0')) + (to[4] - '0') ;
if (!M || M > 12) return E_BADVALUE ; if (M == 2 && D > 29) return E_BADVALUE ;
if ((M == 4 || M == 6 || M == 9 || M == 11) && D > 30) return E_BADVALUE ; if (D > 31) return E_BADVALUE ;
m_toMth = M ; m_toDay = D ; return E_OK ; }
return E_FORMAT ; }
hzEcode hzCron::Validate (void) { // Validate the hzCron settings. The following tests are performed:- // // 1) The periodicity must be set. // // 2) If the periodicty is every two weeks, then an era start date must be provided. This is because the DoW (day of week) function could // not unambiquously trigger the event. // // 3) If the periodicty is monthly or less often, there must be a month-rule to define at which point in the month, the trigger applies. // // Arguments: None // // Returns: E_NOINIT If the settings have not been set up or set up correctly // E_OK If the settings pass all the tests
_hzfunc("hzCron::Validate") ;
hzEcode rc = E_OK ; // Return code
switch (m_Period) { case HZPERIOD_DAY: // Every day", case HZPERIOD_MONSAT: // Mon - Sat", case HZPERIOD_WEEKDAY: // Weekdays only case HZPERIOD_EMON: // Every Monday case HZPERIOD_ETUE: // Every Tuesday case HZPERIOD_EWED: // Every Wednesday case HZPERIOD_ETHR: // Every Thursday case HZPERIOD_EFRI: // Every Friday case HZPERIOD_ESAT: // Every Saturday case HZPERIOD_ESUN: // Every Sunday break ;
case HZPERIOD_ALT_MON: // Every other Monday case HZPERIOD_ALT_TUE: // Every other Tuesday case HZPERIOD_ALT_WED: // Every other Wednesday case HZPERIOD_ALT_THR: // Every other Thursday case HZPERIOD_ALT_FRI: // Every other Friday case HZPERIOD_ALT_SAT: // Every other Saturday case HZPERIOD_ALT_SUN: // Every other Sunday if (!m_Era.IsSet()) { rc = E_NOINIT ; m_error = "\tFortnightly invokations must have an era start date otherwise weeks are ambiguous" ; } break ;
case HZPERIOD_MONTH: // Monthly case HZPERIOD_MONTH1: // Bi-Monthly (odd) case HZPERIOD_MONTH2: // Bi-Monthly (even) case HZPERIOD_QTR1: // Quarterly (1) case HZPERIOD_QTR2: // Quarterly (2) case HZPERIOD_QTR3: // Quarterly (3) case HZPERIOD_HYEAR1: // Half-Yearly (1) case HZPERIOD_HYEAR2: // Half-Yearly (2) case HZPERIOD_HYEAR3: // Half-Yearly (3) case HZPERIOD_HYEAR4: // Half-Yearly (4) case HZPERIOD_HYEAR5: // Half-Yearly (5) case HZPERIOD_HYEAR6: // Half-Yearly (6) case HZPERIOD_YEAR1: // Yearly (Jan) case HZPERIOD_YEAR2: // Yearly (Feb) case HZPERIOD_YEAR3: // Yearly (Mar) case HZPERIOD_YEAR4: // Yearly (Apr) case HZPERIOD_YEAR5: // Yearly (May) case HZPERIOD_YEAR6: // Yearly (Jun) case HZPERIOD_YEAR7: // Yearly (Jul) case HZPERIOD_YEAR8: // Yearly (Aug) case HZPERIOD_YEAR9: // Yearly (Sep) case HZPERIOD_YEAR10: // Yearly (Oct) case HZPERIOD_YEAR11: // Yearly (Nov) case HZPERIOD_YEAR12: // Yearly (Dec) case HZPERIOD_RANDOM: // Completly Random if (m_Rule == HZMONTHRULE_INVALID && !m_Era.IsSet()) { rc = E_NOINIT ; m_error = "Periods of a month or more must have a valid monthrule" ; } break ;
default: m_error = "Periodicity not set" ; rc = E_NOINIT ; }
if (rc == E_OK) m_bActive = true ;
return rc ; }
hzEcode hzCron::TestDate (hzSDate& D) { // Apply rules to see if the supplied date is a running day // // Arguments: 1) D Short form date // // Returns: E_NOINIT If this hzCron is not initialized // E_RANGE If the date is out of range // E_OK If the supplied date is a running day
_hzfunc("hzCron::TestDate") ;
hzXDate tmp ; // Current system time and date uint32_t _mon ; // First/last Monday uint32_t _tue ; // First/last Tuesday uint32_t _wed ; // First/last Wednesday uint32_t _thr ; // First/last Thursday uint32_t _fri ; // First/last Friday uint32_t _sat ; // First/last Saturday uint32_t _sun ; // First/last Sunday uint32_t dow = D.Dow() ; // Day of week of test date uint32_t day = D.Day() ; // Day of month of test date uint32_t mon = D.Month() ; // Month of year of test date uint32_t mlen ; // Length of month
if (!m_bActive) { m_error = "Interval not validated/initialized" ; return E_NOINIT ; }
switch (m_Period) { case HZPERIOD_NEVER: return E_RANGE ; case HZPERIOD_RANDOM: return E_OK ; case HZPERIOD_DAY: return E_OK ; case HZPERIOD_MONSAT: return dow == 6 ? E_RANGE : E_OK ; case HZPERIOD_WEEKDAY: return dow < 5 ? E_OK : E_RANGE ; case HZPERIOD_EMON: return dow == 0 ? E_OK : E_RANGE ; case HZPERIOD_ETUE: return dow == 1 ? E_OK : E_RANGE ; case HZPERIOD_EWED: return dow == 2 ? E_OK : E_RANGE ; case HZPERIOD_ETHR: return dow == 3 ? E_OK : E_RANGE ; case HZPERIOD_EFRI: return dow == 4 ? E_OK : E_RANGE ; case HZPERIOD_ESAT: return dow == 5 ? E_OK : E_RANGE ; case HZPERIOD_ESUN: return dow == 6 ? E_OK : E_RANGE ;
case HZPERIOD_ALT_MON: // Every other Monday case HZPERIOD_ALT_TUE: // Every other Tuesday case HZPERIOD_ALT_WED: // Every other Wednesday case HZPERIOD_ALT_THR: // Every other Thursday case HZPERIOD_ALT_FRI: // Every other Friday case HZPERIOD_ALT_SAT: // Every other Saturday case HZPERIOD_ALT_SUN: // Every other Sunday if (m_Era.IsSet() && !((D.NoDays() - m_Era.NoDays()) % 14)) return E_OK ; return E_RANGE ; default: break ; }
// Check if we are in a month in which it will run
if (m_Period == HZPERIOD_MONTH1 && (!(mon % 2))) return E_RANGE ; if (m_Period == HZPERIOD_MONTH2 && (mon % 2)) return E_RANGE ; if (m_Period == HZPERIOD_QTR1 && (mon % 3) != 1) return E_RANGE ; if (m_Period == HZPERIOD_QTR2 && (mon % 3) != 2) return E_RANGE ; if (m_Period == HZPERIOD_QTR3 && (mon % 3) != 0) return E_RANGE ; if (m_Period == HZPERIOD_HYEAR1 && (mon % 6) != 1) return E_RANGE ; if (m_Period == HZPERIOD_HYEAR2 && (mon % 6) != 2) return E_RANGE ; if (m_Period == HZPERIOD_HYEAR3 && (mon % 6) != 3) return E_RANGE ; if (m_Period == HZPERIOD_HYEAR4 && (mon % 6) != 4) return E_RANGE ; if (m_Period == HZPERIOD_HYEAR5 && (mon % 6) != 5) return E_RANGE ; if (m_Period == HZPERIOD_HYEAR6 && (mon % 6) != 0) return E_RANGE ; if (m_Period == HZPERIOD_YEAR1 && mon != 1) return E_RANGE ; if (m_Period == HZPERIOD_YEAR2 && mon != 2) return E_RANGE ; if (m_Period == HZPERIOD_YEAR3 && mon != 3) return E_RANGE ; if (m_Period == HZPERIOD_YEAR4 && mon != 4) return E_RANGE ; if (m_Period == HZPERIOD_YEAR5 && mon != 5) return E_RANGE ; if (m_Period == HZPERIOD_YEAR6 && mon != 6) return E_RANGE ; if (m_Period == HZPERIOD_YEAR7 && mon != 7) return E_RANGE ; if (m_Period == HZPERIOD_YEAR8 && mon != 8) return E_RANGE ; if (m_Period == HZPERIOD_YEAR9 && mon != 9) return E_RANGE ; if (m_Period == HZPERIOD_YEAR10 && mon != 10) return E_RANGE ; if (m_Period == HZPERIOD_YEAR11 && mon != 11) return E_RANGE ; if (m_Period == HZPERIOD_YEAR12 && mon != 12) return E_RANGE ;
// We are in a month that it will run so check if rules will allow run on supplied date
if (m_Rule == HZMONTHRULE_ERA_DERIVE && day != m_Era.Day()) return E_RANGE ; if (m_Rule == HZMONTHRULE_FIRST_DAY && day != 1) return E_RANGE ;
mlen = monlen(D.Year(), mon) ;
if (m_Rule == HZMONTHRULE_LAST_DAY && day != mlen) return E_RANGE ;
tmp.SetDate(D.Year(), mon, 1) ;
switch (tmp.Dow()) { case 0: _mon = 1 ; _tue = 2 ; _wed = 3 ; _thr = 4 ; _fri = 5 ; _sat = 6 ; _sun = 7 ; break ; case 1: _mon = 7 ; _tue = 1 ; _wed = 2 ; _thr = 3 ; _fri = 4 ; _sat = 5 ; _sun = 6 ; break ; case 2: _mon = 6 ; _tue = 7 ; _wed = 1 ; _thr = 2 ; _fri = 3 ; _sat = 4 ; _sun = 5 ; break ; case 3: _mon = 5 ; _tue = 6 ; _wed = 7 ; _thr = 1 ; _fri = 2 ; _sat = 3 ; _sun = 4 ; break ; case 4: _mon = 4 ; _tue = 5 ; _wed = 6 ; _thr = 7 ; _fri = 1 ; _sat = 2 ; _sun = 3 ; break ; case 5: _mon = 3 ; _tue = 4 ; _wed = 5 ; _thr = 6 ; _fri = 7 ; _sat = 1 ; _sun = 2 ; break ; case 6: _mon = 2 ; _tue = 3 ; _wed = 4 ; _thr = 5 ; _fri = 6 ; _sat = 7 ; _sun = 1 ; break ; }
if (m_Rule == HZMONTHRULE_FIRST_WEEK_DAY && day != _mon) // First of month if (m_Rule == HZMONTHRULE_FIRST_WORK_DAY && day != _mon) // First work day of month (must impliment national holidays)
if (m_Rule == HZMONTHRULE_1ST_MON && day != _mon) return E_RANGE ; if (m_Rule == HZMONTHRULE_1ST_TUE && day != _tue) return E_RANGE ; if (m_Rule == HZMONTHRULE_1ST_WED && day != _wed) return E_RANGE ; if (m_Rule == HZMONTHRULE_1ST_THR && day != _thr) return E_RANGE ; if (m_Rule == HZMONTHRULE_1ST_FRI && day != _fri) return E_RANGE ; if (m_Rule == HZMONTHRULE_1ST_SAT && day != _sat) return E_RANGE ; if (m_Rule == HZMONTHRULE_1ST_SUN && day != _sun) return E_RANGE ;
if (m_Rule == HZMONTHRULE_2ND_MON && day != (_mon+7)) return E_RANGE ; if (m_Rule == HZMONTHRULE_2ND_TUE && day != (_tue+7)) return E_RANGE ; if (m_Rule == HZMONTHRULE_2ND_WED && day != (_wed+7)) return E_RANGE ; if (m_Rule == HZMONTHRULE_2ND_THR && day != (_thr+7)) return E_RANGE ; if (m_Rule == HZMONTHRULE_2ND_FRI && day != (_fri+7)) return E_RANGE ; if (m_Rule == HZMONTHRULE_2ND_SAT && day != (_sat+7)) return E_RANGE ; if (m_Rule == HZMONTHRULE_2ND_SUN && day != (_sun+7)) return E_RANGE ;
if (m_Rule == HZMONTHRULE_3RD_MON && day != (_mon+14)) return E_RANGE ; if (m_Rule == HZMONTHRULE_3RD_TUE && day != (_tue+14)) return E_RANGE ; if (m_Rule == HZMONTHRULE_3RD_WED && day != (_wed+14)) return E_RANGE ; if (m_Rule == HZMONTHRULE_3RD_THR && day != (_thr+14)) return E_RANGE ; if (m_Rule == HZMONTHRULE_3RD_FRI && day != (_fri+14)) return E_RANGE ; if (m_Rule == HZMONTHRULE_3RD_SAT && day != (_sat+14)) return E_RANGE ; if (m_Rule == HZMONTHRULE_3RD_SUN && day != (_sun+14)) return E_RANGE ;
if (m_Rule == HZMONTHRULE_4TH_MON && day != (_mon+21)) return E_RANGE ; if (m_Rule == HZMONTHRULE_4TH_TUE && day != (_tue+21)) return E_RANGE ; if (m_Rule == HZMONTHRULE_4TH_WED && day != (_wed+21)) return E_RANGE ; if (m_Rule == HZMONTHRULE_4TH_THR && day != (_thr+21)) return E_RANGE ; if (m_Rule == HZMONTHRULE_4TH_FRI && day != (_fri+21)) return E_RANGE ; if (m_Rule == HZMONTHRULE_4TH_SAT && day != (_sat+21)) return E_RANGE ; if (m_Rule == HZMONTHRULE_4TH_SUN && day != (_sun+21)) return E_RANGE ;
tmp.SetDate(D.Year(), mon, monlen(D.Year(), mon)) ;
switch (tmp.Dow()) { case 0: _mon = mlen ; _tue = mlen-6 ; _wed = mlen-5 ; _thr = mlen-4 ; _fri = mlen-3 ; _sat = mlen-2 ; _sun = mlen-1 ; break ; case 1: _mon = mlen-1 ; _tue = mlen ; _wed = mlen-6 ; _thr = mlen-5 ; _fri = mlen-4 ; _sat = mlen-3 ; _sun = mlen-2 ; break ; case 2: _mon = mlen-2 ; _tue = mlen-1 ; _wed = mlen ; _thr = mlen-6 ; _fri = mlen-5 ; _sat = mlen-4 ; _sun = mlen-3 ; break ; case 3: _mon = mlen-3 ; _tue = mlen-2 ; _wed = mlen-1 ; _thr = mlen ; _fri = mlen-6 ; _sat = mlen-5 ; _sun = mlen-4 ; break ; case 4: _mon = mlen-4 ; _tue = mlen-5 ; _wed = mlen-6 ; _thr = mlen-1 ; _fri = mlen ; _sat = mlen-6 ; _sun = mlen-5 ; break ; case 5: _mon = mlen-5 ; _tue = mlen-4 ; _wed = mlen-5 ; _thr = mlen-6 ; _fri = mlen-1 ; _sat = mlen ; _sun = mlen-6 ; break ; case 6: _mon = mlen-6 ; _tue = mlen-5 ; _wed = mlen-4 ; _thr = mlen-3 ; _fri = mlen-2 ; _sat = mlen-1 ; _sun = mlen ; break ; }
//if (m_Rule == HZMONTHRULE_LAST_WEEK_DAY, // Last weekday of month //if (m_Rule == HZMONTHRULE_LAST_WORK_DAY, // Last workday of month
if (m_Rule == HZMONTHRULE_LAST_MON && day != _mon) return E_RANGE ; if (m_Rule == HZMONTHRULE_LAST_TUE && day != _tue) return E_RANGE ; if (m_Rule == HZMONTHRULE_LAST_WED && day != _wed) return E_RANGE ; if (m_Rule == HZMONTHRULE_LAST_THR && day != _thr) return E_RANGE ; if (m_Rule == HZMONTHRULE_LAST_FRI && day != _fri) return E_RANGE ; if (m_Rule == HZMONTHRULE_LAST_SAT && day != _sat) return E_RANGE ; if (m_Rule == HZMONTHRULE_LAST_SUN && day != _sun) return E_RANGE ;
return E_OK ; }
hzEcode hzCron::TestDate (hzXDate& D) { // Apply rules to see if date is a running day // // Arguments: 1) D Short form date // // Returns: E_NOINIT If this hzCron is not initialized // E_RANGE If the date is out of range // E_OK If the supplied date is a running day
hzXDate f ; // Test date
f.SetDate(D.Year(), D.Month(), D.Day()) ; return TestDate(f) ; }
hzEcode hzCron::Advance (hzXDate& date, uint32_t factor) { // From the supplied date, advance to the Nth next valid date & time for the cron entry, where N is the supplied factor. Skip over // excluded periods or dates. // // Arguments: 1) date Full date and time. If this is not set it will be set to the current date and time. // 2) factor No of intervals to advance // // Returns: E_ARGUMENT If the number of intervals to advance is zero // E_NOINIT If this hzCron is not active or cannot be advanced // E_OK If this hzCron set the date of the next occurance
_hzfunc("hzCron::Advance(hzXDate)") ;
hzSDate fr ; // From start of exclusion period hzSDate to ; // To end of exclusion period hzSDate sd ; // System date uint32_t Y ; // Year uint32_t M ; // Month uint32_t D ; // Day of month uint32_t dow ; // Day of week uint32_t alt ; // Days to advance bool bExcl ; // Date is excluded
if (!m_bActive) { date.Clear() ; m_error = "Interval not active" ; return E_RANGE ; } if (m_Period == HZPERIOD_NEVER) { date.Clear() ; m_error = "Interval has no periodicity" ; return E_RANGE ; } if (m_Period == HZPERIOD_INVALID) { date.Clear() ; m_error = "Interval has invalid periodicity" ; return E_RANGE ; } if (m_Period == HZPERIOD_RANDOM) { date.Clear() ; m_error = "Interval is chaotic" ; return E_RANGE ; }
if (!factor) { m_error = "Factor must be 1 or more" ; return E_ARGUMENT ; }
if (!date.IsSet()) date.SysDateTime() ;
do { Y = date.Year() ; M = date.Month() ; D = date.Day() ; dow = date.Dow() ;
// Every day if (m_Period == HZPERIOD_DAY) { alt = 1 ; date.altdate(DAY, alt) ; }
// Mon - Sat", else if (m_Period == HZPERIOD_MONSAT) { alt = dow==5 ? 2 : 1 ; date.altdate(DAY, alt) ; }
// Weekdays only else if (m_Period == HZPERIOD_WEEKDAY) { alt = dow==4 ? 3 : dow==5 ? 2 : 1 ; date.altdate(DAY, alt) ; }
// Every [Monday - Sunday] else if (m_Period == HZPERIOD_EMON) { alt = 7-dow ; date.altdate(DAY, alt) ; } else if (m_Period == HZPERIOD_ETUE) { alt = dow==1 ? 7 : dow<1 ? 1 : 8-dow ; date.altdate(DAY, alt) ; } else if (m_Period == HZPERIOD_EWED) { alt = dow==2 ? 7 : dow<2 ? 2-dow : 9-dow ; date.altdate(DAY, alt) ; } else if (m_Period == HZPERIOD_ETHR) { alt = dow==3 ? 7 : dow<3 ? 3-dow : 10-dow ; date.altdate(DAY, alt) ; } else if (m_Period == HZPERIOD_EFRI) { alt = dow==4 ? 7 : dow<4 ? 4-dow : 11-dow ; date.altdate(DAY, alt) ; } else if (m_Period == HZPERIOD_ESAT) { alt = dow==5 ? 7 : dow<5 ? 5-dow : 6 ; date.altdate(DAY, alt) ; } else if (m_Period == HZPERIOD_ESUN) { alt = dow==6 ? 7 : 6-dow ; date.altdate(DAY, alt) ; }
// Every other [Monday - Sunday] else if (m_Period == HZPERIOD_ALT_MON) { alt = 7-dow ; alt += 7 ; date.altdate(DAY, alt) ; } else if (m_Period == HZPERIOD_ALT_TUE) { alt = dow==1 ? 7 : dow<1 ? 1 : 8-dow ; alt += 7 ; date.altdate(DAY, alt) ; } else if (m_Period == HZPERIOD_ALT_WED) { alt = dow==2 ? 7 : dow<2 ? 2-dow : 9-dow ; alt += 7 ; date.altdate(DAY, alt) ; } else if (m_Period == HZPERIOD_ALT_THR) { alt = dow==3 ? 7 : dow<3 ? 3-dow : 10-dow ; alt += 7 ; date.altdate(DAY, alt) ; } else if (m_Period == HZPERIOD_ALT_FRI) { alt = dow==4 ? 7 : dow<4 ? 4-dow : 11-dow ; alt += 7 ; date.altdate(DAY, alt) ; } else if (m_Period == HZPERIOD_ALT_SAT) { alt = dow==5 ? 7 : dow<5 ? 5-dow : 6 ; alt += 7 ; date.altdate(DAY, alt) ; } else if (m_Period == HZPERIOD_ALT_SUN) { alt = dow==6 ? 7 : 6-dow ; alt += 7 ; date.altdate(DAY, alt) ; }
else { // Periodicities that are multiples of months
switch (m_Period) { // Monthly case HZPERIOD_MONTH: M++ ; break ;
// Bi-monthly [odd/even] case HZPERIOD_MONTH1: case HZPERIOD_MONTH2: alt = M % 2 ; M += alt ? alt : 2 ; break ;
// Quarterly [month1 - month3] case HZPERIOD_QTR1: case HZPERIOD_QTR2: case HZPERIOD_QTR3: alt = M % 3 ; M += alt ? alt : 3 ; break ;
// Half yearly [month1 - month6] case HZPERIOD_HYEAR1: case HZPERIOD_HYEAR2: case HZPERIOD_HYEAR3: case HZPERIOD_HYEAR4: case HZPERIOD_HYEAR5: case HZPERIOD_HYEAR6: alt = M % 6 ; M += alt ? alt : 6 ; break ;
// Every [Jan - Dec] case HZPERIOD_YEAR1: case HZPERIOD_YEAR2: case HZPERIOD_YEAR3: case HZPERIOD_YEAR4: case HZPERIOD_YEAR5: case HZPERIOD_YEAR6: case HZPERIOD_YEAR7: case HZPERIOD_YEAR8: case HZPERIOD_YEAR9: case HZPERIOD_YEAR10: case HZPERIOD_YEAR11: case HZPERIOD_YEAR12: Y++ ; break ; }
if (M > 12) { Y++ ; M -= 12 ; } date.SetDate(Y, M, 1) ; }
// Have arrived at a possible date. Check if this is not excluded. If it is we do not decrement the factor bExcl = false ;
if (m_frMth) { // An exclusion period is in force sd.SetDate(date.Year(), date.Month(), date.Day()) ; fr.SetDate(date.Year(), m_frMth, m_frDay) ; to.SetDate(date.Year(), m_toMth, m_toDay) ;
if (sd >= fr && sd <= to) bExcl = true ; }
if (!m_frMth && m_toMth) { if (M == m_toMth && D == m_toDay) bExcl = true ; }
if (IsHoliday(date, m_exclhols)) bExcl = true ;
if (!bExcl) factor-- ; } while (factor) ;
return E_OK ; }
hzEcode hzCron::Advance (hzSDate& d, uint32_t factor) { // Set the trigger date to the next date according to the periodicity and rules // // Arguments: 1) date Full date and time // 2) factor No of intervals to advance // // Returns: E_ARGUMENT If the number of intervals to advance is zero // E_NOINIT If this hzCron is not active or cannot be advanced // E_OK If this hzCron set the date of the next occurance
_hzfunc("hzCron::Advance(hzSDate)") ;
hzXDate fullDate ; // Translate the supplied short form date to a date and time (midnight) hzEcode rc ; // Return code
fullDate.SetDate(d.Year(), d.Month(), d.Day()) ; rc = Advance(fullDate, factor) ; if (rc == E_OK) d.SetDate(fullDate.Year(), fullDate.Month(), fullDate.Day()) ; return rc ; }
hzEcode hzCron::Retard (hzXDate& date, uint32_t factor) { // Set the trigger date to the previous date according to the periodicity and rules // // Arguments: 1) date Full date and time // 2) factor No of intervals to retard // // Returns: E_ARGUMENT If the number of intervals to retard is zero // E_NOINIT If this hzCron is not active or cannot be retarded // E_OK If this hzCron set the date of the previous occurance
_hzfunc("hzCron::Advance(hzXDate)") ;
uint32_t Y ; // Year uint32_t M ; // Month uint32_t dow ; // Day of week uint32_t alt ; // No of days to retard
if (!m_bActive) { date.Clear() ; m_error = "Interval not active" ; return E_NOINIT ; }
if (factor < 1) { m_error = "Factor must be 1 or more" ; return E_ARGUMENT ; }
if (!date.IsSet()) date.SysDateTime() ;
do { Y = date.Year() ; M = date.Month() ; dow = date.Dow() ;
// Every day if (m_Period == HZPERIOD_DAY) { alt = 1 ; date.altdate(DAY, -alt) ; }
// Mon - Sat", else if (m_Period == HZPERIOD_MONSAT) { alt = dow==0 ? 2 : 1 ; date.altdate(DAY, -alt) ; }
// Weekdays only else if (m_Period == HZPERIOD_WEEKDAY) { alt = dow>4 ? dow - 4 : dow ? 1 : 3 ; date.altdate(DAY, -alt) ; }
// Every [Monday - Friday] else if (m_Period == HZPERIOD_EMON) { alt = dow>0 ? dow : 7 ; date.altdate(DAY, -alt) ; } else if (m_Period == HZPERIOD_ETUE) { alt = dow>1 ? dow - 1 : dow + 6 ; date.altdate(DAY, -alt) ; } else if (m_Period == HZPERIOD_EWED) { alt = dow>2 ? dow - 2 : dow + 5 ; date.altdate(DAY, -alt) ; } else if (m_Period == HZPERIOD_ETHR) { alt = dow>3 ? dow - 3 : dow + 4 ; date.altdate(DAY, -alt) ; } else if (m_Period == HZPERIOD_EFRI) { alt = dow>4 ? dow - 4 : dow + 3 ; date.altdate(DAY, -alt) ; } else if (m_Period == HZPERIOD_ESAT) { alt = dow>5 ? dow - 5 : dow + 2 ; date.altdate(DAY, -alt) ; } else if (m_Period == HZPERIOD_ESUN) { alt = dow + 1 ; date.altdate(DAY, -alt) ; }
// Every other [Monday - Friday] else if (m_Period == HZPERIOD_ALT_MON) { alt = dow>0 ? dow : 7 ; alt += 7 ; date.altdate(DAY, -alt) ; } else if (m_Period == HZPERIOD_ALT_TUE) { alt = dow>1 ? dow - 1 : dow + 6 ; alt += 7 ; date.altdate(DAY, -alt) ; } else if (m_Period == HZPERIOD_ALT_WED) { alt = dow>2 ? dow - 2 : dow + 5 ; alt += 7 ; date.altdate(DAY, -alt) ; } else if (m_Period == HZPERIOD_ALT_THR) { alt = dow>3 ? dow - 3 : dow + 4 ; alt += 7 ; date.altdate(DAY, -alt) ; } else if (m_Period == HZPERIOD_ALT_FRI) { alt = dow>4 ? dow - 4 : dow + 3 ; alt += 7 ; date.altdate(DAY, -alt) ; } else if (m_Period == HZPERIOD_ALT_SAT) { alt = dow>5 ? dow - 5 : dow + 2 ; alt += 7 ; date.altdate(DAY, -alt) ; } else if (m_Period == HZPERIOD_ALT_SUN) { alt = dow + 1 ; alt += 7 ; date.altdate(DAY, -alt) ; }
else { switch (m_Period) { // Monthly case HZPERIOD_MONTH: M-- ; break ;
// Bi-monthly [odd/even] case HZPERIOD_MONTH1: case HZPERIOD_MONTH2: alt = M % 2 ; M -= alt ? alt : 2 ; break ;
// Quarterly [month1 - month3] case HZPERIOD_QTR1: case HZPERIOD_QTR2: case HZPERIOD_QTR3: alt = M % 3 ; M -= alt ? alt : 3 ; break ;
// Half yearly [month1 - month6] case HZPERIOD_HYEAR1: case HZPERIOD_HYEAR2: case HZPERIOD_HYEAR3: case HZPERIOD_HYEAR4: case HZPERIOD_HYEAR5: case HZPERIOD_HYEAR6: alt = M % 6 ; M -= alt ? alt : 6 ; break ;
// Every [Jan - Dec] case HZPERIOD_YEAR1: case HZPERIOD_YEAR2: case HZPERIOD_YEAR3: case HZPERIOD_YEAR4: case HZPERIOD_YEAR5: case HZPERIOD_YEAR6: case HZPERIOD_YEAR7: case HZPERIOD_YEAR8: case HZPERIOD_YEAR9: case HZPERIOD_YEAR10: case HZPERIOD_YEAR11: case HZPERIOD_YEAR12: Y-- ; break ; }
if (M < 1) { Y-- ; M += 12 ; } date.SetDate(Y, M, 1) ; }
// Have arrived at a possible date. Check if this is not excluded. If it is we do not decrement the factor if (!IsHoliday(date, m_exclhols)) factor-- ; } while (factor) ;
return E_OK ; }
hzEcode hzCron::Retard (hzSDate& d, uint32_t factor) { // Set the trigger date to the previous date according to the periodicity and rules // // Arguments: 1) date Short form date // 2) factor No of intervals to retard // // Returns: E_ARGUMENT If the number of intervals to retard is zero // E_NOINIT If this hzCron is not active or cannot be retarded // E_OK If this hzCron set the date of the previous occurance
_hzfunc("hzCron::Retard(hzSDate)") ;
hzXDate fullDate ; // Translate the supplied short form date to a date and time (midnight) hzEcode rc ; // Return code
fullDate.SetDate(d.Year(), d.Month(), d.Day()) ; rc = Retard(fullDate, factor) ; if (rc == E_OK) d.SetDate(fullDate.Year(), fullDate.Month(), fullDate.Day()) ; return rc ; }