home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 18 REXX / 18-REXX.zip / atrgf31.zip / datergf.cmd < prev    next >
OS/2 REXX Batch file  |  1996-04-30  |  41KB  |  1,072 lines

  1. /*
  2. program: datergf.cmd
  3. type:    REXXSAA-OS/2
  4. purpose: allow transformations and calculations on sorted dates
  5.          (reverse Julian dates into sorted dates)
  6. version: 1.6
  7. date:    1991-05-20
  8. changed: 1992-06-07, RGF, error-message is only displayed on STDERR, if DATERGF was not
  9.                           invoked as a function
  10.          1992-06-18, removed bundling to ATRGF.CMD etc., RGF
  11.          1993-09-20, changed the definition of ANSI-color-sequences; gets them from
  12.                      procedure ScrColor.CMD
  13.          1994-03-03, error-messages are shown, even if called as a function in order
  14.                      to ease debugging
  15.          1996-04-30, error-messages are shown only, if flag "C" was *not* specified
  16.  
  17. author:  Rony G. Flatscher,
  18.          Wirtschaftsuniversität/Vienna
  19.          Rony.Flatscher@wu-wien.ac.at
  20.  
  21. usage:   DATERGF(argument)
  22.          DATERGF(argument, flag)
  23.          DATERGF(argument1, flag, argument2)
  24.          see enclosed Tutorial "RGFSHOW.CMD" and syntax below
  25.  
  26. needs:   SCRCOLOR.CMD
  27.  
  28. remark:  This program reflects the change in 1582, where the calendar
  29.          was corrected by subtracting 10 days (1582/10/05 - 1582/10/14 were
  30.          skipped) by the Roman Catholic pope Gregor XIII:
  31.          0000/01/01 ---> 1581/12/31 ... Julian Calendar (every 4 years leap year)
  32.          1582/01/01 ---> 9999/12/31 ... Gregorian Calendar, with 10 days less in 1582,
  33.                                         every 4 years leap year, EXCEPT whole centuries
  34.                                         which are not dividable MOD 400 = 0
  35.  
  36.  
  37.  
  38. All rights reserved, copyrighted 1991, 1992, no guarantee that it works without
  39. errors, etc. etc.
  40.  
  41. donated to the public domain granted that you are not charging anything
  42. (money etc.) for it and derivates based upon it, as you did not write it,
  43. etc. if that holds you may bundle it with commercial programs too
  44.  
  45. Please, if you find an error, post me a message describing it, I will
  46. try to fix and rerelease it to the net.
  47.  
  48. procedures:
  49.       CHECK_DATE:  check correct date and parse it
  50.       CHECK_TIME:  check correct time and parse it
  51.       DATE2DAYS:   calculate days based on 0000/01/01 (== 1)
  52.       DAYS2DATE:   generate sorted day from given days
  53.       TIME2FRACT:  calculate decimal fraction for time
  54.       FRACT2TIME:  calculate time from given decimal fraction
  55.       DATE2JULIAN: generate Julian date (YYYYDDD)
  56.       JULIAN2DATE: generate sorted date (YYYYMMDD)
  57.       WEEKDAY:     generate dayname and/or dayindex
  58.  
  59.  
  60. syntax:
  61.     SDATE ...... sorted date (YYYYMMDD)
  62.     JDATE ...... Julian date (YYYYDDD)
  63.     DAYS ....... DATERGF-days, if time supplied, time is a decimal fraction
  64.     TIME ....... on input 24hour- (military) or 12hour-format allowed,
  65.                  output will be allways in 24hour-format (military, computer)
  66.     FRACT ...... decimal fraction of time
  67.     SECONDS .... second-value for DAYS.FRACT
  68.  
  69.    Argument1:      Flag: Argument2:        Result:
  70.  
  71.    "SDATE[ TIME]"                          DAYS[.FRACT] since 00000101 inclusive
  72.    "DAYS[.FRACT]", "S"                     SDATE[ TIME24]
  73.    "SDATE[ TIME]", "J"                     JDATE
  74.    "JDATE",        "JR"                    SDATE ("Julian Reversed")
  75.  
  76.    "TIME",         "F"                     FRACT
  77.    "FRACT",        "FR"                    TIME24 ("Fraction Reversed")
  78.  
  79.    "DAYS[.FRACT]", "SEC"                   SECONDS
  80.    "SECONDS",      "SECR"                  DAYS[.FRACT] ("SEConds Reversed")
  81.  
  82.    "SDATE[ TIME]", "-S", "SDATE[ TIME]"    DAYS[.FRACT]
  83.    "SDATE[ TIME]", "-",  "DAYS[.FRACT]"    SDATE[ TIME24]
  84.    "SDATE[ TIME]", "+",  "DAYS[.FRACT]"    SDATE[ TIME24]
  85.  
  86.    "SDATE[ TIME]", "C"                     SDATE if correct, nothing ('') if false
  87.    "SDATE[ TIME]", "L"                     if leap-year, return 1, else 0
  88.  
  89.    "SDATE[ TIME]", "Y"                     check date & return year
  90.    "SDATE[ TIME]", "YB"[, DAYS[.FRACT]]    year begin [+/- DAYS[.FRACT]]
  91.    "SDATE[ TIME]", "YE"[, DAYS[.FRACT]]    year end [+/- DAYS[.FRACT]]
  92.  
  93.    "SDATE[ TIME]", "HY"                    semester index (1-2)
  94.    "SDATE[ TIME]", "HYB"[, DAYS[.FRACT]]   semester begin [+/- DAYS[.FRACT]]
  95.    "SDATE[ TIME]", "HYE"[, DAYS[.FRACT]]   semester end [+/- DAYS[.FRACT]]
  96.  
  97.    "SDATE[ TIME]", "M"                     check date & return month
  98.    "SDATE[ TIME]", "MN"                    monthname
  99.    "SDATE[ TIME]", "MB"[, DAYS[.FRACT]]    month begin [+/- DAYS[.FRACT]]
  100.    "SDATE[ TIME]", "ME"[, DAYS[.FRACT]]    month end [+/- DAYS[.FRACT]]
  101.  
  102.    "SDATE[ TIME]", "D"                     check date & return day
  103.    "SDATE[ TIME]", "DN"                    dayname
  104.    "SDATE[ TIME]", "DI"                    dayindex (1=monday, ..., 7=sunday)
  105.  
  106.    "SDATE[ TIME]", "W"                     week-number
  107.    "SDATE[ TIME]", "WB"[, DAYS[.FRACT]]    week begin (Monday) [+/- DAYS.[FRACT]]
  108.    "SDATE[ TIME]", "WE"[, DAYS[.FRACT]]    week end (Sunday) [+/- DAYS.[FRACT]]
  109.  
  110.    "SDATE[ TIME]", "Q"                     quarter index (1-4)
  111.    "SDATE[ TIME]", "QB"[, DAYS[.FRACT]]    quarter begin [+/- DAYS[.FRACT]]
  112.    "SDATE[ TIME]", "QE"[, DAYS[.FRACT]]    quarter end [+/- DAYS[.FRACT]]
  113.  
  114. */
  115.  
  116.  
  117. IF ARG(1) = ''|ARG(1) = '?' THEN SIGNAL usage
  118.  
  119.  
  120. /* invoked as a COMMAND (from command-line) or as a FUNCTION ? */
  121. PARSE SOURCE . invocation .
  122. invocation_as_function = (invocation = "FUNCTION")
  123.  
  124. IF \invocation_as_function THEN     /* called as COMMAND or SUBROUTINE and not as a FUNCTION! */
  125. DO                                  /* only one argument-string, which has to be parsed ! */
  126.    IF VERIFY(ARG(1), "(", "M") > 0 THEN
  127.       PARSE ARG "(" tmp ")"
  128.    ELSE
  129.       tmp = ARG(1)
  130.  
  131.    PARSE VAR tmp argument1 "," argument2 "," argument3
  132.  
  133.    argument1 = STRIP(argument1)     /* strip blanks */
  134.    argument2 = STRIP(argument2)
  135.    argument3 = STRIP(argument3)
  136.    argument1 = STRIP(argument1,,"'")/* strip ' */
  137.    argument2 = STRIP(argument2,,"'")
  138.    argument3 = STRIP(argument3,,"'")
  139.    argument1 = STRIP(argument1,,'"')/* strip " */
  140.    argument2 = STRIP(argument2,,'"')
  141.    argument3 = STRIP(argument3,,'"')
  142.  
  143.    SELECT                  /* number of arguments */
  144.       WHEN argument3 <> '' THEN argcount = 3
  145.       WHEN argument2 <> '' THEN argcount = 2
  146.       OTHERWISE                 argcount = 1
  147.  
  148.    END
  149.  
  150. END
  151. ELSE     /* invoked as a function */
  152. DO
  153.     argument1 = ARG(1)
  154.     argument2 = ARG(2)
  155.     argument3 = ARG(3)
  156.     argcount  = ARG()      /* number of arguments */
  157. END
  158.  
  159.  
  160. NUMERIC DIGITS 14          /* set precision to 14 digits after comma */
  161.  
  162. PARSE UPPER VAR argument1 date1 time1
  163. flag = TRANSLATE(argument2)
  164. PARSE UPPER VAR argument3 date2 time2
  165.  
  166. /* build monthdays array */
  167. monthdays.1 = 31
  168. monthdays.2 = 28
  169. monthdays.3 = 31
  170. monthdays.4 = 30
  171. monthdays.5 = 31
  172. monthdays.6 = 30
  173. monthdays.7 = 31
  174. monthdays.8 = 31
  175. monthdays.9 = 30
  176. monthdays.10 = 31
  177. monthdays.11 = 30
  178. monthdays.12 = 31
  179.  
  180.  
  181. /* check sorted dates & numbers */
  182.  
  183. IF argument3 <> '' & flag <> '-S' THEN     /* check whether third argument is a valid number */
  184.    IF \DATATYPE(argument3, 'N') THEN
  185.    DO
  186.       errmsg = argument3': not numeric'
  187.       SIGNAL error
  188.    END
  189.  
  190. SELECT
  191.    /* check sorted date and adjust monthdays. array, if necessary */
  192.    WHEN WORDPOS(flag, "S SEC SECR JR F FR") = 0 | flag = '' THEN
  193.         DO
  194.            date1 = check_date(date1)
  195.            IF time1 <> '' THEN time1 = check_time(time1)
  196.         END
  197.  
  198.    WHEN flag = 'F' THEN time1 = check_time(date1)       /* time in hand ? */
  199.  
  200.    OTHERWISE    /* argument1 a positive number ? */
  201.         DO
  202.            IF \DATATYPE(argument1,'N') THEN
  203.            DO
  204.               errmsg = argument1': not numeric'
  205.               SIGNAL error
  206.            END
  207.            ELSE IF argument1 < 0 THEN
  208.            DO
  209.               errmsg = argument1': must be a positive number'
  210.               SIGNAL error
  211.            END
  212.         END
  213. END
  214.  
  215.  
  216. /* act according to flag */
  217. SELECT
  218.    WHEN flag = '' THEN                  /* SDATE: calculate days of date = DAYS */
  219.         DO
  220.            days1 = date2days(date1)
  221.            if time1 <> '' THEN fraction1 = time2fract(time1)
  222.                           ELSE fraction1 = ''
  223.            result_datergf = days1||SUBSTR(fraction1,2)
  224.         END
  225.  
  226.    WHEN flag = 'S' THEN                 /* DAY: calculate date/time = SDATE */
  227.         DO
  228.           IF date1 < 1 THEN
  229.           DO
  230.                 errmsg = argument1 argument2": yields invalid date (< 0000/01/01) !"
  231.                 SIGNAL error
  232.           END
  233.  
  234.           days_int = date1 % 1
  235.           IF days_int > 3652427 THEN    /* > 9999/12/31 ? */
  236.           DO
  237.                 errmsg = argument1 argument2": yields invalid date (> 9999/12/31) !"
  238.                 SIGNAL error
  239.           END
  240.  
  241.           time_only = date1 - days_int
  242.           date = days2date(days_int)      /* get rid of fraction */
  243.  
  244.           IF time_only > 0 THEN date = date fract2time(time_only)
  245.  
  246.           result_datergf = date
  247.         END
  248.  
  249.    WHEN flag = '-S' THEN                /* SDATE - SDATE = DAYS */
  250.         DO
  251.            days1 = date2days(date1)
  252.            IF time1 <> '' THEN fraction1 = time2fract(time1)
  253.                           ELSE fraction1 = ''
  254.            result_datergf1 = days1||SUBSTR(fraction1,2)
  255.  
  256.            /* check date2 and prepare monthdays. */
  257.            date2 = check_date(date2)
  258.            days2 = date2days(date2)
  259.  
  260.            IF time2 <> '' THEN fraction2 = time2fract(check_time(time2))
  261.            ELSE fraction2 = ''
  262.  
  263.            result_datergf2 = days2||SUBSTR(fraction2,2)
  264.            result_datergf = result_datergf1 - result_datergf2
  265.         END
  266.  
  267.    WHEN (flag = '-') | (flag = '+') THEN        /* SDATE-DAYS = SDATE */
  268.         DO
  269.           days1 = date2days(date1)
  270.  
  271.           IF time1 <> '' THEN fraction1 = time2fract(time1)
  272.                          ELSE fraction1 = ''
  273.  
  274.           temp = days1||SUBSTR(fraction1,2)
  275.  
  276.           IF flag = '-' THEN
  277.              temp = temp - date2           /* subtract */
  278.           ELSE
  279.              temp = temp + date2           /* add */
  280.  
  281.           IF temp < 1 | ABS(temp) > 3652427 THEN        /* < 0000/01/01 or > 9999/12/31 ? */
  282.           DO
  283.                errmsg = argument1 argument2 argument3": does not yield a valid date!"
  284.                SIGNAL error
  285.           END
  286.  
  287.           days1 = temp % 1                  /* days since 0000/01/01 inclusive == 1 */
  288.           time1 = temp // 1                 /* time */
  289.           result_datergf = days2date(days1)
  290.  
  291.           IF time1 > 0 THEN
  292.              result_datergf = result_datergf fract2time(time1)
  293.         END
  294.  
  295.    WHEN flag = 'J' THEN                 /* SDATE = JDATE */
  296.         result_datergf = date2julian(date1)
  297.  
  298.    WHEN flag = 'JR' THEN                /* JDATE = SDATE (reverse Julian date) */
  299.         result_datergf = julian2date(date1)
  300.  
  301.    WHEN flag = 'F' THEN                 /* generate FRACT from TIME */
  302.         result_datergf = time2fract(time1)
  303.  
  304.    WHEN flag = 'FR' THEN                /* generate TIME24 from decimal fraction */
  305.         result_datergf = fract2time(date1 // 1)
  306.  
  307.    WHEN flag = 'SEC' THEN               /* generate SECONDS from DAYS[.FRACTION] */
  308.         result_datergf = (date1 * 86400 + 0.5) % 1  /* round to seconds */
  309.  
  310.    WHEN flag = 'SECR' THEN              /* generate DAYS[.FRACTION] from SECONDS */
  311.         result_datergf = date1 / 86400
  312.  
  313.    WHEN flag = 'C' THEN                 /* check date[ time] */
  314.         result_datergf = argument1
  315.  
  316.    WHEN flag = 'L' THEN                 /* is date in a leap year? */
  317.         DO
  318.            year = WORD(date1, 1)
  319.            IF year > 1582 THEN             /* Gregorian calender */
  320.               result_datergf = (((year // 4) = 0) & \((year // 100) = 0)) | ((year // 400)=0)
  321.            ELSE result_datergf = ((year // 4) = 0) /* Julian calender    */
  322.         END
  323.  
  324.    WHEN flag = 'Y' THEN                 /* return year */
  325.         result_datergf = WORD(date1, 1)
  326.  
  327.    WHEN flag = 'YB' | flag = 'YE' THEN     /* return year-begin or -end  */
  328.         DO
  329.            year = WORD(date1, 1)           /* optionally add (negative) days */
  330.  
  331.            IF argcount < 3 THEN               /* no third argument */
  332.            DO
  333.               IF flag = 'YB' THEN result_datergf = year||'0101'
  334.               ELSE result_datergf = year||'1231'
  335.  
  336.               IF time1 <> '' THEN result_datergf = result_datergf TRANSLATE(time1,':',' ')
  337.            END
  338.            ELSE
  339.            DO
  340.               IF flag = 'YB' THEN result_datergf = year '1 1'
  341.               ELSE result_datergf = year '12 31'
  342.  
  343.               result_datergf = date2days(result_datergf)
  344.               IF time1 <> '' THEN result_datergf = result_datergf + time2fract(time1)
  345.               result_datergf = result_datergf + date2
  346.  
  347.               days = result_datergf % 1
  348.               IF days < 1 | ABS(days) > 3652427 THEN    /* < 0000/01/01 or > 9999/12/31 ? */
  349.               DO
  350.                    errmsg = argument1 argument2 argument3": does not yield a valid date!"
  351.                    SIGNAL error
  352.               END
  353.  
  354.               fraction = result_datergf // 1
  355.               result_datergf = days2date(days)
  356.               IF fraction > 0 THEN result_datergf = result_datergf fract2time(fraction)
  357.            END
  358.         END
  359.  
  360.    WHEN flag = 'M' THEN                 /* return month */
  361.         result_datergf = WORD(date1, 2)
  362.  
  363.    WHEN flag = 'MN' THEN                /* return monthname */
  364.         result_datergf = WORD("January February March April May June July",
  365.                  "August September October November December", WORD(date1, 2))
  366.  
  367.    WHEN flag = 'MB' | flag = 'ME' THEN     /* return month-begin or -end  */
  368.         DO
  369.            PARSE VAR date1 year month .    /* optionally add (negative) days */
  370.  
  371.            IF argcount < 3 THEN               /* no third argument */
  372.            DO
  373.               IF flag = 'MB' THEN result_datergf = year||RIGHT(month,2,'0')||'01'
  374.               ELSE IF year = 1582 & month = 10 THEN result_datergf = year||RIGHT(month,2,'0')||'31'
  375.               ELSE result_datergf = year||RIGHT(month,2,'0')||monthdays.month
  376.  
  377.               IF time1 <> '' THEN result_datergf = result_datergf TRANSLATE(time1,':',' ')
  378.            END
  379.            ELSE
  380.            DO
  381.               IF flag = 'MB' THEN result_datergf = year month '1'
  382.               ELSE IF year = 1582 & month = 10 THEN result_datergf = year month '31'
  383.               ELSE result_datergf = year month monthdays.month
  384.  
  385.               result_datergf = date2days(result_datergf)
  386.               IF time1 <> '' THEN result_datergf = result_datergf + time2fract(time1)
  387.               result_datergf = result_datergf + date2
  388.  
  389.               days = result_datergf % 1
  390.               IF days < 1 | ABS(days) > 3652427 THEN    /* < 0000/01/01 or > 9999/12/31 ? */
  391.               DO
  392.                    errmsg = argument1 argument2 argument3": does not yield a valid date!"
  393.                    SIGNAL error
  394.               END
  395.  
  396.               fraction = result_datergf // 1
  397.               result_datergf = days2date(days)
  398.               IF fraction > 0 THEN result_datergf = result_datergf fract2time(fraction)
  399.            END
  400.         END
  401.  
  402.  
  403.    WHEN flag = 'W' THEN                 /* calculate week of year */
  404.         DO
  405.           PARSE VAR date1 year month day
  406.  
  407.           PARSE VALUE weekday(year "1 1", "ALL") WITH days_a d_ia
  408.  
  409.           /* 1. week or old year's week ? */
  410.           IF d_ia > 4 THEN diff = d_ia - 9
  411.           ELSE diff = d_ia - 2
  412.  
  413.           c1 = SUBSTR(date2julian(date1),5,3) + diff
  414.           result_datergf = c1 % 7 + 1           /* number of weeks */
  415.  
  416.           IF result_datergf > 51 THEN           /* last week in year ?, probably 1st week? */
  417.           DO
  418.  
  419.              PARSE VALUE weekday(year "12 31", "ALL") WITH days_0 di0
  420.  
  421.              IF di0 < 4 THEN
  422.                 IF day > (31-di0) THEN result_datergf = 1 /* first week, as 31st smaller than thursday  */
  423.           END
  424.           ELSE IF result_datergf = 1 THEN       /* beginning of January, is it last year's last week ? */
  425.           DO
  426.              IF (day + diff) < 0 THEN
  427.              DO
  428.                 PARSE VALUE weekday(RIGHT(year-1,4,'0') "1 1", "ALL") WITH days_0 di0
  429.  
  430.                 IF di0 > 4 THEN diff2 = di0 - 9 /* second week is first */
  431.                 ELSE diff2 = di0 - 2            /* first week */
  432.  
  433.                 c1 = SUBSTR(date2julian((RIGHT(year-1,4,'0') "12 31")),5,3) + diff2
  434.                 result_datergf  = c1 % 7 + 1            /* number of weeks */
  435.              END
  436.           END
  437.         END
  438.  
  439.    WHEN flag = 'D' THEN                 /* return day */
  440.         result_datergf = WORD(date1, 3)
  441.  
  442.    WHEN flag = 'DN' THEN                /* return dayname */
  443.         result_datergf = weekday(date1)
  444.  
  445.    WHEN flag = 'DI' THEN               /* return dayindex */
  446.         result_datergf = weekday(date1, "I")
  447.  
  448.    WHEN flag = 'WB' | flag = 'WE' THEN     /* return week-begin (MON) or -end (SUN)  */
  449.         DO
  450.            PARSE VALUE weekday(date1, "ALL") WITH tmp di
  451.  
  452.            IF flag = 'WB' THEN diff = 1 - di
  453.            ELSE diff = 7 - di
  454.  
  455.            new_days = tmp + diff
  456.  
  457.            IF argcount < 3 THEN               /* no third argument */
  458.            DO
  459.               IF new_days < 1 THEN         /* 0000/01/01 = THU, no monday available */
  460.               DO
  461.                    errmsg = argument1 argument2": does not yield a valid date!"
  462.                    SIGNAL error
  463.               END
  464.               result_datergf = days2date(new_days)
  465.               IF time1 <> '' THEN result_datergf = result_datergf TRANSLATE(time1,':',' ')
  466.            END
  467.            ELSE
  468.            DO
  469.               IF time1 <> '' THEN result_datergf = new_days + time2fract(time1)
  470.               ELSE result_datergf = new_days
  471.  
  472.               result_datergf = result_datergf + date2
  473.  
  474.               days = result_datergf % 1
  475.               IF days < 1 | ABS(days) > 3652427 THEN    /* < 0000/01/01 or > 9999/12/31 ? */
  476.               DO
  477.                    errmsg = argument1 argument2 argument3": does not yield a valid date!"
  478.                    SIGNAL error
  479.               END
  480.  
  481.               fraction = result_datergf // 1
  482.               result_datergf = days2date(days)
  483.               IF fraction > 0 THEN result_datergf = result_datergf fract2time(fraction)
  484.            END
  485.         END
  486.  
  487.  
  488.    WHEN flag = 'Q' THEN                 /* return quarter */
  489.         DO
  490.            year = WORD(date1, 1)
  491.            tmp = WORD(argument1, 1)           /* sorted date */
  492.            SELECT
  493.               WHEN tmp < year||'0401' THEN result_datergf = 1
  494.               WHEN tmp < year||'0701' THEN result_datergf = 2
  495.               WHEN tmp < year||'1001' THEN result_datergf = 3
  496.               OTHERWISE result_datergf = 4
  497.            END
  498.         END
  499.  
  500.    WHEN flag = 'QB' | flag = 'QE' THEN  /* return quarter-begin or -end  */
  501.         DO                              /* optionally add (negative) days */
  502.            year = WORD(date1, 1)
  503.            tmp = WORD(argument1, 1)        /* sorted date */
  504.  
  505.            IF argcount < 3 THEN            /* no third argument */
  506.            DO
  507.               IF flag = 'QB' THEN       /* quarter begin */
  508.               DO
  509.                  SELECT
  510.                     WHEN tmp < year||'0401' THEN result_datergf = year||'0101'
  511.                     WHEN tmp < year||'0701' THEN result_datergf = year||'0401'
  512.                     WHEN tmp < year||'1001' THEN result_datergf = year||'0701'
  513.                     OTHERWISE result_datergf = year||'1001'
  514.                  END
  515.               END
  516.               ELSE                      /* quarter end */
  517.               DO
  518.                  SELECT
  519.                     WHEN tmp < year||'0401' THEN result_datergf = year||'0331'
  520.                     WHEN tmp < year||'0701' THEN result_datergf = year||'0630'
  521.                     WHEN tmp < year||'1001' THEN result_datergf = year||'0930'
  522.                     OTHERWISE result_datergf = year||'1231'
  523.                  END
  524.               END
  525.  
  526.               IF time1 <> '' THEN result_datergf = result_datergf TRANSLATE(time1,':',' ')
  527.            END
  528.            ELSE
  529.            DO
  530.               IF flag = 'QB' THEN
  531.               DO
  532.                  SELECT
  533.                     WHEN tmp < year||'0401' THEN result_datergf = year  '1 1'
  534.                     WHEN tmp < year||'0701' THEN result_datergf = year  '4 1'
  535.                     WHEN tmp < year||'1001' THEN result_datergf = year  '7 1'
  536.                     OTHERWISE result_datergf = year  '10 1'
  537.                  END
  538.               END
  539.               ELSE
  540.               DO
  541.                  SELECT
  542.                     WHEN tmp < year||'0401' THEN result_datergf = year  '3 31'
  543.                     WHEN tmp < year||'0701' THEN result_datergf = year  '6 30'
  544.                     WHEN tmp < year||'1001' THEN result_datergf = year  '9 30'
  545.                     OTHERWISE result_datergf = year  '12 31'
  546.                  END
  547.               END
  548.  
  549.               result_datergf = date2days(result_datergf)
  550.               IF time1 <> '' THEN result_datergf = result_datergf + time2fract(time1)
  551.               result_datergf = result_datergf + date2
  552.  
  553.               days = result_datergf % 1
  554.               IF days < 1 | ABS(days) > 3652427 THEN    /* < 0000/01/01 or > 9999/12/31 ? */
  555.               DO
  556.                    errmsg = argument1 argument2 argument3": does not yield a valid date!"
  557.                    SIGNAL error
  558.               END
  559.  
  560.               fraction = result_datergf // 1
  561.               result_datergf = days2date(days)
  562.               IF fraction > 0 THEN result_datergf = result_datergf fract2time(fraction)
  563.            END
  564.         END
  565.  
  566.    WHEN flag = 'HY' THEN                /* return semester (1 = 1.half, 2 = 2.half */
  567.            IF WORD(date1, 2) < 7 THEN result_datergf = 1
  568.            ELSE result_datergf = 2
  569.  
  570.    WHEN flag = 'HYB' | flag = 'HYE' THEN   /* return quarter-begin or -end  */
  571.         DO
  572.            PARSE VAR date1 year month .    /* optionally add (negative) days */
  573.  
  574.            IF argcount < 3 THEN               /* no third argument */
  575.            DO
  576.               IF flag = 'HYB' THEN
  577.               DO
  578.                  IF month < 7 THEN result_datergf = year||'0101'
  579.                  ELSE result_datergf = year||'0701'
  580.               END
  581.               ELSE
  582.               DO
  583.                  IF month < 7 THEN result_datergf = year||'0630'
  584.                  ELSE result_datergf = year||'1231'
  585.               END
  586.  
  587.               IF time1 <> '' THEN result_datergf = result_datergf TRANSLATE(time1,':',' ')
  588.            END
  589.            ELSE
  590.            DO
  591.               IF flag = 'HYB' THEN
  592.               DO
  593.                  IF month < 7 THEN result_datergf = year '1 1'
  594.                  ELSE result_datergf = year '7 1'
  595.               END
  596.               ELSE
  597.               DO
  598.                  IF month < 7 THEN result_datergf = year  '6 30'
  599.                  ELSE result_datergf = year '12 31'
  600.               END
  601.  
  602.               result_datergf = date2days(result_datergf)
  603.               IF time1 <> '' THEN result_datergf = result_datergf + time2fract(time1)
  604.               result_datergf = result_datergf + date2
  605.  
  606.               days = result_datergf % 1
  607.               IF days < 1 | ABS(days) > 3652427 THEN    /* < 0000/01/01 or > 9999/12/31 ? */
  608.               DO
  609.                    errmsg = argument1 argument2 argument3": does not yield a valid date!"
  610.                    SIGNAL error
  611.               END
  612.  
  613.               fraction = result_datergf // 1
  614.               result_datergf = days2date(days)
  615.               IF fraction > 0 THEN result_datergf = result_datergf fract2time(fraction)
  616.            END
  617.         END
  618.  
  619.    OTHERWISE
  620.         DO
  621.           errmsg = flag': unknown flag'
  622.           SIGNAL error
  623.         END
  624. END
  625.  
  626. IF invocation_as_function THEN   /* invoked as function, therefore return the value */
  627.    RETURN result_datergf         /* return value */
  628.  
  629. /* invoked from the COMMAND-line or as a SUBROUTINE, both invocations must not return a value */
  630. SAY "DATERGF - result:" result_datergf       /* show result on standard output */
  631. RETURN
  632. /* end of main routine */
  633.  
  634.  
  635.  
  636.  
  637.  
  638.  
  639. /* parse & check arguments */
  640. CHECK_DATE: PROCEDURE EXPOSE monthdays. flag
  641.  
  642.     PARSE ARG 1 year 5 month 7 day 9
  643.  
  644.     IF \DATATYPE(year,'N') THEN
  645.     DO
  646.        errmsg = ARG(1)": year is not numeric"
  647.        SIGNAL error
  648.     END
  649.  
  650.     IF year < 0 THEN
  651.     DO
  652.        errmsg = ARG(1)": year must be 0000 or greater"
  653.        SIGNAL error
  654.     END
  655.  
  656.     /* is year a leap year ? */
  657.     IF year > 1582 THEN                 /* Gregorian calender */
  658.        leap_year = (((year // 4) = 0) & \((year // 100) = 0)) | ((year // 400)=0)
  659.     ELSE leap_year = ((year // 4) = 0)  /* Julian calender    */
  660.  
  661.     monthdays.2 = 28 + leap_year
  662.     IF year = 1582 THEN monthdays.10 = 21       /* 1582: October had 10 days less */
  663.  
  664.     SELECT
  665.        WHEN \DATATYPE(month,'N') THEN
  666.             DO
  667.                errmsg = ARG(1)||": month is not numeric"
  668.                SIGNAL error
  669.             END
  670.        WHEN (month < 1) | (month > 12) THEN
  671.             DO
  672.                errmsg = ARG(1)||": month out of range"
  673.                SIGNAL error
  674.             END
  675.        OTHERWISE
  676.             month = month % 1   /* get rid of leading nulls */
  677.     END
  678.  
  679.     SELECT
  680.        WHEN \DATATYPE(day,'N') THEN
  681.             DO
  682.                errmsg = ARG(1)": day is not numeric"
  683.                SIGNAL error
  684.             END
  685.        WHEN (day < 1) THEN
  686.             DO
  687.                errmsg = ARG(1)": day out of range"
  688.                SIGNAL error
  689.             END
  690.        WHEN year = 1582 & month = 10 THEN    /* Gregorian: 1582, October 1-4, 15-31 */
  691.             DO
  692.                IF (day > 4 & day < 15) | day > 31 THEN
  693.                DO
  694.                   IF day > 31 THEN
  695.                      errmsg = ARG(1)": too many days for given month"
  696.                   ELSE
  697.                      errmsg = ARG(1)": day out of range (1582/10/05-1582/10/14 do not exist)"
  698.                   SIGNAL error
  699.                END
  700.             END
  701.        WHEN day > monthdays.month THEN
  702.             DO
  703.                errmsg = ARG(1)": too many days for given month"
  704.                SIGNAL error
  705.             END
  706.        OTHERWISE
  707.             day = day % 1 /* get rid of leading nulls */
  708.     END
  709.  
  710.     RETURN year month day
  711. /* end of CHECK_DATE */
  712.  
  713.  
  714. /* parse & check time, return 24hour-Time (military time) */
  715. CHECK_TIME: PROCEDURE EXPOSE monthdays.
  716.     PARSE UPPER ARG tmp
  717.     time24 = 1                  /* starting with 24 hour time in mind */
  718.     time12 = POS('M', tmp)      /* AM or PM ? */
  719.     IF time12 > 0 THEN
  720.     DO
  721.       time24 = 0                /* 12 hour time in hand */
  722.       letter = SUBSTR(tmp, time12 - 1, 1)
  723.       IF \((letter = 'A') | letter = 'P') THEN
  724.       DO
  725.          errmsg = ARG(1)': not a valid AM/PM-time'
  726.          SIGNAL error
  727.       END
  728.       tmp = SUBSTR(tmp, 1, time12 - 2)  /* remove ?M */
  729.     END
  730.  
  731.     PARSE VAR tmp hours ':' minutes ':' seconds
  732.  
  733.     SELECT
  734.       WHEN hours = '' THEN hours = 0
  735.       WHEN \datatype(hours,'N') THEN     /* no numeric type */
  736.            DO
  737.               errmsg = ARG(1)": hours are not numeric"
  738.               SIGNAL error
  739.            END
  740.       WHEN (hours < 0) | (hours > 23) THEN      /* out of range    */
  741.            DO
  742.               errmsg = ARG(1)": hours out of range"
  743.               SIGNAL error
  744.            END
  745.       OTHERWISE NOP
  746.     END
  747.  
  748.     SELECT
  749.       WHEN minutes = '' THEN minutes = 0
  750.       WHEN \datatype(minutes,'N') THEN     /* no numeric type */
  751.            DO
  752.               errmsg = ARG(1)": minutes are not numeric"
  753.               SIGNAL error
  754.            END
  755.       WHEN (minutes < 0) | (minutes > 59) THEN /* out of range    */
  756.            DO
  757.               errmsg = ARG(1)": minutes out of range"
  758.               SIGNAL error
  759.            END
  760.       OTHERWISE NOP
  761.     END
  762.  
  763.     SELECT
  764.       WHEN seconds = '' THEN seconds = 0
  765.       WHEN \datatype(seconds,'N') THEN     /* no numeric type */
  766.            DO
  767.               errmsg = ARG(1)": seconds are not numeric"
  768.               SIGNAL error
  769.            END
  770.       WHEN (seconds < 0) | (seconds >= 60) THEN /* out of range    */
  771.            DO
  772.               errmsg = ARG(1)": seconds out of range"
  773.               SIGNAL error
  774.            END
  775.       OTHERWISE NOP
  776.     END
  777.  
  778.     IF \time24 THEN             /* received a 12hour time, adjust it to 24hour time */
  779.     DO
  780.        IF (letter = 'A') & (hours = 12) THEN hours = 0
  781.        ELSE IF ((letter = 'P') & (hours < 12)) THEN hours = hours + 12
  782.     END
  783.  
  784.     RETURN hours  minutes seconds
  785. /* end of CHECK_TIME */
  786.  
  787.  
  788.  
  789. /* calculate days based on 0000/01/01 (= 1. day == 1) */
  790. DATE2DAYS: PROCEDURE  EXPOSE monthdays.
  791.     PARSE ARG year month day
  792.  
  793.     days_1    = year * 365
  794.     leap_days = year % 4
  795.  
  796.     IF year > 0 THEN
  797.     DO
  798.        leap_days = leap_days + 1        /* account for leap year in 0000 */
  799.  
  800.        IF year > 1582 THEN days_1 = days_1 - 10 /* account for 1582, which had 10 days less */
  801.  
  802.        IF year > 1600 THEN         /* account for Gregorian calender */
  803.        DO
  804.            diff = year - 1600
  805.            leap_days = leap_days - (diff % 100 - diff % 400)
  806.            leap_year = (((diff // 4) = 0) & \((diff // 100) = 0)) | ((diff // 400)=0) /* leap year in hand ? */
  807.        END
  808.        ELSE leap_year = ((year // 4) = 0)       /* leap year in hand ? */
  809.  
  810.        leap_days = leap_days - leap_year
  811.     END
  812.  
  813.     days_2 = SUBSTR(date2julian(ARG(1)), 5, 3)
  814.  
  815.     RETURN (days_1 + leap_days + days_2)
  816. /* end of DATE2DAYS */
  817.  
  818.  
  819. DAYS2DATE: PROCEDURE  EXPOSE monthdays. /* calculate sorted day from given days */
  820.     days = ARG(1)
  821.  
  822.     avg_days = 365.25        /* average days a year */
  823.  
  824.     /* estimate years */
  825.     IF days > 578181 THEN               /* year greater than 1582/12/31 ? */
  826.        year1 = (days + 10 ) % avg_days  /* account for 10 missing days in 1582 */
  827.     ELSE
  828.        year1 = days % avg_days
  829.  
  830.  
  831.     DO FOREVER
  832.  
  833.        /* is year1 a leap year ? */
  834.        IF year1 > 1582 THEN                     /* Gregorian calender */
  835.           year_days = (((year1 // 4) = 0) & \((year1 // 100) = 0)) | ((year1 // 400)=0)
  836.        ELSE year_days = ((year1 // 4) = 0)      /* Julian calender    */
  837.  
  838.        IF year1 <> 1582 THEN year_days = year_days + 365
  839.        ELSE year_days = 355             /* 1582 had 10 days less */
  840.  
  841.        days_year1 = date2days(year1 "1 1")   /* pad year with 0 */
  842.        diff1 = days - days_year1
  843.  
  844.        IF diff1 < 0 THEN year1 = year1 - 1
  845.        ELSE IF diff1 > (year_days - 1) THEN year1 = year1 + 1
  846.        ELSE LEAVE
  847.     END
  848.     diff1 = diff1 + 1           /* one day off, due to subtraction */
  849.  
  850.     tmp = RIGHT(year1,4,'0')|| RIGHT(diff1,3,'0')       /* build Julian date */
  851.  
  852.     RETURN julian2date(tmp)     /* build sorted day */
  853. /* end of DAYS2DATE */
  854.  
  855.  
  856.  
  857.  
  858. /* calculate decimal fraction from time */
  859. TIME2FRACT: PROCEDURE  EXPOSE monthdays.     /* calculate decimal value for time */
  860.     PARSE ARG hours minutes seconds
  861.  
  862.     /* hour_div = 24      =   24           */
  863.     /* min_div  = 1440    =   24 * 60      */
  864.     /* sec_div  = 86400   =   24 * 60 * 60 */
  865.  
  866.     RETURN ((hours/24) + (minutes/1440) + (seconds/86400))
  867. /* end of TIME2FRACT */
  868.  
  869.  
  870. /* calculate time from fraction */
  871. FRACT2TIME: PROCEDURE  EXPOSE monthdays.     /* calculate time from given value */
  872.     /* hours    = 24      =   24           */
  873.     /* minutes  = 1440    =   24 * 60      */
  874.     /* seconds  = 86400   =   24 * 60 * 60 */
  875.  
  876.     tmp = arg(1) + 0.0000001            /* account for possible precision error */
  877.  
  878.     hours   = (tmp * 24) % 1
  879.     minutes = (tmp * 1440 - hours * 60) % 1
  880.     seconds = (tmp * 86400 - hours * 3600 - minutes * 60) % 1
  881.  
  882.     RETURN RIGHT(hours,2,'0')':'RIGHT(minutes,2,'0')':'RIGHT(seconds,2,'0')
  883.  
  884. /* end of FRACT2TIME */
  885.  
  886.  
  887. /* build Julian date from sorted date, result: yyyyddd */
  888. DATE2JULIAN: PROCEDURE EXPOSE monthdays.
  889.     PARSE ARG year month day
  890.  
  891.     /* is year a leap year ? */
  892.     IF year > 1582 THEN                 /* Gregorian calender */
  893.        leap_year = (((year // 4) = 0) & \((year // 100) = 0)) | ((year // 400)=0)
  894.     ELSE leap_year = (year // 4) = 0    /* Julian calender    */
  895.  
  896.     monthdays.2 = 28 + leap_year
  897.     IF year = 1582 THEN monthdays.10 = 21       /* 1582: October just had 21 days */
  898.  
  899.     result_function = 0
  900.     DO i = 1 TO month - 1
  901.        result_function = result_function + monthdays.i
  902.     END
  903.  
  904.     IF year = 1582 & month = 10 & day > 4 THEN day = day - 10       /* Gregorian: 10 days too many */
  905.     result_function = result_function + day
  906.  
  907.     RETURN year||RIGHT(result_function,3,'0')
  908. /* end of DATE2JULIAN */
  909.  
  910.  
  911.  
  912.  
  913. /* build sorted date from Julian date, result: yyyymmdd */
  914. JULIAN2DATE: PROCEDURE EXPOSE monthdays.
  915.     year = SUBSTR(ARG(1),1,4)
  916.  
  917.     /* is year a leap year ? */
  918.     IF year > 1582 THEN                 /* Gregorian calender */
  919.        leap_year = (((year // 4) = 0) & \((year // 100) = 0)) | ((year // 400)=0)
  920.     ELSE leap_year = (year // 4) = 0    /* Julian calender    */
  921.  
  922.     monthdays.2 = 28 + leap_year
  923.     IF year = 1582 THEN monthdays.10 = 21       /* 1582: October just had 21 days */
  924.  
  925.     jul_days = SUBSTR(ARG(1),5)       /* Julian days */
  926.     SELECT
  927.        WHEN jul_days > (365 + leap_year) THEN
  928.           DO
  929.              errmsg = ARG(1)": too many days for the given year"
  930.              SIGNAL error
  931.           END
  932.        WHEN year = 1582 & jul_days > 355 THEN       /* 1582: 10 days less than other years */
  933.           DO
  934.              errmsg = ARG(1)": too many days for 1582 (had 355 days only)"
  935.              SIGNAL error
  936.           END
  937.        OTHERWISE NOP
  938.     END
  939.  
  940.     /* calculate days */
  941.     tmp = 0
  942.     DO month = 1 TO 12
  943.        tmp = tmp + monthdays.month
  944.        IF tmp = jul_days THEN        /* exactly given days ?       */
  945.        DO
  946.           day = monthdays.month
  947.           LEAVE
  948.        END
  949.  
  950.        IF tmp > jul_days THEN      /* got over month              */
  951.        DO
  952.           day = monthdays.month - (tmp - jul_days)
  953.           LEAVE
  954.        END
  955.     END
  956.  
  957.     /* 1582: October 1-4, 15-31,  adjust for 10 missing days if necessary */
  958.     IF year = 1582 & month = 10 & day > 4 THEN day = day + 10
  959.  
  960.     RETURN year||RIGHT(month,2,'0')||RIGHT(day,2,'0')
  961. /* end of JULIAN2DATE */
  962.  
  963.  
  964.  
  965.  
  966. /* return day of sorted date as name or as index */
  967. WEEKDAY: PROCEDURE EXPOSE monthdays.
  968.     total_days = date2days(ARG(1))
  969.     dayindex = (total_days + 2) // 7 + 1  /* normalize on Mondays = 1, ..., Sunday = 7 */
  970.  
  971.     IF ARG(2) = 'I' THEN result_function = dayindex
  972.     ELSE IF ARG(2) = 'ALL' THEN result_function = total_days dayindex
  973.     ELSE result_function = WORD("Monday Tuesday Wednesday Thursday Friday Saturday Sunday", dayindex)
  974.  
  975.     RETURN result_function
  976.     /* remark:
  977.         According to an advice of ISO a week starts with MONDAY, hence:
  978.         Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5,
  979.         Saturday = 6, Sunday = 7.
  980.         The German DIN-organization already normalized on the ISO advice.
  981.     */
  982. /* end of WEEKDAY */
  983.  
  984. USAGE:
  985. /* get ANSI-color-sequences from ScrColor.CMD */
  986. PARSE VALUE ScrColor() WITH screen_normal screen_inverse text_normal text_info text_highlight text_alarm .
  987.  
  988. SAY
  989. SAY text_info'DATERGF:'screen_normal' manipulate sorted dates, time, days and time-fractions'
  990. SAY
  991. SAY text_alarm'usage as a function in REXX-programs:'
  992. SAY text_highlight'         DATERGF(argument)'
  993. SAY '         DATERGF(argument, flag)'
  994. SAY '         DATERGF(argument1, flag, argument2)'
  995. SAY
  996. SAY text_alarm'usage from command-line:'
  997. SAY text_highlight'         DATERGF argument '
  998. SAY '         DATERGF argument, flag '
  999. SAY '         DATERGF argument1, flag, argument2 '
  1000. SAY
  1001. SAY screen_normal'         see enclosed Tutorial "RGFSHOW.CMD" and syntax below'
  1002. SAY
  1003. SAY text_alarm'syntax:'
  1004. SAY
  1005. SAY text_info'SDATE'screen_normal' ...... sorted date (YYYYMMDD)'
  1006. SAY text_info'JDATE'screen_normal' ...... Julian date (YYYYDDD)'
  1007. SAY text_info'DAYS'screen_normal' ....... DATERGF-days, if time supplied, time is a decimal fraction'
  1008. SAY text_info'TIME'screen_normal' ....... on input 24hour- (military) or 12hour-format allowed,'
  1009. SAY '             output will be allways in 24hour-format (military, computer)'
  1010. SAY text_info'FRACT'screen_normal' ...... decimal fraction of time'
  1011. SAY text_info'SECONDS'screen_normal' .... second-value for DAYS.FRACT'
  1012. SAY
  1013. SAY 'Argument1:      Flag: Argument2:        Result:'
  1014. SAY
  1015. SAY text_info'"SDATE[ TIME]"                          DAYS[.FRACT]'screen_normal' since 00000101 inclusive'
  1016. SAY text_info'"DAYS[.FRACT]", "S"                     SDATE[ TIME24]'
  1017. SAY '"SDATE[ TIME]", "J"                     JDATE'
  1018. SAY '"JDATE",        "JR"                    SDATE'screen_normal' ("Julian Reversed")'
  1019. SAY
  1020. SAY text_info'"TIME",         "F"                     FRACT'
  1021. SAY '"FRACT",        "FR"                    TIME24'screen_normal' ("Fraction Reversed")'
  1022. SAY
  1023. SAY text_info'"DAYS[.FRACT]", "SEC"                   SECONDS'
  1024. SAY '"SECONDS",      "SECR"                  DAYS[.FRACT]'screen_normal' ("SEConds Reversed")'
  1025. SAY
  1026. SAY text_info'"SDATE[ TIME]", "-S", "SDATE[ TIME]"    DAYS[.FRACT]'
  1027. SAY '"SDATE[ TIME]", "-",  "DAYS[.FRACT]"    SDATE[ TIME24]'
  1028. SAY '"SDATE[ TIME]", "+",  "DAYS[.FRACT]"    SDATE[ TIME24]'
  1029. SAY
  1030. SAY '"SDATE[ TIME]", "C"                     SDATE'screen_normal' if correct, nothing ('text_info''''''screen_normal') if false'
  1031. SAY text_info'"SDATE[ TIME]", "L"'screen_normal'                     if leap-year, return 1, else 0'
  1032. SAY text_info'"SDATE[ TIME]", "Y"'screen_normal'                     check date & return year'
  1033. SAY text_info'"SDATE[ TIME]", "YB"[, DAYS[.FRACT]]'screen_normal'    year begin 'text_info'[+/- DAYS[.FRACT]]'
  1034. SAY '"SDATE[ TIME]", "YE"[, DAYS[.FRACT]]'screen_normal'    year end 'text_info'[+/- DAYS[.FRACT]]'
  1035. SAY '"SDATE[ TIME]", "HY"'screen_normal'                    semester index (1-2)'
  1036. SAY text_info'"SDATE[ TIME]", "HYB"[, DAYS[.FRACT]]'screen_normal'   semester begin 'text_info'[+/- DAYS[.FRACT]]'
  1037. SAY '"SDATE[ TIME]", "HYE"[, DAYS[.FRACT]]'screen_normal'   semester end 'text_info'[+/- DAYS[.FRACT]]'
  1038. SAY
  1039. SAY '"SDATE[ TIME]", "M"'screen_normal'                     check date & return month'
  1040. SAY text_info'"SDATE[ TIME]", "MN"'screen_normal'                    monthname'
  1041. SAY text_info'"SDATE[ TIME]", "MB"[, DAYS[.FRACT]]'screen_normal'    month begin 'text_info'[+/- DAYS[.FRACT]]'
  1042. SAY '"SDATE[ TIME]", "ME"[, DAYS[.FRACT]]'screen_normal'    month end 'text_info'[+/- DAYS[.FRACT]]'
  1043. SAY ''
  1044. SAY '"SDATE[ TIME]", "D"'screen_normal'                     check date & return day'
  1045. SAY text_info'"SDATE[ TIME]", "DN"'screen_normal'                    dayname'
  1046. SAY text_info'"SDATE[ TIME]", "DI"'screen_normal'                    dayindex (1=monday, ..., 7=sunday)'
  1047. SAY
  1048. SAY text_info'"SDATE[ TIME]", "W"'screen_normal'                     week-number'
  1049. SAY text_info'"SDATE[ TIME]", "WB"[, DAYS[.FRACT]]'screen_normal'    week begin (Monday) 'text_info'[+/- DAYS.[FRACT]]'
  1050. SAY '"SDATE[ TIME]", "WE"[, DAYS[.FRACT]]'screen_normal'    week end (Sunday) 'text_info'[+/- DAYS.[FRACT]]'
  1051. SAY
  1052. SAY '"SDATE[ TIME]", "Q"'screen_normal'                     quarter index (1-4)'
  1053. SAY text_info'"SDATE[ TIME]", "QB"[, DAYS[.FRACT]]'screen_normal'    quarter begin 'text_info'[+/- DAYS[.FRACT]]'
  1054. SAY '"SDATE[ TIME]", "QE"[, DAYS[.FRACT]]'screen_normal'    quarter end 'text_info'[+/- DAYS[.FRACT]]'screen_normal
  1055. EXIT
  1056.  
  1057.  
  1058. ERROR:
  1059. /* 1994-03-03: produce an error-message on stderr even if called as a function
  1060.    /* invoked as a COMMAND (from command-line) or as a FUNCTION ? */
  1061.    PARSE SOURCE . invocation .
  1062.    invocation_as_function = (invocation = "FUNCTION")
  1063.  
  1064.    /* error message on device "STDERR", only if not called as function */
  1065.    IF \invocation_as_function THEN
  1066. */
  1067.  
  1068.    IF flag <> "C" THEN                          /* don't show error message if "C" (checked) was used */
  1069.       '@ECHO DATERGF:' errmsg '>&2'
  1070.  
  1071.    EXIT ''                                      /* error, therefore return empty string */
  1072.