DateUtility

From Pickwiki
Jump to navigationJump to search

HomePage>>SourceCode>>BasicSource>>DateUtility

This is a toolbox of date/time zone conversion utilities, not supplied by ICONV/OCONV.

All in one convenient place!

Methods

  • GetDateFormat - return: true if International format (dd/mm/yy) is set.
  • GetElapsedPeriod - return: elapsed years @am months @am days
  • GetElapsedTime - return: difference in seconds
  • GetElapsedTime.toString - return: oconv'd time (hh:mm:ss)
  • GetEpochTime - return: epoch time - seconds from 1970-01-01 UTC
  • GetISODateTime - return: ISO Date Time string yyyy-mm-ddThh:mm:ssZ
  • GetISOWeekOfYear - return: ISO8601 week of year
  • GetRFCDateTime - return: RFC1123 Date Time string: Sun, 06 Nov 1994 08:49:37 GMT
  • GetNumberSuffix - return: numeric ordinal suffix ('st,nd,rd,th')
  • GetTimeZoneString - return: users current TZ string
  • ParseEpochTime - return: date:@am:time [local at the time zone]
  • ParseEpochTime.toString - return: iso date time
  • ParseISODateTime - return: epoch time - seconds from 1970-01-01 UTC
  • ParseISODateTime.local - return: (user local) date:@am:time
  • ParseRFCDateTime - return: Epoch time - seconds from 1970-01-01 UTC
  • ParseRFCDateTime.local - return: (user local) date:@am:time
  • SetDateFormat - Set the date format ON to International (dd/mm/yy)

Background

The main goal of this project was to provide a tool to convert to and from ISO8601. This is the date/time format used as standard in such things as XML documents. The nature of ISO8601, means that time zone must be taken into consideration. So, this turned into a bunch of methods which will also assist in Time Zone handling.

Overkill? Well possibly for most applications. However, regard placing orders on foreign countries and setting local delivery dates & times or easily finding the east coast time of an XML document stamped with west coast time (amongst others uses). We found we had several places we needed these types of time & date conversion, so it grew.

If you don't see something useful, please feel free to comment and add to it.

The program is designed as a function requiring a method and input arguments and returns a result value, and sets status() if conversion fails. It means it can be easily called from basic or an I-type subr() statement. See the comments in the code for examples and details of methods and the arguments required for each method.

Remember to tweak the E.DEFAULT.TZ equate constant, or read it in from a parameter file.

Glossary

  • Epoch - meaning the Unix epoch - (milli)seconds from 1970-01-01T00:00Z.
  • GMT - Greenwich Mean Time, unadjusted time at Greenwich, London UK.
  • ISO - International Standards Organisation - they set, er, international standards.
  • ISO8601 - Set of standards for representing local dates and time relative to UTC.
  • RFC - Request For Comment. The set of documents which make up the standards which the internet is built around.
  • RFC1123 - preferred as an Internet standard date format (HTTP).
  • TZ - Unix environment variable used to carry time zone info. On a Windows system this must be derived from the registry.
  • UTC - Universal Coordinated Time - International standard for time measurement (something close to GMT).
  • Z - Zulu, shorthand for the time at zero meridian or UTC.

The Code


function DATE.UTILITY(method,arguments)
* DTV: Various date & time methods not handled by OCONV, eg.ISO8601, RFC1123
*// Stuart Boydell 2003-02-24
*//
*//    Change E.DEFAULT.TZ for target server time zone locale.
*//
*// see http://www.timeanddate.com for more information about times and time zones
*//
*// Catalog this globally.
*// Call as either function ! i-type subr() with method and arguments.
*// Methods and arguments are enumerated in the Mainline subroutine.
*// No method, default datetime() string is returned
*// No arguments, results for "now" are returned.
*// Use of Epoch time - seconds since 1970-01-01T00:00Z
*// Use of iso8601 date time formats (used extensively with xml, html & sql)
*// general format is: YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+10:00)
*//
*// Usage 1: program
*//      PROGRAM SOME.PROGRAM
*//      DEFFUN DATE.UTIL(method,arguments) CALLING '*DATE.UTILITY'
*//      ...
*//      MY.METHOD     = '[[GetISODateTime]]'
*//      MY.ARGUMENTS  = ORD.DATE:@AM:ORD.TIME
*//      ISO.DATE.TIME = DATE.UTIL(MY.METHOD,MY.ARGUMENTS)
*//
*// Usage 2: I-type from a file with MY.DATE[[/TIME]].FIELD attributes.
*//      0001 I
*//      0002 subr('*DATE.UTILITY','[[GetISODateTime]]',MY.DATE.FIELD:@AM:MY.TIME.FIELD)
*//      ...
*//
*// see Methods below for further information.

*--------------------------------------------
[[DefineEquates]]:
*--------------------------------------------

   equ E.THIS.PROG      to 'DATE.UTILITY',
       E.VERSION        to '1.0'

   *// set this to your local server time as a default/fallback position
   equ E.DEFAULT.TZ     to 'EET-10EETDT-11,M10.5.0,M3.5.0'

   *// various time constants
   equ E.SECS.PER.MIN   to 60,
       E.SECS.PER.HOUR  to 3600,
       E.SECS.PER.DAY   to 86400,
       E.SECS.PER.YEAR  to 31556926,  ;*// tropical year seconds (approx 365.25 days)
       E.EPOCH.SECS     to system(99),;*// seconds from 1970-01-01T00:00Z
       E.EPOCH.OFFSET   to 732        ;*// iconv('01-Jan-1970','d')

   *// numeric ordinal suffix ; eg 1st 2nd 3rd 4th ...
   equ E.NUM.SUFFIX     to 'st':@am:'nd':@am:'rd':@am:'th'

   *// codes, characters, conversions and such
   equ E.ISODATE.CONV   to 'D-YMD[4,2,2]',
       E.ISOTIME.CONV   to 'MT',
       E.RFCDATE.CONV   to 'D WBDMBYL[", "]',
       E.RFCTIME.CONV   to 'MTS',
       E.TZ.ENVSTR      to 'TZ',     ;*// time zone environment string lead-in
       E.ISOTIME.SEP    to 'T',
       E.ISOZONE.SEP    to 'Z',
       E.PLUS           to '+',
       E.MINUS          to '-',
       E.POINT          to '.',
       E.TMP.ID         to @tty:'tz.vbs'

   *// time[[ZoneArray]]
   *// 1=tz desc, 2=tz offset, 3=dls desc, 4=dls offset
   *// 5=start month,  6=week,  7=day,  8=time
   *// 9=stop  month, 10=week, 11=day, 12=time
   equ E.GTZO.DESC        to time[[ZoneArray]]<1>,
       E.GTZO.OFFSET      to time[[ZoneArray]]<2>,
       E.GTZO.DLS.DESC    to time[[ZoneArray]]<3>,
       E.GTZO.DLS.OFFSET  to time[[ZoneArray]]<4>,
       E.GTZO.START.MONTH to time[[ZoneArray]]<5>,
       E.GTZO.START.WEEK  to time[[ZoneArray]]<6>,
       E.GTZO.START.DAY   to time[[ZoneArray]]<7>,
       E.GTZO.START.TIME  to time[[ZoneArray]]<8>,
       E.GTZO.STOP.MONTH  to time[[ZoneArray]]<9>,
       E.GTZO.STOP.WEEK   to time[[ZoneArray]]<10>,
       E.GTZO.STOP.DAY    to time[[ZoneArray]]<11>,
       E.GTZO.STOP.TIME   to time[[ZoneArray]]<12>

*--------------------------------------------
Mainline:
*--------------------------------------------

   returnValue = oconv('','c')               ;*// set RV to '' and status() function to false

   *// handle passed method parameter
   begin case

   case method = '[[GetDateFormat]]'
      *// args:   null
      *// return: true/false
      gosub [[GetDateFormat]]:

   case method = '[[GetElapsedPeriod]]'
      *// args:   start date @am stop date
      *// return: elapsed years am months am days
      gosub [[GetElapsedPeriod]]:

   case method = '[[GetElapsedTime]]'
      *// args:   [start date @vm] start time @am [stop date @vm] stop time
      *// return: difference in seconds
      gosub [[GetElapsedTime]]:

   case method = '[[GetElapsedTime]].toString'
      *// args: [start date @vm] start time @am [stop date @vm] stop time
      *// return: oconv'd time (hh:mm:ss)
      gosub [[GetElapsedTime]]:
      returnValue = oconv(returnValue,E.ISOTIME.CONV)
      if status() then returnValue = ''

   case method = '[[GetEpochTime]]'
      *// args: [date][@am time][@am time[[ZoneString]]]
      *// return: epoch time - seconds from epoch
      gosub [[GetEpochTime]]:

   case method = '[[GetISODateTime]]' ! method = 'get[[ISODateTime]].toString'
      *// args: [date],[time],[timezone]
      *// return ISO Date Time string yyyy-mm-ddThh:mm:ssZ
      gosub [[GetISODateTime]]:

   case method = '[[GetISOWeekOfYear]]'
      *// args: [date]
      *// return ISO8601 week of year
      gosub [[GetISOWeekOfYear]]:

   case method = '[[GetRFCDateTime]]'
      *// args: [date],[time]
      *// return rfc1123 Date Time string: Sun, 06 Nov 1994 08:49:37 GMT
      gosub [[GetRFCDateTime]]:

   case method = '[[GetNumberSuffix]]'
      *// args: number
      *// return: numeric ordinal suffix ('st,nd,rd,th')
      gosub [[GetNumberSuffix]]:

   case method = '[[GetTimeZoneString]]'
      *// args: null
      *// return users current TZ string
      gosub [[GetTimeZoneString]]:

   case method = '[[GetTimeZoneStringFromArray]]'
      *// args: [[TimeZoneArray]] (eg time[[ZoneArray]])
      *// return users current TZ string
      gosub [[GetTimeZoneStringFromArray]]:

   case method = '[[ParseEpochTime]]'
      *// args: [UTCSeconds][@am time[[ZoneString]]]
      *// return: date:@am:time [local at the time zone]
      gosub [[ParseEpochTime]]:

   case method = '[[ParseEpochTime]].toString'
      *// args: a value being seconds from epoch
      *// return: iso date time
      gosub [[ParseEpochTime]]:
      arguments = returnValue
      gosub [[GetISODateTime]]:

   case method = '[[ParseISODateTime]]'
      *// args: ISO formatted date string eg 1997-07-16T19:20+10:00
      *// return epoch time - seconds from 1/1/70 UTC
      gosub [[ParseISODateTime]]:

   case method = '[[ParseISODateTime]].local'
      *// args: ISO formatted date string eg 1997-07-16T19:20+10:00
      *// return (user local) date:@am:time
      gosub [[ParseISODateTime]]:
      if validTime then
         arguments = returnValue
         gosub [[ParseEpochTime]]:
      end

   case method = '[[ParseRFCDateTime]]'
      *// args: rfc1123 Date Time string: Sun, 06 Nov 1994 08:49:37 GMT
      *// return Epoch time - seconds from epoch
      gosub [[ParseRFCDateTime]]:

   case method = '[[ParseRFCDateTime]].local'
      *// args: rfc1123 Date Time string: Sun, 06 Nov 1994 08:49:37 GMT
      *// return (user local) date:@am:time
      gosub [[ParseRFCDateTime]]:
      arguments = returnValue
      gosub [[ParseEpochTime]]:

   case method = '[[ParseTimeZoneString]]'
      *// args: [time zone string]
      *// return Time Zone Object
      gosub [[ParseTimeZoneString]]:

   case method = '[[SetDateEuropean]]'
   *// deprecated - used in legacy calls - use set[[DateFormat]]
      *// args: null
      *// return: null
      arguments = 'ON'
      gosub [[SetDateFormat]]:

   case method = '[[SetDateFormat]]'
      *// args: set [ON[[/OFF]]][D new date format] 
      *// return: null
      gosub [[SetDateFormat]]:

   case @true
      *// default case, return time date string
      *// return: timedate eg "15:39:12 22 APR 2003"
      returnValue = timedate()

   end case

return(returnValue)
stop

*--------------------------------------------
[[GetDateFormat]]:    *// International = true, US = false
*--------------------------------------------

   get[[DateFormatCmd]] = 'DATE.FORMAT INFORM'
   execute get[[DateFormatCmd]] capturing cap returning returnValue

return(@null)

*--------------------------------------------
[[GetElapsedPeriod]]:
*--------------------------------------------

   periodStart = arguments<1>
   periodEnd   = arguments<2>
   if (periodEnd >= periodStart) then 
      totalYears  = oconv(periodEnd,'dy') - oconv(periodStart,'dy') 
      totalMonths = oconv(periodEnd,'dm') - oconv(periodStart,'dm') 
      totalDays   = oconv(periodEnd,'dd') - oconv(periodStart,'dd') 
      if (totalDays < 0) then 
         totalDays   += oconv(iconv('1 ':oconv(periodEnd,'d mby'),'d') -1,'dd') 
         totalMonths -= 1 
      end 
      if (totalMonths < 0) then 
         totalMonths += 12 
         totalYears  -= 1 
      end 
      returnValue = totalYears : @am : totalMonths : @am : totalDays 
   end 

return(@null)

*--------------------------------------------
[[GetElapsedTime]]:   *// find elapsed time from 2 second counts
*--------------------------------------------

   startTime = arguments<1>
   stopTime  = arguments<2>

   begin case
   case dcount(startTime,@vm) = 2 & dcount(stopTime,@vm) = 2 ;*// dates & times provided
      arguments = raise(startTime<1>)
      gosub [[GetEpochTime]]:        ;*// get start as secs from epoch
      startTime = returnValue
      arguments = raise(stopTime<1>)
      gosub [[GetEpochTime]]:        ;*// get stop as secs from epoch
      returnValue -= startTime

   case not(num(startTime) & num(stopTime)) ;*// bad data

   case (startTime - stopTime) > E.SECS.PER.DAY
   case stopTime < startTime
      *// calculate up to same time over a midnight boundry
      *// ie start=23:00, end=03:00 : total => (27:00 - 23:00) => 05:00
      returnValue = stopTime + E.SECS.PER.DAY - startTime

   case @true
      returnValue = (stopTime - startTime)
   end case

return(@null)

*--------------------------------------------
[[GetEpochTime]]:    *// return seconds from 1/1/70 UTC
*--------------------------------------------

   internalDate   = arguments<1>
   internalTime   = arguments<2>
   time[[ZoneString]] = arguments<3>

   if internalDate   = '' then internalDate = date()
   if internalTime   = '' then internalTime = time()
   if time[[ZoneString]] = '' then
      *// get default
      gosub [[GetTimeZoneString]]:
      time[[ZoneString]] = returnValue
   end
   arguments = time[[ZoneString]]
   gosub [[ParseTimeZoneString]]:

   arguments = internalDate
   gosub [[GetTimeZoneOffset]]:

   returnValue += (internalDate - E.EPOCH.OFFSET) * E.SECS.PER.DAY
   returnValue += internalTime

return(@null)

*--------------------------------------------
[[GetISODateTime]]:  *// return date in ISO format yyyy-mm-ddThh:mm:ssZhh:mm
*--------------------------------------------

   internalDate   = arguments<1>
   internalTime   = arguments<2>
   if unassigned(time[[ZoneString]]) then time[[ZoneString]] = arguments<3>

   if internalDate = '' then internalDate = date()
   if internalTime = '' then internalTime = time()
   if time[[ZoneString]] = '' then
      *// get default
      gosub [[GetTimeZoneString]]:
      time[[ZoneString]] = returnValue
   end

   isoDate = oconv(internalDate,E.ISODATE.CONV)
   if not(status()) then
      isoTime = oconv(internalTime,E.RFCTIME.CONV)
      if not(status()) then
         if len(time[[ZoneString]]) then
            arguments = time[[ZoneString]]
            gosub [[ParseTimeZoneString]]:
         end
         arguments = internalDate
         gosub [[GetTimeZoneOffset]]:
         arguments = returnValue
         gosub [[GetISOZone]]:
         if not(status()) then
            returnValue = isoDate : E.ISOTIME.SEP : isoTime : returnValue
         end
      end
   end

return(@null)

*--------------------------------------------
[[GetISOWeekOfYear]]:
*--------------------------------------------
   
   internalDate = arguments<1>
   if internalDate = '' then internalDate = date()
   day[[OfWeek]] = oconv(internalDate, 'dw')
   if status() then                 ;*// test for bad conversion
      weekNumber = '-1'
   end else
      thisThursday = internalDate - day[[OfWeek]] + 4 ;* calculate date for that Thursday
      day[[OfYear]]    = oconv(thisThursday,'dj')
      weekNumber   = int((day[[OfYear]] + 6) / 7) ;*// ta WOL
   end
   returnValue = weekNumber

return(@null)

*--------------------------------------------
[[GetISOZone]]:   *// return ISO time zone offset
*--------------------------------------------

   local[[OffsetSecs]] = arguments
   isoZone = oconv(abs(local[[OffsetSecs]]),E.ISOTIME.CONV)

   begin case
   case status()                                ;*// invalid oconv         15-Apr-2003 return default offset for melbourne
      returnValue = '+10:00'
   case abs(local[[OffsetSecs]]) >= E.SECS.PER.DAY  ;*// invalid offset value  15-Apr-2003 return default offset for melbourne
      returnValue = '+10:00'
   case local[[OffsetSecs]] < 0
      returnValue = E.PLUS:isoZone
   case local[[OffsetSecs]] > 0
      returnValue = E.MINUS:isoZone
   case local[[OffsetSecs]] = 0
      returnValue = E.ISOZONE.SEP:isoZone
   case @true
      returnValue = '+10:00'
   end case

return(@null)

*--------------------------------------------
[[GetRFCDateTime]]:  *// return Date Time string: Sun, 06 Nov 1994 08:49:37 GMT
*--------------------------------------------
      
   gosub [[GetEpochTime]]:

   *// get time and date at GMT
   internalDate = int(returnValue[[/E]].SECS.PER.DAY) + E.EPOCH.OFFSET - (returnValue < 0)
   internalTime = mod(returnValue,E.SECS.PER.DAY) + (E.SECS.PER.DAY * (returnValue < 0))

   rfcDate = oconv(internalDate,E.RFCDATE.CONV)
   if not(status()) then
      rfcTime = oconv(internalTime,E.RFCTIME.CONV)
      if not(status()) then
         returnValue = rfcDate : ' ': rfcTime : ' GMT'
      end
   end

return(@null)

*--------------------------------------------
[[GetNumberSuffix]]: *// get numerical ordinal suffix
*--------------------------------------------

   number = arguments<1>
   if len(number) then
      lastChar = number[1] ;*// right(number,1)
      lastTeen = number[2] ;*// right(number,2)

      begin case
      case lastTeen >= 11 & lastTeen <= 20
         returnValue = number:(E.NUM.SUFFIX)<4>
      case lastChar > 0 & lastChar <= 3
         returnValue = number:(E.NUM.SUFFIX)<lastChar>
      case @true
         returnValue = number:(E.NUM.SUFFIX)<4>
      end case

   end

return(@null)

*--------------------------------------------
[[GetTimeZoneOffset]]: *// get time zone offset 
*--------------------------------------------

   *// Args in: internal date
   *// time[[ZoneArray]] required for accurate offset.

   if unassigned(time[[ZoneArray]]) then time[[ZoneArray]] = ''
   checkDLSFlag = num(arguments<1>) ;*// date passed in
   checkDLSFlag = checkDLSFlag * (E.GTZO.OFFSET # E.GTZO.DLS.OFFSET)
   checkDLSFlag = checkDLSFlag * len(E.GTZO.START.MONTH)
   checkDLSFlag = checkDLSFlag * len(E.GTZO.START.WEEK)
   checkDLSFlag = checkDLSFlag * len(E.GTZO.START.DAY)
   checkDLSFlag = checkDLSFlag * len(E.GTZO.STOP.MONTH)
   checkDLSFlag = checkDLSFlag * len(E.GTZO.STOP.WEEK)
   checkDLSFlag = checkDLSFlag * len(E.GTZO.STOP.DAY)

   if checkDLSFlag then

      *// find the dls start date for this year
      tz[[InternalDate]] = arguments<1>
      tzYear       = oconv(tz[[InternalDate]],'d4y')
      tz[[StartMonth]] = E.GTZO.START.MONTH
      tz[[StartWeek]]  = E.GTZO.START.WEEK
      tz[[StartDay]]   = E.GTZO.START.DAY

      tz[[StartDate]]  = iconv(tz[[StartMonth]]:'/':tzYear,'dmy') ;*// finds the first
      tz[[StartOffset]] = tz[[StartDay]] - mod(tz[[StartDate]],7)
      if tz[[StartOffset]] < 1 then tz[[StartOffset]] += 7
      tz[[StartDate]] += tz[[StartOffset]]
      tz[[StartDate]] += tz[[StartWeek]] * 7
      loop until oconv(tz[[StartDate]],'dm') = tz[[StartMonth]] do tz[[StartDate]] -= 7 repeat

      *// find the dls stop date for this year
      tz[[StopMonth]] = E.GTZO.STOP.MONTH
      tz[[StopWeek]]  = E.GTZO.STOP.WEEK
      tz[[StopDay]]   = E.GTZO.STOP.DAY

      tz[[StopDate]]  = iconv(tz[[StopMonth]]:'/':tzYear,'dmy')   ;*// finds the first
      tz[[StopOffset]] = tz[[StopDay]] - mod(tz[[StopDate]],7)
      if tz[[StopOffset]] < 1 then tz[[StopOffset]] += 7
      tz[[StopDate]] += tz[[StopOffset]]
      tz[[StopDate]] += tz[[StopWeek]] * 7
      loop until oconv(tz[[StopDate]],'dm') = tz[[StopMonth]] do tz[[StopDate]] -= 7 repeat

      *// return the appropriate time offset for the date.
      begin case
      case tz[[StartDate]] < tz[[StopDate]] & tz[[InternalDate]] > tz[[StartDate]] & tz[[InternalDate]] < tz[[StopDate]] ;*// northern hemisphere dls
         time[[ZoneOffset]] = E.GTZO.DLS.OFFSET
      case tz[[StartDate]] > tz[[StopDate]] & (tz[[InternalDate]] < tz[[StopDate]] ! tz[[InternalDate]] > tz[[StartDate]]) ;*// southern hemisphere dls
         time[[ZoneOffset]] = E.GTZO.DLS.OFFSET
      case @true
         time[[ZoneOffset]] = E.GTZO.OFFSET
      end case
      returnValue = time[[ZoneOffset]]
   end else
      if abs(E.GTZO.OFFSET) > 0 then
         time[[ZoneOffset]] = E.GTZO.OFFSET
         returnValue = time[[ZoneOffset]]
      end else
         returnValue = 0 ;*// return as UTC
      end
   end

return(@null)

*--------------------------------------------
[[GetTimeZoneString]]:  *// return current unix TZ setting - unix
*--------------------------------------------

   if system(91) then ;*// WIN NT needs registry reader
   *// http://msdn.microsoft.com/library/en-us/wmisdk/wmi/win32_timezone.asp
   *// return unix tz string like: EET-10EETDT-11,M10.5.0/2,M3.5.0/2
      open '&SAVEDLISTS&' to f.temp then
         script     = 'Set wbem[[ObjectSet]] = [[GetObject]]("winmgmts:").[[InstancesOf]]("[[Win32_TimeZone]]")'
         script<-1> = 'For Each wbemObject In wbem[[ObjectSet]] : With wbemObject'
         script<-1> = '[[StdOffset]] = 0 - .Bias + .[[StandardBias]]'
         script<-1> = 'SS = mid("+-",1-([[StdOffset]] <0),1)'
         script<-1> = '[[DltOffset]] = 0 - .Bias + .[[DaylightBias]]'
         script<-1> = 'DS = mid("+-",1-([[DltOffset]] <0),1)'
         script<-1> = 'WScript.stdOut.writeline _'
         script<-1> = '"STD" & SS & formatdatetime(timeserial(0,[[StdOffset]],0),4) & _'
         script<-1> = '"DLT" & DS & formatdatetime(timeserial(0,[[DltOffset]],0),4) & _'
         script<-1> = '",M" & .[[DaylightMonth]] & "." & .[[DaylightDay]] & _'
         script<-1> = '"." & .[[DaylightDayOfWeek]] & "/" & .[[DaylightHour]] & _'
         script<-1> = '",M" & .[[StandardMonth]] & "." & .[[StandardDay]] & _'
         script<-1> = '"." & .[[StandardDayOfWeek]] & "/" & .[[StandardHour]]'
         script<-1> = 'End With : Next : Set wbem[[ObjectSet]] = Nothing'
         write script on f.temp,E.TMP.ID then
            execute 'dos /c cscript /[[/B]] /[[/T]]:1 ^&SAVEDLISTS^&/':E.TMP.ID capturing cap
            returnValue = cap<1>
         end else
            returnValue = E.DEFAULT.TZ
         end
         delete f.temp,E.TMP.ID
      end else
         returnValue = E.DEFAULT.TZ
      end
   end else
      execute 'ENVIRONMENT' capturing envStrings
      envVars = fields(envStrings,'=',1)
      locate E.TZ.ENVSTR in envVars setting tzPos then
         returnValue = field(envStrings<tzPos>,'=',2)
      end else
         returnValue = E.DEFAULT.TZ
      end
   end

return(@null)

*--------------------------------------------
[[GetTimeZoneStringFromArray]]:  *// return current unix TZ setting - unix
*--------------------------------------------

   time[[ZoneArray]] = arguments

   if E.GTZO.DESC then
      returnValue = E.GTZO.DESC
      if E.GTZO.OFFSET then
         z = int(E.GTZO.OFFSET[[/E]].SECS.PER.HOUR)
         if z > 0 then returnValue := E.PLUS
         returnValue := z
         if mod(E.GTZO.OFFSET,E.SECS.PER.HOUR) then returnValue := (':':mod(abs(E.GTZO.OFFSET),E.SECS.PER.HOUR)[[/E]].SECS.PER.MIN)
      end else
         returnValue := '0'
      end

      if E.GTZO.DLS.DESC then
         returnValue := E.GTZO.DLS.DESC
         if E.GTZO.DLS.OFFSET then
            z = int(E.GTZO.DLS.OFFSET[[/E]].SECS.PER.HOUR)
            if z > 0 then returnValue := E.PLUS
            returnValue:= z
            if mod(E.GTZO.DLS.OFFSET,E.SECS.PER.HOUR) then returnValue := ':':mod(abs(E.GTZO.DLS.OFFSET),E.SECS.PER.HOUR)[[/E]].SECS.PER.MIN
         end else
            returnValue := '0'
         end

         returnValue := ',M':E.GTZO.START.MONTH
         returnValue := '.':E.GTZO.START.WEEK
         returnValue := '.':E.GTZO.START.DAY:'/2'
         returnValue := ',M':E.GTZO.STOP.MONTH
         returnValue := '.':E.GTZO.STOP.WEEK
         returnValue := '.':E.GTZO.STOP.DAY:'/2'
      end

   end

return(@null)

*--------------------------------------------
[[ParseEpochTime]]:    *// return local UV seconds from 1970-01-01T00:00Z
*--------------------------------------------

   if num(arguments<1>) then
      time[[ToParse]]    = arguments<1>
      time[[ZoneString]] = arguments<2>

      *// convert to local time
      if time[[ToParse]]    = '' then time[[ToParse]] = E.EPOCH.SECS
      if time[[ZoneString]] = '' then
         *// get default
         gosub [[GetTimeZoneString]]:
         time[[ZoneString]] = returnValue
      end
      arguments = time[[ZoneString]]
      gosub [[ParseTimeZoneString]]:

      arguments = int(time[[ToParse/E]].SECS.PER.DAY) + E.EPOCH.OFFSET - (time[[ToParse]] < 0) ;*// internal date - is DLS applicable?
      gosub [[GetTimeZoneOffset]]:
      time[[ToParse]] -= returnValue

      *// get time and date at UTC
      internalDate = int(time[[ToParse/E]].SECS.PER.DAY) + E.EPOCH.OFFSET - (time[[ToParse]] < 0)
      internalTime = mod(time[[ToParse]],E.SECS.PER.DAY) + (E.SECS.PER.DAY * (time[[ToParse]] < 0))

      returnValue  = internalDate:@am:internalTime

   end

return(@null)

*--------------------------------------------
[[ParseISODateTime]]:    *// return Epoch
*--------------------------------------------

* sample of possible formats for ISO date/time
* ISO 8601:2000, The full iso8601 date/time spec as at 2000 may be downloaded here:
* http://www.iso.ch/iso/en/CatalogueDetailPage.CatalogueDetail?CSNUMBER=26780
*   Year:
*      YYYY (eg 1997)
*   Year and month:
*      YYYY-MM (eg 1997-07)
*   Complete date:
*      YYYY-MM-DD (eg 1997-07-16)
*   Complete date plus hours and minutes:
*      YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00)
*   Complete date plus hours, minutes and seconds:
*      YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
*   Complete date plus hours, minutes, seconds and a decimal fraction of a second
*      YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)

   isoString   = upcase(arguments<1>)

   if len(isoString) then

      iso[[DateString]]     = field(isoString,E.ISOTIME.SEP,1)
      iso[[TimeZoneString]] = field(isoString,E.ISOTIME.SEP,2)
      internalTime      = 0
      timeOffset        = 0
      validTime         = @true

      internalDate      = iconv(iso[[DateString]],E.ISODATE.CONV)
      begin case
      case status()         ;*// date not parsed
         validTime    = @false
      case index(iso[[TimeZoneString]],E.PLUS,1)
         internalTime = iconv(field(iso[[TimeZoneString]],E.PLUS,1),E.ISOTIME.CONV)
         if status() then validTime = @false
         timeOffset   = -1 * iconv(field(iso[[TimeZoneString]],E.PLUS,2),E.ISOTIME.CONV)
         if status() then validTime = @false
      case index(iso[[TimeZoneString]],E.MINUS,1)
         internalTime = iconv(field(iso[[TimeZoneString]],E.MINUS,1),E.ISOTIME.CONV)
         if status() then validTime = @false
         timeOffset   = iconv(field(iso[[TimeZoneString]],E.MINUS,2),E.ISOTIME.CONV)
         if status() then validTime = @false
      case index(iso[[TimeZoneString]],E.ISOZONE.SEP,1)
         internalTime = iconv(field(iso[[TimeZoneString]],E.ISOZONE.SEP,1),E.ISOTIME.CONV)
         if status() then validTime = @false
      case len(iso[[TimeZoneString]])
         internalTime = iconv(field(iso[[TimeZoneString]],E.ISOZONE.SEP,1),E.ISOTIME.CONV)
         if status() then validTime = @false
      end case

      if validTime then
         internalDate  = (internalDate - E.EPOCH.OFFSET) * E.SECS.PER.DAY
         internalTime  = internalTime + timeOffset
         returnValue   = internalDate + internalTime
      end
   end else
      validTime = @false
   end

return(@null)

*--------------------------------------------
[[ParseRFCDateTime]]:    *// return Epoch
*--------------------------------------------
* RFC date time string: Sun, 06 Nov 1994 08:49:37 GMT

   time[[ToParse]] = arguments<1>
   if time[[ToParse]] then
      datePart = iconv(field(time[[ToParse]],' ',2,3),'D')
      if not(status()) then
         timePart = iconv(field(time[[ToParse]],' ',5),'MT')
         if not(status()) then
            returnValue = datePart * E.SECS.PER.DAY + timePart
         end
      end
   end

return(@null)

*--------------------------------------------
[[ParseTimeZoneString]]:  *// parse unix TZ env string
*--------------------------------------------

   tzString         = arguments<1>
   returnValue      = ''
   time[[ZoneArray]]  = ''
   *// 1=tz desc, 2=tz offset, 3=dls desc, 4=dls offset
   *// 5=start month,  6=week,  7=day,  8=time
   *// 9=stop  month, 10=week, 11=day, 12=time

   if len(tzString) then
      *// get standard and dls offsets (and descriptors)
      tzPart = field(tzString,',',1)
      maxCharacter = len(tzPart)
      for charPos = 1 to maxCharacter
         character  = tzPart[charPos,1]
         lastDOChar = time[[ZoneArray]][1]

         begin case
         case character = ':'
            time[[ZoneArray]]    := character
         case not(alpha(character) ! num(character))
            time[[ZoneArray]]<-1> = character
         case alpha(lastDOChar) & num(character)
            time[[ZoneArray]]<-1> = character
         case num(lastDOChar) & alpha(character)
            time[[ZoneArray]]<-1> = character
         case @true
            time[[ZoneArray]]    := character
         end case

      next charPos

      if len(E.GTZO.OFFSET) then
         *// convert offset to seconds
         utcOffset = ''
         utc[[OffsetTime]] = E.GTZO.OFFSET
         if utc[[OffsetTime]][1,1] = E.PLUS ! utc[[OffsetTime]][1,1] = E.MINUS then
         *// first char is +/-
            utcOffset = utc[[OffsetTime]][1,1]
            utc[[OffsetTime]] = utc[[OffsetTime]][2,99]
         end
         utc[[OffsetSecs]]  = field(utc[[OffsetTime]],':',1) * E.SECS.PER.HOUR
         utc[[OffsetSecs]] += field(utc[[OffsetTime]],':',2) * E.SECS.PER.MIN
         utc[[OffsetSecs]] += field(utc[[OffsetTime]],':',3)
         E.GTZO.OFFSET = utcOffset:utc[[OffsetSecs]]
      end

      if len(E.GTZO.DLS.OFFSET) then
         *// convert offset to seconds
         utcOffset = ''
         utc[[OffsetTime]] = E.GTZO.DLS.OFFSET
         if utc[[OffsetTime]][1,1] = E.PLUS ! utc[[OffsetTime]][1,1] = E.MINUS then
         *// first char is +/-
            utcOffset = utc[[OffsetTime]][1,1]
            utc[[OffsetTime]] = utc[[OffsetTime]][2,99]
         end
         utc[[OffsetSecs]]  = field(utc[[OffsetTime]],':',1) * E.SECS.PER.HOUR
         utc[[OffsetSecs]] += field(utc[[OffsetTime]],':',2) * E.SECS.PER.MIN
         utc[[OffsetSecs]] += field(utc[[OffsetTime]],':',3)
         E.GTZO.DLS.OFFSET = utcOffset:utc[[OffsetSecs]]
      end else
         if utc[[OffsetSecs]] then ;*// use standard time as default - hour
*!!        E.GTZO.DLS.OFFSET = utcOffset:(utc[[OffsetSecs]] - E.SECS.PER.HOUR)
         end
      end

      if dcount(time[[ZoneArray]],@am) > 4 then
         ndo = ''
         for i=1 to 4; ndo<i> = time[[ZoneArray]]<i>; next i
         time[[ZoneArray]] = ndo
      end

      *// get summer time start params
      tzPart       = field(tzString,',',2)
      if len(tzPart) then
         E.GTZO.START.MONTH = ''
         maxCharacter = len(field(tzPart,'/',1)) ;*// assume 2am start/stop
         for charPos  = 1 to maxCharacter
            character = tzPart[charPos,1]

            begin case
            case character = 'M'
            case character = 'J' ;*!!! julian date - shouldn't happen
            case character = '.'
               time[[ZoneArray]] := @am
            case @true
               time[[ZoneArray]] := character
            end case

         next charPos
         E.GTZO.START.TIME = 7200 ;*// assume 2am

         if dcount(time[[ZoneArray]],@am) > 8 then
            ndo = ''
            for i=1 to 8; ndo<i> = time[[ZoneArray]]<i>; next i
            time[[ZoneArray]] = ndo
         end
      end else
         *// standard default - northern hemisphere
*!!         E.GTZO.START.MONTH = 4
*!!         E.GTZO.START.WEEK  = 1
*!!         E.GTZO.START.DAY   = 0
*!!         E.GTZO.START.TIME  = '02:00'
      end

      *//  get summer time stop params
      tzPart = field(tzString,',',3)
      if len(tzPart) then
         E.GTZO.STOP.MONTH = ''
         maxCharacter = len(field(tzPart,'/',1)) ;*// assume 2am start/stop
         for charPos  = 1 to maxCharacter
            character = tzPart[charPos,1]

            begin case
            case character = 'M'
            case character = 'J' ;*!!! julian date - shouldn't happen
            case character = '.'
               time[[ZoneArray]] := @am
            case @true
               time[[ZoneArray]] := character
            end case

         next charPos
         E.GTZO.STOP.TIME = 7200 ;*// assume 2am

         if dcount(time[[ZoneArray]],@am) > 12 then
            ndo = ''
            for i=1 to 12; ndo<i> = time[[ZoneArray]]<i>; next i
            time[[ZoneArray]] = ndo
         end
      end else
         *// standard default - northern hemisphere
*!!         E.GTZO.STOP.MONTH = 10
*!!         E.GTZO.STOP.WEEK  = 5
*!!         E.GTZO.STOP.DAY   = 0
*!!         E.GTZO.STOP.TIME  = '02:00'
      end

      returnValue = time[[ZoneArray]]

   end

return(@null)

*--------------------------------------------
[[SetDateFormat]]:       *// 'ON' = International, 'OFF' = US
*--------------------------------------------

   switch = upcase(trim(arguments<1,1>))

   begin case
   case switch = 'OFF'
      set[[DateFormatCmd]] = 'DATE.FORMAT OFF'

   case switch[1,1] = 'D'  ;*// change default date output format
      set[[DateFormatCmd]] = 'DATE.FORMAT ':arguments<1,1>

   case @true
      set[[DateFormatCmd]] = 'DATE.FORMAT ON'

   end case

   execute set[[DateFormatCmd]] capturing cap
   gosub [[GetDateFormat]]: ;*// return current date format

return(@null)

*--------------------------------------------
[[ThatsAllFolks]]: end
*--------------------------------------------

Related Pages


Open Questions

Question 1:
I have a field that holds the number of milliseconds since 1/1/1970 GMT, which is how Java handles dates internally. I know UniData stores dates as the number of days since 12/31/1967. I need a UniBasic utility that can convert between these formats.

Answer 1:

DateUtility does quite a bit more but the functionality you want is included. Don't forget to change the default TZ string if you are not from nsw or vic in australia! ;)

This was built for UV on unix and Win NT will require a registry reader if you nead to check a users timezone.

Also, I'm not sure if UD supports system(99).


To call as a function:
      PROGRAM SOME.PROGRAM
      DEFFUN DATE.UTIL(Method,Arguments) CALLING '*DATE.UTILITY'
      ...
      MY.METHOD    = 'parse[[EpochTime]]'
      MY.ARGUMENTS = javatime:@AM:timezone
      U2.DATE.TIME = DATE.UTIL(MY.METHOD,MY.ARGUMENTS)
      U2.DATE = U2.DATE.TIME<1>
      U2.TIME = U2.DATE.TIME<2>

or call as an I-type
      0001 I
      0002 subr('*DATE.UTILITY','parse[[EpochTime]]',JAVADATE.FIELD)

Cheers, Stuart


Answer 1: - Option 2


Converting between pick dates and unix or java dates is fairly straight forward. It is basically as follows.

UNIXTIME = (DATE() - 732) * 86400

This will return you the unix time in GMT, which is how unix stores all dates. You may need to convert to local time so just add the time diff for your timezone.

Gordon.


HomePage>>SourceCode>>BasicSource>>DateUtility