/* ------------------------------------------------------------------ */ /* Unlike Perl, REXX has no predefined, global date/time variables */ /* [e.g. '$year', '$mday' etc]. Instead, native date/time support is */ /* offered by two built-in functions [BIFs]: */ /* */ /* * DATE([option_out [, date [, option_in]]]) */ /* * TIME([option_out [, time [, option_in]]]) */ /* */ /* These BIFs operate in two modes: */ /* */ /* * Generate a date/time string formatted according to 'option_out' */ /* using the current date/time */ /* */ /* * Accept a date/time string in format specified by 'option_in', and*/ /* use it to generate a string as specified by 'option_out' */ /* */ /* Typical REXX code will see either direct use of these BIF's along */ /* with parsing / formatting code, or more likely, used as part of */ /* custom date/time routines. It is this latter approach that will be */ /* used here as most of the examples use routines from the REXXToolkit*/ /* Library [see Appendix for details]. */ /* */ /* It is worth mentioning that REXX, unlike Perl, and other languages,*/ /* sports no 'date' or 'time' type / structure / object, and that such*/ /* values are always represented as strings. It is, however, quite a */ /* simple matter to implement functionally equivalent entities. This */ /* has been done: a 'super'-string, the Date-Time-String [DTS], is */ /* used in many of the examples. While useful in itself the motivation*/ /* for implementing it has been to show how type / structure / object */ /* can be implemented procedurally using non-mutable strings. */ /* ------------------------------------------------------------------ */ |
/* ------------------------------------------------------------------ */ /* The usual way of obtaining the current date is to invoke the DATE()*/ /* BIF to generate a recognised date format, parse, then manipulate, */ /* the resulting string as required. */ /* */ /* However since additional string manipulation of the resulting item */ /* is usually required, it is usual to see custom native REXX routines*/ /* implemented for such tasks. The examples make use of such as set of*/ /* routines [REXXToolkit Library - see appendix for details] */ /* ------------------------------------------------------------------ */ /* Generate, then parse, date string in 'standard' format: YYYYMMDD */ parse value DATE('S') with year +4 month +2 day /* ----------------------------- */ /* Formatting via basic string manipulation */ SEP = SPACE say "The current date is" year || SEP || month || SEP || day SEP = DATESEP say "The current date is" year || SEP || month || SEP || day /* ----------------------------- */ /* Using 'makeYMD' helper function */ say "The current date is" makeYMD(SPACE, year, month, day) say "The current date is" makeYMD(SPACE) say "The current date is" makeYMD(DATESEP, year, month, day) say "The current date is" makeYMD(DATESEP) say "The current date is" makeYMD() /* Optional date validity check */ current = makeYMD() if current == NULL then ; current = "*invalid date*" say "The current date is" current /* Alternative date validity check */ current = makeYMD() if \isYMD(current) then ; current = "*invalid date*" say "The current date is" current /* ----------------------------- */ /* Using 'localtime' and 'YMDHMS...' helper functions */ say "The current date is" YMDHMS2YMD(EPS2YMDHMS(localtime()), SPACE) say "The current date is" YMDHMS2YMD(EPS2YMDHMS(localtime())) /* ----------------------------- */ /* Using 'strftime' helper function */ say "The current date is" strftime("+%Y %m %d") say "The current date is" strftime("+%Y-%m-%d") |
/* ------------------------------------------------------------------ */ /* UNIX-derived languages Perl and C store date/time values as 32 bit */ /* entities - epoch seconds [seconds since Jan 1, 1970]. This provides*/ /* for a simple, efficient, easily manipuable, and readily convertable*/ /* format [i.e. minimal storage use, easy to perform date arithmetic].*/ /* */ /* Since all REXX values are strings, and all arithmetic is string- */ /* based, there are no efficiency benefits in doing the same in REXX. */ /* However, since epoch second use is so widespread, the 'DATE' BIF */ /* supports it via the 'T' option, thus allowing for some of the same */ /* Perl / C techniques to be mimiced in REXX. */ /* ------------------------------------------------------------------ */ /* Get current date/time in Epoch Seconds */ /* Local timezone */ say "Epoch seconds:" DATE('T') /* REXX BIF */ say "Epoch seconds:" localtime() /* Custom routines */ say "Epoch seconds:" strftime("+%s") /* UTC */ say "Epoch seconds:" gmtime() /* ----------------------------- */ /* Convert YMDHMS-formatted current date/time to Epoch Seconds */ ymdhms = makeYMDHMS(makeYMD(), makeHMS()) /* Convert to Epoch Seconds [alternative: localtime(ymdhms)] */ eps = YMDHMS2EPS(ymdhms) /* ----------------------------- */ /* Convert YMDHMS-formatted date/time to Epoch Seconds */ /* Literal string in YMDHMS format */ ymdhms = "2004-04-17 13:03:55" /* Alternatively, make YMDHMS-formatted date/time from components */ y = 2004 ; mth = 4 ; d = 17 ; h = 13 ; m = 3 ; s = 55 ymdhms = makeYMDHMS(makeYMD(DATESEP, y, mth, d),, makeHMS(TIMESEP, h, m, s)) /* Convert to Epoch Seconds [alternative: localtime(ymdhms)] */ eps = YMDHMS2EPS(ymdhms) |
/* ------------------------------------------------------------------ */ /* See comments in previous section header */ /* ------------------------------------------------------------------ */ /* Convert Epoch Seconds to date/time [YMDHMS-format] */ eps = localtime() /* or: gmtime(), or DATE('T') */ /* Convert to YMDHMS */ ymdhms = EPS2YMDHMS(eps) /* ----------------------------- */ /* Parse YMDHMS into components, format and print */ parse var ymdhms, year (DATESEP) month (DATESEP) day, hour (TIMESEP) minute (TIMESEP) second fmtdate = hour || TIMESEP || minute || TIMESEP || second || "-" ||, year || "/" || month || "/" || day say "Dateline:" fmtdate /* ----------------------------- */ /* Use helpers to suitably format date for printing */ fmtdate = YMDHMS2HMS(ymdhms, TIMESEP) || "-" || YMDHMS2YMD(ymdhms, "/") say "Dateline:" fmtdate |
/* ------------------------------------------------------------------ */ /* Date/time arithmetic may be performed: */ /* */ /* * Directly on date/time components */ /* * Converting to a base value, performing arithmetic, then back to */ /* date/time format */ /* */ /* REXX supports base value conversion approach via 'DATE' and 'TIME' */ /* BIFs, and does so in two ways: */ /* */ /* * Base Date Method */ /* * UNIX-derived 'epoch seconds' Method */ /* */ /* The latter method is widely used so will not be described, but the */ /* examples well illustrate its usage. The 'base date' method is day */ /* based [days since 1 Jan 0001 AD], and quite simple to use if date */ /* arithmetic is day-based. If finer granularity is needed then both */ /* the 'DATE' and 'TIME' BIF's must be used making this method less */ /* convenient to use. Despite this, it is the method of choice if */ /* cross-platform portability is a concern as not all REXX interpreter*/ /* are guaranteed to support the 'epoch seconds' method. */ /* ------------------------------------------------------------------ */ when = now + difference then = now - difference /* ----------------------------- */ /* Helper function use: 'dateOffset', and 'dateInterval' */ now = YMDHMS2EPS(makeYMDHMS(makeYMD(DATESEP, 2003, 8, 6), makeHMS())) diff1 = dateOffset("day=1") ; diff2 = dateOffset("weeks=-2") say "One day in the future is:" EPS2YMDHMS(now + diff1) say "Two weeks in the past is:" EPS2YMDHMS(now + diff2) d1 = YMDHMS2EPS(makeYMDHMS(makeYMD(DATESEP, 2003, 8, 6),, makeHMS(TIMESEP))) d2 = YMDHMS2EPS(makeYMDHMS(makeYMD(DATESEP, 2000, 8, 6),, makeHMS(TIMESEP))) interval = d1 - d2 say "Interval - weeks:" dateInterval("weeks", interval) say "Interval - days:" dateInterval("days", interval) say "Interval - hours:" dateInterval("hours", interval) say "Interval - minutes:" dateInterval("minutes", interval) say "Interval - seconds:" dateInterval("seconds", interval) /* ----------------------------- */ /* Epoch second-based arithmetic */ /* 18th January, 1973 3:45:50 am */ birthtime = YMDHMS2EPS(makeYMD(DATESEP, 1973, 1, 18), makeHMS(TIMESEP, 3, 45, 50)) interval = 5 + , /* 5 seconds */ 17 * 60 + , /* 17 minutes */ 2 * 60 * 60 + , /* 2 hours */ 55 * 60 * 60 * 24 /* 55 days */ then = birthtime + interval say "Then is:" YMDHMS2UNIX(EPS2YMDHMS(then)) /* ----------- */ /* REXX 'Base Date'-based arithmetic */ /* 18th January, 1973 3:45:50 am */ bday = DATE('B', "19730118", 'S') ; btime = TIME('S', "03:45:50", 'N') interval_days = 55 ; interval_secs = 5 + 17 * 60 + 2 * 3600 then_days = bday + interval_days ; then_secs = btime + interval_secs parse value DATE('S', then_days, 'B'), TIME('N', then_secs, 'S'), LEFT(DATE('W', then_days, 'B'), 3), LEFT(DATE('M', then_days, 'B'), 3) with, year +4 month +2 day +2 hms downame monthname say "Then is:" downame monthname day hms year /* ----------------------------- */ /* 18th January, 1973 3:45:50 am */ birth = YMDHMS2EPS(makeYMD(DATESEP, 1973, 1, 18), makeHMS(TIMESEP, 3, 45, 50)) say "Nat was 55 days old on:", YMD2US(YMDHMS2YMD(EPS2YMDHMS(birth + dateOffset("days=55"))),," / ") /* ----------- */ /* 18th January, 1973 3:45:50 am */ bday = DATE('B', "19730118", 'S') parse value DATE('S', bday + 55, 'B') with year +4 month +2 day +2 say "Nat was 55 days old on:" month "/" day "/" year |
/* ------------------------------------------------------------------ */ /* See comments in previous section header */ /* ------------------------------------------------------------------ */ when = now + difference then = now - difference /* ----------------------------- */ bree = YMDHMS2EPS(makeYMD(DATESEP, 1981, 6, 16), makeHMS(TIMESEP, 4, 35, 25)) nat = YMDHMS2EPS(makeYMD(DATESEP, 1973, 1, 18), makeHMS(TIMESEP, 3, 45, 50)) diff = bree - nat say "There were" dateInterval("minutes", diff) "minutes", "between Nat and Bree" /* ----------- */ say "There were" dateInterval("weeks", diff) "weeks,", diff // dateOffset("weeks=1") % dateOffset("days=1") || ", days,", S2HMS(diff // dateOffset("days=1")) "between Nat and Bree" /* ----------- */ say "There were" dateInterval("days", diff) "days", "between Nat and Bree" |
/* ------------------------------------------------------------------ */ /* The REXX-idiomatic approach to this task is to perform arithmetic */ /* using the value obtained from the relevant 'DATE' BIF call. */ /* However, as with many other date/time tasks, packaging them as */ /* custom routines allows them to be more reliably, and conveniently */ /* performed. */ /* ------------------------------------------------------------------ */ /* REXX-idiomatic approach using 'DATE' BIF */ day_of_week = DATE('B') // 7 + 1 day_of_week = DATE('B', YMDHMS2EPS(ymdhms), 'T') // 7 + 1 day_of_year = DATE('D') day_of_year = DATE('D', YMDHMS2EPS(ymdhms), 'T') week_of_year = day_of_year % 7 + 1 /* ----------------------------- */ /* DTS Format ['extractDTS' / 'updateDTS' indices below]: yyyy-mm-dd hh:mm:ss +HHMM DOWNAME DOW DOY WOY EPS 1 2 3 4 5 6 7 8 9 10 11 12 */ dts = makeDTS(makeYMD(), makeHMS()) day_of_week = extractDTS(dts, 9) day_of_year = extractDTS(dts, 10) week_of_year = extractDTS(dts, 11) /* ----------------------------- */ day_of_week = strftime("+%w") day_of_year = strftime("+%j") week_of_year = strftime("+%W") /* ----------- */ day_of_week = getDOW() day_of_year = getDOY() week_of_year = getWOY() /* ----------------------------- */ ymdhms = makeYMDHMS(makeYMD(DATESEP, 1981, 6, 16), makeHMS(TIMESEP)) say YMDHMS2YMD(ymdhms, "/") "was a" getDOWName(ymdhms) say "in week" getWOY(ymdhms) || "." /* ----------- */ dts = makeDTS(makeYMD(DATESEP, 1981, 6, 16), makeHMS(TIMESEP)) say YMDHMS2YMD(DTS2YMDHMS(dts), "/") "was a" extractDTS(dts, 8) say "in week" extractDTS(dts, 11) || "." say YMDHMS2YMD(DTS2YMDHMS(dts), "/") "was a" getDOWName(dts) say "in week" getWOY(dts) || "." |
/* ------------------------------------------------------------------ */ /* REXX date/time validation can be performed a few ways: */ /* */ /* * Make 'DATE' / 'TIME' BIF calls, and check whether a SYNTAX */ /* condition is generated [indicating a 'bad' date/time value] */ /* * Parse date/time values, and check individual component values */ /* * Regular expressions [via 'RxRe' external library] */ /* */ /* The first approach is easy to implement, but probably best used */ /* to create custom validation routines rather than in inline code. */ /* This is because condition-checking requires the use of SIGNAL, and */ /* jumping to labels - such code is best quarantined within a routine */ /* body. */ /* */ /* The second approach sees the PARSE instruction used to break up */ /* date/time strings into components which are then conditionally */ /* tested. In short, very typical procedural code, of which several */ /* examples appear below. Of particular note is an implementation of */ /* the C-derived 'strptime' function, a very convenient validation */ /* routine. */ /* */ /* The third approach is language-neutral, and is available in most */ /* modern languages. Not all REXX interpreters can be expected to */ /* offer it since it depends on external library availability */ /* ------------------------------------------------------------------ */ /* Custom Validation Routine [REXXToolkit] Examples */ date = "1998-06-03" if \isYMD(date) then ; say "*invalid date*" time = "22:19:34" if \isHMS(date) then ; say "*invalid time*" /* ----------- */ parse var date yyyy (DATESEP) mm (DATESEP) dd if \acceptableYMDValues(yyyy, mm, dd) then ; say "*invalid date*" parse var time h (TIMESEP) m (TIMEEP) s if \acceptableHMSValues(h, m, s) then ; say "*invalid time*" /* ----------------------------- */ /* 'strptime' Examples */ /* *** incomplete *** */ /* ----------------------------- */ /* Regex-based Examples */ /* *** incomplete *** */ |
/* ------------------------------------------------------------------ */ /* Formatting date/time values for printing or other output purposes */ /* sees 'raw' components [e.g. year, month, etc] variously converted, */ /* and combined to meet requirements. Since such components are just */ /* strings in REXX, it amounts to no more than a string manipulation */ /* exercise. As such, no specialised date formatting facilities are */ /* offered - it is left to the programmer to perform as they see fit. */ /* A few things are worth mentioning: */ /* */ /* * The 'DATE' and 'TIME' BIF's allow some inter-format conversions */ /* but are too limited to be of much practical value on their own - */ /* additional string manipulation is nearly always required */ /* */ /* * The more common date formatting, conversion and validation tasks */ /* are packaged as native REXX 'helper' routines in the REXXToolkit */ /* */ /* * The UNIX-derived [now POSIX standard] 'strftime' facility and */ /* accompanying mini 'date formatting language' have proved to be so*/ /* versatile that a native REXX implementation is offered in the */ /* REXXToolkit's set of date/time routines */ /* ------------------------------------------------------------------ */ /* 'DATE' and 'TIME' examples */ ymd = "20050825" ; hms = "11:08:04" downame = DATE('W', ymd, 'S') ; monthname = DATE('M', ymd, 'S') parse var ymd year +4 month +2 day /* Standard UNIX Format: Thu Aug 25 11:08:04 2005 */ unix = LEFT(downame, 3) LEFT(monthname, 3) day hms year /* As per Perl example */ say "'DATE' gives:" downame month || "/" || day || "/" || RIGHT(year, 2) /* ----------------------------- */ /* 'strftime' examples */ ymdhms = "2005-08-25 11:08:04" /* Standard UNIX Format: Thu Aug 25 11:08:04 2005 */ unix = strftime("+%c", ymdhms) unix = strftime("+%a %b %d %T %Y", ymdhms) /* As per Perl example */ say "strftime gives:" strftime("+%A %D", ymdhms) /* ----------------------------- */ /* Miscellaneous REXXToolkit routines examples */ /* Year-Month-Day Formats */ ymd = makeYMD() /* 2005-08-25 */ say ymd say makeYMD("/") /* 2005/08/25 */ say makeYMD(SPACE) /* 2005 08 25 */ say makeYMD(NULL) /* 20050825 */ /* Other Year-Month-Day Formats */ say YMD2US(ymd) /* 08/25/2005 */ say YMD2UK(ymd) /* 25/08/2005 */ say YMD2ISOWD(ymd) /* 2005-W34-4 */ say YMD2ISODDD(ymd) /* 2005-237 */ /* ----------- */ /* Hour-Minute-Second Formats */ say makeHMS(TIMESEP) /* 00:00:00 */ say makeHMS() /* 11:08:04 */ say makeHMS(".", 11, 8, 4) /* 11.08.04 */ say makeHMS(SPACE, 11, 8, 4) /* 11 08 04 */ say makeHMS(NULL, 11, 8, 4) /* 110804 */ /* ----------- */ /* YMDHMS Examples */ /* 2005-08-25 11:08:04 */ ymdhms = EPS2YMDHMS(localtime()) ymdhms = makeYMDHMS(makeYMD(), makeHMS()) /* Standard ISO Format: 2005-08-25T11:08:04 */ iso = YMDHMS2ISO(ymdhms) iso = CHANGESTR(SPACE, ymdhms, ISOSEP) /* Standard UNIX Format: Thu Aug 25 11:08:04 2005 */ unix = YMDHMS2UNIX(ymdhms) unix = LEFT(getDOWName(ymdhms), 3) LEFT(getMonthName(ymdhms), 3), getDay(ymdhms) YMDHMS2HMS(ymdhms) getYear(ymdhms) /* As per Perl example */ say "YMDHMS gives:" getDOWName(ymdhms) YMD2US(YMDHMS2YMD(ymdhms), 'S') /* ----------- */ /* Date-Time-Structure [DTS] Examples */ /* 2005-08-25 11:08:04 +1000 August Thursday 4 237 34 1124932084 */ dts = makeDTS(makeYMD(), makeHMS()) /* Standard UNIX Format: Thu Aug 25 11:08:04 2005 */ unix = LEFT(extractDTS(dts, 9), 3) LEFT(extractDTS(dts, 8), 3), extractDTS(dts, 3) YMDHMS2HMS(DTS2YMDHMS(dts)) extractDTS(dts, 1) unix = LEFT(getDOWName(dts), 3) LEFT(getMonthName(dts), 3), getDay(dts) YMDHMS2HMS(DTS2YMDHMS(dts)) getYear(dts) /* As per Perl example */ say "DTS gives:" extractDTS(dts, 9), YMD2US(YMDHMS2YMD(DTS2YMDHMS(dts)), 'S') |
/* ------------------------------------------------------------------ */ /* Timing in REXX is usually performed with the 'TIME' BIF, and it may*/ /* take three forms: */ /* */ /* * High resolution [sub-second-based] timing via, TIME('L') */ /* * Low resolution [second-based] timing via, TIME('T') */ /* * Stop watch [second-based] timing via, TIME('R') and TIME('E') */ /* */ /* The precision of high resolution timing is platform-specific, so */ /* caution is needed in interpreting the microsecond-based value */ /* returned from TIME('L') calls. On UNIX / Win32 desktop systems it */ /* is probably safest to assume that values are in the millisecond */ /* range, and no more accurate than about +/- 20 ms. */ /* */ /* Low resolution timing via TIME('T') [i.e. Epoch second] calls is */ /* easily performed, but not all REXX interpreters may offer this */ /* option. */ /* */ /* Stop watch timing is platform-independant, easy to use, hence quite*/ /* widely used; it would qualifiy as a REXX-idiomatic practice */ /* ------------------------------------------------------------------ */ /* High resolution timer [i.e. microsecond (us) granularity] */ /* Start time - time of day ['long' format to us. resolution] */ t1 = TIME('L') /* Perform timed operation(s) here */ /* Stop Time */ t2 = TIME('L') /* Extract us. values, and compute elapsed time in ms.*/ parse var t1 . "." t1us ; parse var t2 . "." t2us elapsed_ms = (t2us - t1us) / 1000 /* For timings likely to exceed 1 second then other time components need to be extracted. The helper function, 'LHMS2S' computes time in fractional seconds [to ms. precision] */ elapsed_s = LHMS2S(t2) - LHMS2S(t1) /* ----------------------------- */ /* Low resolution timer [i.e. second (s) granularity] */ /* Start time - time of day [Epoch seconds] */ t1 = TIME('T') /* Perform timed operation(s) here */ /* Stop Time */ t2 = TIME('T') /* Compute elapsed time in seconds */ elapsed = t2 - t1 /* ----------- */ /* Low resolution 'stopwatch' timer [i.e. second (s) granularity] */ /* Reset timer */ call TIME 'R' /* Perform timed operation(s) here */ /* Seconds since last timer reset */ elapsed = TIME('E') /* ----------------------------- */ /* Load general-purpose functions from external library */ call rxFuncAdd 'sysLoadFuncs', 'rexxUtil', 'sysLoadFuncs' call sysLoadFuncs /* Extract before and after times for operation */ before = TIME('L') call CHAROUT , "Press any key ..." ; call sysGetKey after = TIME('L') /* Compute elapsed time in fractional seconds [to ms. precision] */ elapsed = LHMS2S(after) - LHMS2S(before) say "You took" elapsed "seconds" /* Unload general-purpose functions */ call sysDropFuncs exit 0 /* ----------------------------- */ /* Load general-purpose functions from external library */ call rxFuncAdd 'sysLoadFuncs', 'rexxUtil', 'sysLoadFuncs' call sysLoadFuncs stem.0 = 500 ; repetitions = 100 ; total_time = 0 do repetitions /* Load array */ do i = 1 for stem.0 stem.i = RANDOM() end /* Time sort operation */ start = TIME('L') call sysStemSort 'stem.' stop = TIME('L') /* Extract timings and accumulate */ /* Given the sub-second duration of the operation, the following approach could be used: parse var start . "." start ; parse var stop . "." stop elapsed = (stop - start) / 1000000 However, for maximum safety, best use 'LHMS2S' */ elapsed = LHMS2S(stop) - LHMS2S(start) total_time = total_time + elapsed end say "On average, sorting" stem.0 "random numbers takes", total_time / repetitions "seconds" drop stem. /* Unload general-purpose functions */ call sysDropFuncs exit 0 |
/* ------------------------------------------------------------------ */ /* Given the platform-specific nature of this task, REXX offers no */ /* in-built support for it aside, of course, from allowing a 'busy */ /* wait' loop to be implemented, an approach best avoided if possible.*/ /* */ /* As is typical for such tasks, the needed functionality is obtained */ /* either from a third party library, or by invoking a system command */ /* utility. Examples of each follow. */ /* ------------------------------------------------------------------ */ /* 'busy waiting' using built-in 'TIME' function - *AVOID* */ stoptime = TIME('S') + 3.5 /* 3.5 second pause */ do while TIME('S') < stoptime /* Do nothing, except burn CPU cycles :) ! */ nop end /* ----------------------------- */ /* Invoking 'sysSleep' function: fine granularity, no CPU wastage */ /* Load general-purpose functions from external library */ call rxFuncAdd 'sysLoadFuncs', 'rexxUtil', 'sysLoadFuncs' call sysLoadFuncs call sysSleep 3.5 /* 3.5 second pause */ call sysSleep 0.35 /* 0.35 second pause */ call sysSleep 0.035 /* 0.035 second pause */ /* Unload general-purpose functions */ call sysDropFuncs exit 0 /* ----------------------------- */ /* Command: UNIX / Win32 'sleep' utility, granularity is seconds */ cmd = "sleep" ; seconds = 3 address SYSTEM cmd seconds with OUTPUT STREAM 'NUL:' ERROR STREAM 'NUL:' |