Subroutine PERIOD_FORMAT( CONV, argANS, argBRANCH, RETURN_DATA)
     /*
     * PERIOD_FORMAT is an example of a developer's custom prompt formatting
     * routine using the square brackets call.
     *
     *  It should be placed in square brackets, like this:
     *
     *                   [PERIOD_FORMAT,branch]
     *
     * See Iso 8601  -- This is not compliant, but could be made so
     *  branch values
     branch   result
     J        yyyy mm
     J2-      yy-mm
     J-       yyyy-mm
     JM       mm            (current year assumed on input)
     JY       yyyy          (month 1 assumed on input)
     J2Y      yy            (month 1 assumed on input)
     JW       ww            week number, current year assumed on input, week1 is week containing jan4
     JW-      yyyy-ww       year, week number
     J2W
     J2W-
     JD       yyyy-ddd     year, day number
     JFD-
     J2D
     J2
     JQ        yyyy Qqq
     JQ-       yyyy-Qqq
     J2Q       yy Qqq

     *----------------------------------------------
     Example:
     *----------------------------------------------

     given table person containing  a date field named "CREATED",

     make calculated columns like below

     created_year   ; @ans = oconv({CREATED},"[PERIOD_FORMAT,JY]")
     created_quarter; @ans = oconv({CREATED},"[PERIOD_FORMAT,JQ-]")
     created_month  ; @ans = oconv({CREATED},"[PERIOD_FORMAT,JM-]")
     created_week   ; @ans = oconv({CREATED},"[PERIOD_FORMAT,JW-]")

     you can index these columns, then run reports like
     * ------------------------------------------------
     LIST PERSON WITH CREATED_MONTH EQ "2017-01" BY CREATED CREATED_YEAR CREATED_QUARTER BREAK-ON CREATED_MONTH BREAK-ON CREATED_WEEK CREATED TOTAL CNT
     * ------------------------------------------------

     Key    Year Quarter Month.. Week.. Created... Cnt
     456300 2017 2017-Q1 2017-01 2017-2 01/03/2017 1
     343420 2017 2017-Q1 2017-01 2017-2 01/04/2017 1
     235412 2017 2017-Q1 2017-01 2017-2 01/05/2017 1
     359226 2017 2017-Q1 2017-01 2017-2 01/05/2017 1
     386344 2017 2017-Q1 2017-01 2017-2 01/06/2017 1
     *** 5
     419290 2017 2017-Q1 2017-01 2017-3 01/09/2017 1
     371020 2017 2017-Q1 2017-01 2017-3 01/10/2017 1
     466330 2017 2017-Q1 2017-01 2017-3 01/10/2017 1
     460838 2017 2017-Q1 2017-01 2017-3 01/14/2017 1
     *** 4
     242294 2017 2017-Q1 2017-01 2017-4 01/15/2017 1
     451632 2017 2017-Q1 2017-01 2017-4 01/15/2017 1
     394126 2017 2017-Q1 2017-01 2017-4 01/16/2017 1
     408958 2017 2017-Q1 2017-01 2017-4 01/16/2017 1
     452012 2017 2017-Q1 2017-01 2017-4 01/17/2017 1
     373470 2017 2017-Q1 2017-01 2017-4 01/18/2017 1
     *** 6
     324396 2017 2017-Q1 2017-01 2017-5 01/25/2017 1
     255764 2017 2017-Q1 2017-01 2017-5 01/26/2017 1
     287786 2017 2017-Q1 2017-01 2017-5 01/26/2017 1
     343596 2017 2017-Q1 2017-01 2017-5 01/27/2017 1
     365166 2017 2017-Q1 2017-01 2017-5 01/28/2017 1
     *** 5
     249224 2017 2017-Q1 2017-01 2017-6 01/29/2017 1
     455278 2017 2017-Q1 2017-01 2017-6 01/29/2017 1
     235614 2017 2017-Q1 2017-01 2017-6 01/31/2017 1
     304394 2017 2017-Q1 2017-01 2017-6 01/31/2017 1
     *** 4
     ***                   24



     * ------------------------------------------------
     LIST PERSON WITH CREATED_QUARTER EQ "2017-Q1" BY CREATED BREAK-ON CREATED_YEAR "'V'" BREAK-ON CREATED_QUARTER "'V'" BREAK-ON CREATED_MONTH "'V'" BREAK-ON CREATED_WEEK "'V'" TOTAL CNT ID-SUPP DET-SUPP
     * ------------------------------------------------

     Created Year Created Quarter Created Month CREATED_WEEK Cnt
     2017-2   3465
     2017-3   3526
     2017-4   3393
     2017-5   3585
     2017-6   1475

     2017-01               15444

     2017-6   2064
     2017-7   3432
     2017-8   3473
     2017-9   3423
     2017-10   1541

     2017-02               13933

     2017-10   1928
     2017-11   3527
     2017-12   3395
     2017-13   3543
     2017-14   3027

     2017-03                15420

     2017-Q1                            44797

     2017                                              44797

     ***                                                   44797

     *----------------------------------------------



     * 2006-08-27  rjc    Created
     * 2017-12-27  rjc    Cleaned up

     */

     #pragma format_Indent_comments
     $insert msg_equates
     $insert logical
     $insert rti_HashTable_Equates
     $insert rti_SSP_Equates
     $insert logical

     Declare Subroutine Set_Status
     Declare Function rti_HashTable_STL, get_status

     /*
     */

     declare function Msg, rtp_65
     common /period_Format_Com/init%,hCache%
     If init% Else
          hCache% = rti_HashTable_STL(REVSTL_HTBLMTD_CREATETABLE$, REVSTL_HTBLTYPE_SPP$ )
          init% = ( hCache% gt 0 )
     end

     *  Local Equates
     *  The STATUS() variable is used to indicated the error condition of the
     *  pattern. They are:
     EQU VALID$         TO 0    ;* Successful
     EQU INVALID_MSG$   TO 1    ;* Bad Data       -   Print error message window
     EQU INVALID_CONV$  TO 2    ;* Bad Conversion -          "         "
     EQU INVALID_NOMSG$ TO 3    ;* Bad but do not print the error message window


     *  Begin Conversion
     *
     if assigned(argAns) then ans = argAns else ans = ''
     if assigned(argBranch) then branch = argBranch else branch  = ''
     RETURN_DATA = ""


     ans = trim(Ans)
     status() = valid$


     * parse for period, delim
     if branch[1,1] _nec 'J' then
          Status() = INVALID_CONV$
          return
     end

     period_types = 'MYWDQ'
     period_type = ''
     delim = ''
     for i = 1 to len(period_types)
          this_type = period_types[i,1]
          period_pos = indexc(branch, this_Type, 1)
          if period_pos then
               period_type = this_type
               delim = branch[period_pos+1,1]
          end
     until period_type
     next

     * J J2 J2- default to month
     if period_type = '' then
          period_type = 'M'
          if alpha(branch[2,1]) then
               delim = branch[2,1]
          end else
               delim = branch[3,1]
          end
     end

     * default delim is space
     if delim = '' then
          delim = ' '
     end

     * 4 digit year?
     begin case
          case Indexc('JM JW JD',branch,1)
               * Special cases, no display of year
               year_digits = 0
          case index(branch, 2, 1)
               year_digits = 2
          case otherwise$
               year_digits = 4
     end case


     begin case
          case conv = 'OCONV'
               GoSub OConv
          case conv = 'ICONV'
               GoSub Iconv
          case otherwise$
               Status() = INVALID_CONV$
     end case

return
     ******



Iconv:
     /*
     ** Iconv returns a standard serial date number ( day 0 = 12/31/1967 )
     ** Where the date is the frst day of the period, i.e the first day of the year, month or week
     */

     cacheKey = ans:"*I":branch
     cacheVal = null$
     stl_ret = rti_HashTable_STL(REVSTL_HTBLMTD_READROW$, hCache%, cacheKey, cacheVal)
     If stl_Ret gt 0 then
          transfer cacheVal To return_data
          return
     End

     * If they passed in a number, assume it is an iconv'd date already, oconv it, then iconv again.
     If Num(ans) And Len(ans) gt 4 Then
          ans = Oconv(ans,'D4-')
     end

     begin case
          case branch _eqc 'JM'
               * Special case, no year supplied, just month
               month = ans[1,2]
               if num(month) else
                    status()=Invalid_Msg$
                    return
               end

               odate = Oconv(date(),'D4-')
               odate[1,6] = month:'-01-'
               idate = Iconv(odate, 'D')
               if idate then
                    return_data = idate
               end else
                    status()=Invalid_Msg$
               end

          case period_type = 'M'

               if num(ans[3,1]) then
                    delim = ans[5,1]
               end else
                    delim = ans[3,1]
               end

               year = field(ans, delim,1)
               month = field(ans, delim, 2 )

               odate = month:'-01-':year
               idate = Iconv(odate, 'D')
               if idate then
                    return_data = idate
               end else
                    status()=Invalid_Msg$
               end

          case period_type = 'Y'
               year = ans
               if num(year) else
                    status()=Invalid_Msg$
               end

               odate = '01-01-':year
               idate = Iconv(odate, 'D')
               if idate then
                    return_data = idate
               end else
                    status()=Invalid_Msg$
               end


          case period_type = 'W'
          
               * Iconv is date of the sunday that starts the week.
               * Week 1 is assumed to start the sunday of the week in the year that contains Jan 4
               delim = ans
               Convert '0123456789' To '' In delim
               delim = delim[1,1]


               * Valid Year?
               If delim == '' then
                    year = ''
                    week_no = ans
               End else
                    year = field(ans, delim,1)
                    week_no = field(ans, delim,2)
               end

               if year and num(year) else
                    odate = Oconv(date(), 'D4-')
                    year = odate[-4,4]
               end

               * Valid week?
               begin case
                    case week_no = ''
                         idate = ''
                    case alpha(week_no)
                         idate = ''
                    case week_no < 1
                         idate = ''
                    case week_no > 53
                         idate = ''
                    case otherwise$

                         * First week of year always has 1/4 in it
                         first_day = Iconv('01/04/':year, 'D')
                         day_nr = mod(first_Day,7)
                         first_sunday = first_Day - day_nr

                         * Internal date is that number of weeks after first week
                         * Adjust by one, so W1 is first week of year, not W0
                         week_no -=1
                         idate = first_sunday + 7 *week_no
               end case

               if idate then
                    return_data = idate
               end else
                    status()=Invalid_Msg$
               end

          case period_type = 'Q'

               if num(ans[3,1]) then
                    delim = ans[5,1]
               end else
                    delim = ans[3,1]
               end

               year = field(ans, delim,1)
               quarter = field(ans, delim, 2 )
               Convert 'Qq' To '' In quarter

               quarter = ( int(month/4) ) + 1
               Begin Case
                    Case quarter lt 2 ; qmonth = 3
                    Case quarter lt 3 ; qmonth = 6
                    Case quarter lt 4 ; qmonth = 9
                    Case 1            ; qmonth = 12
               End case

               odate = qmonth:'-01-':year
               idate = Iconv(odate, 'D')
               if idate then
                    return_data = idate
               end else
                    status()=Invalid_Msg$
               end

          case period_type = 'D'

               * Iconv is standard date

               if year_digits = 0 or ( ans matches '(1,366)' ) then
                    day_no = ans
                    year = ''
               end else
                    delim = ans[3,1]

                    * Valid Year?
                    begin case
                         case delim = ''
                              year = ''
                              day_no = ''
                         case num(delim)
                              year = ans[1,4]
                              day_no = ans[5,len(ans)]
                              if num(day_no[1,1]) else
                                   day_no[1,1] = ''
                              end
                         case otherwise$
                              year = field(ans, delim,1)
                              day_no = field(ans, delim,2)
                    end case
               end

               if year and num(year) else
                    odate = Oconv(date(), 'D4-')
                    year = odate[-4,4]
               end

               * Valid day?
               is_leap = ( mod(year, 4) = 0 ) and not(mod(year, 100) = 0)
               begin case
                    case day_no = ''
                         idate = ''
                    case alpha(day_no)
                         idate = ''
                    case day_no < 1
                         idate = ''
                    case is_leap and day_no > 366
                         idate = ''
                    case day_no > 365
                         idate = ''
                    case otherwise$
                         first_day = Iconv('01/01/':year, 'D')
                         zeroth_day = first_Day-1
                         idate = zeroth_Day + day_no
               end case

               if idate then
                    return_data = idate
                    unused = rti_HashTable_STL(REVSTL_HTBLMTD_WRITEROW$, hCache%, cacheKey, return_Data)

               end else
                    status()=Invalid_Msg$
               end
     end case

return


Oconv:
     /*
     **  Input is expected to be a serial date number
     **  Output will be the requested format
     */

     return_Data = ''
     if num(ans) and ( ans # '' ) else
          return
     end


     cacheKey = ans:"*O":branch
     cacheVal = null$
     stl_ret = rti_HashTable_STL(REVSTL_HTBLMTD_READROW$, hCache%, cacheKey, cacheVal)
     If stl_Ret gt 0 then
          transfer cacheVal To return_data
          return
     End

     begin case

          case period_type = 'M'
               odate = Oconv(ans, 'D4-')
               month = odate[1,2]
               year  = odate[7,4]
               begin case
                    case year_digits = 4
                         return_data = year : delim : month
                    case year_Digits = 2
                         return_data = year[-2,2] : delim : month
                    case year_digits = 0
                         return_data = month
               end case

          case period_type = 'Y'

               oDate = Oconv(ans, 'D4-')
               year  = odate[-1,'B-']
               if year_Digits = 2 then
                    return_Data = year[-2,2]
               end else
                    return_Data = year
               end

          case period_type = 'W'

               * Get date of sunday for the week containing date
               day_nr = mod(ans, 7)
               sunday = ans - day_nr

               * get date of sunday for date containing Jan 4 of same year
               odate = Oconv(ans, 'D4-')
               year = odate[-4,4]
               jan_4 = iconv('01/-04-':year, 'D')
               day_nr = mod(jan_4, 7)
               first_sunday = jan_4 - day_nr

               * Calc week nr
               * Week 1 is assumed to start the sunday of the week in the year that contains Jan 4
               * Add one to result as first week is week one, not week zero

               day_nr = int(sunday - first_sunday)
               week_no = Int(day_nr / 7) + 1
               week_no +=1
               begin case
                    case year_digits = 4
                         return_data = year : delim : week_no
                    case year_Digits = 2
                         return_data = year[-2,2] : delim : week_no
                    case year_digits = 0
                         return_data = week_no
               end Case
          case period_type = 'Q'
               odate = Oconv(ans, 'D4-')
               month = odate[1,2]
               quarter = int(month/4)+1
               year  = odate[7,4]
               begin case
                    case year_digits = 4
                         return_data = year : delim : "Q":quarter
                    case year_Digits = 2
                         return_data = year[-2,2] : delim : "Q":quarter
                    case year_digits = 0
                         return_data = "Q":quarter
               end Case

          case period_type = 'D'

               odate = Oconv(ans, 'D4/')
               year = odate[-4,4]
               first_day = Iconv('01/01/':year, 'D')
               zeroth_day = first_Day-1
               day_no = ans - zeroth_Day
               day_no = fmt(day_no, 'R(0)#3')
               begin case
                    case year_digits = 4
                         return_data = year : delim : day_no
                    case year_Digits = 2
                         return_data = year[-2,2] : delim : day_no
                    case year_digits = 0
                         return_data = day_no
               end case


     end Case


     unused = rti_HashTable_STL(REVSTL_HTBLMTD_WRITEROW$, hCache%, cacheKey, return_Data)


return
