--
--	Inmos Libraries
--	Copyright (C) 1988, 1989, 1990, 1991, 1993  Inmos Limited
--
--	This 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 2 of the License, or (at your option) any later version.
--
--	This 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 this library; if not, write to the Free Software
--	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
--

--** @module hostio

#COMMENT "Host i/o library, V1.00, 10/03/89"
#COMMENT "(c) Copyright INMOS Limited 1989"
#INCLUDE "hostio.inc"
#USE "splib.lib"
--{{{  so.time.to.date
--* Convert the number of seconds since the midnight before 1st January
-- 1970 into the associated time and date.
--
-- @param input.time the number of seconds since the midnight before
--   1st January 1970
-- @param date an array of six integers where the elements are, index
--                   data 0              seconds past the minute 1
--           minutes past the hour 2              the hour in the 24
--   hour clock 3              the day of the month 4              the
--   month, where January is 1 5              the year expressed fully,
--   i.e. 4 digits
PROC so.time.to.date (VAL INT32 input.time, [so.date.len]INT date)
  -- The output of sp.time is suitable for the parameter input.time
  -- because sp.time counts the number of seconds since the midnight
  -- before 1st Jan 1970; but note that it is actually an UNSIGNED
  -- number, so we have to process it carefully

  INT32 day.number :
  SEQ
    --{{{  calculate the TIME (SS:MM:HH)
    INT32 time :
    INT   adjusted.for.overflow :
    --{{{  constants
    -- This is the largest number of whole days which can be held in an INT32
    -- before overlowing. However, as the TIME is defined to be
    -- unsigned, we must allow for higher numbers.
    
    VAL whole.days.before.wraparound IS 24855(INT32) :
    VAL secs.before.wraparound       IS whole.days.before.wraparound *
      ((60(INT32) * 60(INT32)) * 24(INT32)) : -- nearly MOSTPOS INT32
    --}}}constants
    SEQ
      time := input.time
      --{{{  adjust because 'time' is an unsigned INT32
      -- the idea is that if we subtract a multiple of a days worth of seconds,
      -- we don't affect the TIME on that day.
      -- we can add the days on again later
      
      SEQ
        adjusted.for.overflow := 0
        WHILE (time /\ #80000000(INT32)) <> 0(INT32)  -- ie test the 'overflow' bit
          SEQ
            time := time MINUS secs.before.wraparound
            adjusted.for.overflow := adjusted.for.overflow + 1
      --}}}adjust because 'time' is an unsigned INT32
    
      date [0] := INT (time \ 60(INT32))   -- seconds
      time     := time / 60(INT32)
      date [1] := INT (time \ 60(INT32))   -- minutes
      time     := time / 60(INT32)
      date [2] := INT (time \ 24(INT32))   -- hours
      time     := time / 24(INT32)
    
      --{{{  re-adjust for the overflow - time is now in DAYS
      SEQ i = 0 FOR adjusted.for.overflow
        time := time + whole.days.before.wraparound
      --}}}
    
      -- time now holds number of days completed since 1 Jan 1970
      -- note that the maximum date representable in this format is
      -- about 136 years from the start date
      -- which lies in the year 2106.
    
      day.number := time + 1(INT32)  -- ie add the current day
    --}}}calculate the TIME (SS:MM:HH)
    --{{{  calculate the DATE (DD:MM:YY)
    INT  days, month, year :
    BOOL leap.year :
    SEQ
      --{{{  calculate the year
      VAL year.dot              IS 1970 :
      VAL year.dot.days.in.year IS 365 :
      VAL year.dot.leap.year    IS FALSE :
      
      INT days.in.year :
      SEQ
        year         := year.dot
        days.in.year := year.dot.days.in.year
        leap.year    := year.dot.leap.year
      
        WHILE day.number > (INT32 days.in.year)
          SEQ
            day.number := day.number - (INT32 days.in.year)
            year       := year + 1
            --{{{  get days.in.year, and leap.year
            -- leap year if
            --( ( ( year REM 4 ) = 0 ) AND ( ( year REM 100 ) <> 0 ) OR ( ( year REM 400 ) = 0 ) )
            -- Therefore within correct range ( 1970 - 2106 ) every 4th year IS a leap
            -- year, except 2100.
            
            SEQ
              -- divisible by 4 is ( year /\ #3 ) = 0
              leap.year := ((year /\ #3) = 0) AND (year <> 2100)
              IF
                leap.year
                  days.in.year := 366
                TRUE
                  days.in.year := 365
            --}}}get days.in.year, and leap.year
      
        days := INT day.number  -- now definitely fits in an INT
      --}}}calculate the year
      -- this leaves 'days' between 1 and 366
      --{{{  calculate the month
      --                      J   F   M   A   M   J   J   A   S   O   N   D
      VAL days.in.months IS [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] :
      
      INT days.in.month :
      SEQ
        month         := 0 -- January
        days.in.month := days.in.months [0] -- January too!
      
        WHILE days > days.in.month
          SEQ
            days  := days - days.in.month
            month := month + 1
            --{{{  get days.in.month (testing for leap year)
            IF
              leap.year AND (month = 1)  -- February
                days.in.month := 29
              TRUE
                days.in.month := days.in.months [month]
            --}}}get days.in.month (testing for leap year)
      
        -- Adjust because people call January month 1 etc!
        month := month + 1
      --}}}calculate the month
      -- this leaves 'days' correct
      date [3] := days
      date [4] := month
      date [5] := year
    --}}}calculate the DATE (DD:MM:YY)
:
--}}}so.time.to.date
--{{{  so.date.to.ascii
--* Format a time and date given in the form of six integers.
--
-- @param date an array of six integers where the elements are, index
--                   data 0              seconds past the minute 1
--           minutes past the hour 2              the hour in the 24
--   hour clock 3              the day of the month 4              the
--   month, where January is 1 5              the year expressed fully,
--   i.e. 4 digits
-- @param long.years TRUE if a four digit year is desired; FALSE if a
--   two digit year is desired, the digits being the tens and units,
--   followed by two spaces
-- @param days.first TRUE if date to be written with the days preceding
--   the month ( i.e. British format ); FALSE if the month is to
--   precede the days ( i.e. U.S. format )
-- @param string an ASCII string representing the time and date as
--   follows: "HH:MM:SS DD/MM/YY  " -- long.years = FALSE; days.first =
--   TRUE "HH:MM:SS DD/MM/YYYY" -- long.years = TRUE; days.first = TRUE
--   "HH:MM:SS MM/DD/YY  " -- long.years = FALSE; days.first = FALSE
--   "HH:MM:SS MM/DD/YYYY" -- long.years = TRUE; days.first = FALSE
--   where HH:MM:SS is hours, minutes and seconds, and DD/MM/YY is day,
--   month and year
PROC so.date.to.ascii (VAL [so.date.len]INT date, VAL BOOL long.years,
                       VAL BOOL days.first, [so.time.string.len]BYTE string)
  --{{{  PROC number.to.two.digits
  PROC number.to.two.digits (VAL INT n, [2]BYTE digits)
    SEQ
      digits [1] := BYTE ((n \ 10) + (INT '0'))
      digits [0] := BYTE ((n / 10) + (INT '0'))
  :
  --}}}PROC number.to.two.digits

  SEQ
    --{{{  Hours, Minutes, Secs
    number.to.two.digits (date[2], [string FROM 0 FOR 2]) -- Hours
    number.to.two.digits (date[1], [string FROM 3 FOR 2]) -- Mins
    number.to.two.digits (date[0], [string FROM 6 FOR 2]) -- Secs
    --}}}Hours, Minutes, Secs
    --{{{  Days / Month
    IF
      days.first
        SEQ
          number.to.two.digits (date[3], [string FROM  9 FOR 2]) -- Days
          number.to.two.digits (date[4], [string FROM 12 FOR 2]) -- Months
      TRUE
        SEQ
          number.to.two.digits (date[3], [string FROM 12 FOR 2]) -- Days
          number.to.two.digits (date[4], [string FROM  9 FOR 2]) -- Months
    --}}}Days / Month
    --{{{  Year
    IF
      long.years
        SEQ
          number.to.two.digits (date[5] / 100, [string FROM 15 FOR 2]) -- Years
          number.to.two.digits (date[5] \ 100, [string FROM 17 FOR 2]) -- Years
      TRUE
        SEQ
          number.to.two.digits (date[5] \ 100, [string FROM 15 FOR 2]) -- Years
          [string FROM 17 FOR 2] := "  "
    --}}}Year
    --{{{  the puctuation
    string [ 2] := ':'
    string [ 5] := ':'
    string [ 8] := ' '
    string [11] := '/'
    string [14] := '/'
    --}}}the puctuation
:
--}}}so.date.to.ascii
--{{{  so.time.to.ascii
--* Convert the number of seconds since the midnight before 1st January
-- 1970 into a formatted time and date
--
-- @param time the number of seconds since the midnight before 1st
--   January 1970
-- @param long.years TRUE if a four digit year is desired; FALSE if a
--   two digit year is desired, the digits being the tens and units,
--   followed by two spaces
-- @param days.first TRUE if date to be written with the days preceding
--   the month ( i.e. British format ); FALSE if the month is to
--   precede the days ( i.e. U.S. format )
-- @param string an ASCII string representing the time and date as
--   follows: "HH:MM:SS DD/MM/YY  " -- long.years = FALSE; days.first =
--   TRUE "HH:MM:SS DD/MM/YYYY" -- long.years = TRUE; days.first = TRUE
--   "HH:MM:SS MM/DD/YY  " -- long.years = FALSE; days.first = FALSE
--   "HH:MM:SS MM/DD/YYYY" -- long.years = TRUE; days.first = FALSE
--   where HH:MM:SS is hours, minutes and seconds, and DD/MM/YY is day,
--   month and year
PROC so.time.to.ascii (VAL INT32 time, VAL BOOL long.years,
                       VAL BOOL days.first, [so.time.string.len]BYTE string)
  [so.date.len]INT date :
  SEQ
    so.time.to.date  (time, date)
    so.date.to.ascii (date, long.years, days.first, string)
:
--}}}so.time.to.ascii
--{{{  so.today.date
--* Give the time and date, in the form of six integers, of when this
-- routine was called.
--
-- If the time and date are unavailable all elements of date are set to
-- zero. Local time is used.
--
-- @param fs from server
-- @param ts to server
-- @param date an array of six integers where the elements are, index
--                   data 0              seconds past the minute 1
--           minutes past the hour 2              the hour in the 24
--   hour clock 3              the day of the month 4              the
--   month, where January is 1 5              the year expressed fully,
--   i.e. 4 digits
PROC so.today.date (CHAN OF SP fs, ts, [so.date.len]INT date)
  INT32 local.time, UTC.time :
  BYTE result:

  SEQ
    sp.time (fs, ts, local.time, UTC.time, result)
    IF
      result = spr.ok
        so.time.to.date (local.time, date)
      TRUE
        SEQ i = 0 FOR SIZE date
          date[i] := 0  -- set to zero if no time
:
--}}}so.today.date
--{{{  so.today.ascii
--* Give the time and date, in formatted form, of when this routine was
-- called.
--
-- If the time and date are unavailable string is filled with spaces.
-- Local time is used.
--
-- @param fs from server
-- @param ts to server
-- @param long.years TRUE if a four digit year is desired; FALSE if a
--   two digit year is desired, the digits being the tens and units,
--   followed by two spaces
-- @param days.first TRUE if date to be written with the days preceding
--   the month ( i.e. British format ); FALSE if the month is to
--   precede the days ( i.e. U.S. format )
-- @param string an ASCII string representing the time and date as
--   follows: "HH:MM:SS DD/MM/YY  " -- long.years = FALSE; days.first =
--   TRUE "HH:MM:SS DD/MM/YYYY" -- long.years = TRUE; days.first = TRUE
--   "HH:MM:SS MM/DD/YY  " -- long.years = FALSE; days.first = FALSE
--   "HH:MM:SS MM/DD/YYYY" -- long.years = TRUE; days.first = FALSE
--   where HH:MM:SS is hours, minutes and seconds, and DD/MM/YY is day,
--   month and year
PROC so.today.ascii (CHAN OF SP fs, ts, VAL BOOL long.years, days.first,
                     [so.time.string.len]BYTE string)
  INT32 local.time, UTC.time :
  BYTE result:

  SEQ
    sp.time (fs, ts, local.time, UTC.time, result)
    IF
      result = spr.ok
        so.time.to.ascii (local.time, long.years, days.first, string)
      TRUE
        SEQ i = 0 FOR SIZE string
          string[i] := ' '
:
--}}}so.today.ascii
