DateUtility: Difference between revisions
From Pickwiki
Jump to navigationJump to search
mNo edit summary |
(No difference)
|
Revision as of 00:30, 18 February 2007
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'
*// timeZoneArray
*// 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 timeZoneArray<1>,
E.GTZO.OFFSET to timeZoneArray<2>,
E.GTZO.DLS.DESC to timeZoneArray<3>,
E.GTZO.DLS.OFFSET to timeZoneArray<4>,
E.GTZO.START.MONTH to timeZoneArray<5>,
E.GTZO.START.WEEK to timeZoneArray<6>,
E.GTZO.START.DAY to timeZoneArray<7>,
E.GTZO.START.TIME to timeZoneArray<8>,
E.GTZO.STOP.MONTH to timeZoneArray<9>,
E.GTZO.STOP.WEEK to timeZoneArray<10>,
E.GTZO.STOP.DAY to timeZoneArray<11>,
E.GTZO.STOP.TIME to timeZoneArray<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 timeZoneString]
*// return: epoch time - seconds from epoch
gosub GetEpochTime:
case method = 'GetISODateTime' ! method = 'getISODateTime.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 timeZoneArray)
*// return users current TZ string
gosub GetTimeZoneStringFromArray:
case method = 'ParseEpochTime'
*// args: [UTCSeconds][@am timeZoneString]
*// 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 setDateFormat
*// 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
*--------------------------------------------
getDateFormatCmd = 'DATE.FORMAT INFORM'
execute getDateFormatCmd 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>
timeZoneString = arguments<3>
if internalDate = '' then internalDate = date()
if internalTime = '' then internalTime = time()
if timeZoneString = '' then
*// get default
gosub GetTimeZoneString:
timeZoneString = returnValue
end
arguments = timeZoneString
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(timeZoneString) then timeZoneString = arguments<3>
if internalDate = '' then internalDate = date()
if internalTime = '' then internalTime = time()
if timeZoneString = '' then
*// get default
gosub GetTimeZoneString:
timeZoneString = returnValue
end
isoDate = oconv(internalDate,E.ISODATE.CONV)
if not(status()) then
isoTime = oconv(internalTime,E.RFCTIME.CONV)
if not(status()) then
if len(timeZoneString) then
arguments = timeZoneString
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()
dayOfWeek = oconv(internalDate, 'dw')
if status() then ;*// test for bad conversion
weekNumber = '-1'
end else
thisThursday = internalDate - dayOfWeek + 4 ;* calculate date for that Thursday
dayOfYear = oconv(thisThursday,'dj')
weekNumber = int((dayOfYear + 6) / 7) ;*// ta WOL
end
returnValue = weekNumber
return(@null)
*--------------------------------------------
GetISOZone: *// return ISO time zone offset
*--------------------------------------------
localOffsetSecs = arguments
isoZone = oconv(abs(localOffsetSecs),E.ISOTIME.CONV)
begin case
case status() ;*// invalid oconv 15-Apr-2003 return default offset for melbourne
returnValue = '+10:00'
case abs(localOffsetSecs) >= E.SECS.PER.DAY ;*// invalid offset value 15-Apr-2003 return default offset for melbourne
returnValue = '+10:00'
case localOffsetSecs < 0
returnValue = E.PLUS:isoZone
case localOffsetSecs > 0
returnValue = E.MINUS:isoZone
case localOffsetSecs = 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
*// timeZoneArray required for accurate offset.
if unassigned(timeZoneArray) then timeZoneArray = ''
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
tzInternalDate = arguments<1>
tzYear = oconv(tzInternalDate,'d4y')
tzStartMonth = E.GTZO.START.MONTH
tzStartWeek = E.GTZO.START.WEEK
tzStartDay = E.GTZO.START.DAY
tzStartDate = iconv(tzStartMonth:'/':tzYear,'dmy') ;*// finds the first
tzStartOffset = tzStartDay - mod(tzStartDate,7)
if tzStartOffset < 1 then tzStartOffset += 7
tzStartDate += tzStartOffset
tzStartDate += tzStartWeek * 7
loop until oconv(tzStartDate,'dm') = tzStartMonth do tzStartDate -= 7 repeat
*// find the dls stop date for this year
tzStopMonth = E.GTZO.STOP.MONTH
tzStopWeek = E.GTZO.STOP.WEEK
tzStopDay = E.GTZO.STOP.DAY
tzStopDate = iconv(tzStopMonth:'/':tzYear,'dmy') ;*// finds the first
tzStopOffset = tzStopDay - mod(tzStopDate,7)
if tzStopOffset < 1 then tzStopOffset += 7
tzStopDate += tzStopOffset
tzStopDate += tzStopWeek * 7
loop until oconv(tzStopDate,'dm') = tzStopMonth do tzStopDate -= 7 repeat
*// return the appropriate time offset for the date.
begin case
case tzStartDate < tzStopDate & tzInternalDate > tzStartDate & tzInternalDate < tzStopDate ;*// northern hemisphere dls
timeZoneOffset = E.GTZO.DLS.OFFSET
case tzStartDate > tzStopDate & (tzInternalDate < tzStopDate ! tzInternalDate > tzStartDate) ;*// southern hemisphere dls
timeZoneOffset = E.GTZO.DLS.OFFSET
case @true
timeZoneOffset = E.GTZO.OFFSET
end case
returnValue = timeZoneOffset
end else
if abs(E.GTZO.OFFSET) > 0 then
timeZoneOffset = E.GTZO.OFFSET
returnValue = timeZoneOffset
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 wbemObjectSet = GetObject("winmgmts:").InstancesOf("Win32_TimeZone")'
script<-1> = 'For Each wbemObject In wbemObjectSet : 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 wbemObjectSet = 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
*--------------------------------------------
timeZoneArray = 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
timeToParse = arguments<1>
timeZoneString = arguments<2>
*// convert to local time
if timeToParse = '' then timeToParse = E.EPOCH.SECS
if timeZoneString = '' then
*// get default
gosub GetTimeZoneString:
timeZoneString = returnValue
end
arguments = timeZoneString
gosub ParseTimeZoneString:
arguments = int(timeToParse/E.SECS.PER.DAY) + E.EPOCH.OFFSET - (timeToParse < 0) ;*// internal date - is DLS applicable?
gosub GetTimeZoneOffset:
timeToParse -= returnValue
*// get time and date at UTC
internalDate = int(timeToParse/E.SECS.PER.DAY) + E.EPOCH.OFFSET - (timeToParse < 0)
internalTime = mod(timeToParse,E.SECS.PER.DAY) + (E.SECS.PER.DAY * (timeToParse < 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
isoDateString = field(isoString,E.ISOTIME.SEP,1)
isoTimeZoneString = field(isoString,E.ISOTIME.SEP,2)
internalTime = 0
timeOffset = 0
validTime = @true
internalDate = iconv(isoDateString,E.ISODATE.CONV)
begin case
case status() ;*// date not parsed
validTime = @false
case index(isoTimeZoneString,E.PLUS,1)
internalTime = iconv(field(isoTimeZoneString,E.PLUS,1),E.ISOTIME.CONV)
if status() then validTime = @false
timeOffset = -1 * iconv(field(isoTimeZoneString,E.PLUS,2),E.ISOTIME.CONV)
if status() then validTime = @false
case index(isoTimeZoneString,E.MINUS,1)
internalTime = iconv(field(isoTimeZoneString,E.MINUS,1),E.ISOTIME.CONV)
if status() then validTime = @false
timeOffset = iconv(field(isoTimeZoneString,E.MINUS,2),E.ISOTIME.CONV)
if status() then validTime = @false
case index(isoTimeZoneString,E.ISOZONE.SEP,1)
internalTime = iconv(field(isoTimeZoneString,E.ISOZONE.SEP,1),E.ISOTIME.CONV)
if status() then validTime = @false
case len(isoTimeZoneString)
internalTime = iconv(field(isoTimeZoneString,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
timeToParse = arguments<1>
if timeToParse then
datePart = iconv(field(timeToParse,' ',2,3),'D')
if not(status()) then
timePart = iconv(field(timeToParse,' ',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 = ''
timeZoneArray = ''
*// 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 = timeZoneArray[1]
begin case
case character = ':'
timeZoneArray := character
case not(alpha(character) ! num(character))
timeZoneArray<-1> = character
case alpha(lastDOChar) & num(character)
timeZoneArray<-1> = character
case num(lastDOChar) & alpha(character)
timeZoneArray<-1> = character
case @true
timeZoneArray := character
end case
next charPos
if len(E.GTZO.OFFSET) then
*// convert offset to seconds
utcOffset = ''
utcOffsetTime = E.GTZO.OFFSET
if utcOffsetTime[1,1] = E.PLUS ! utcOffsetTime[1,1] = E.MINUS then
*// first char is +/-
utcOffset = utcOffsetTime[1,1]
utcOffsetTime = utcOffsetTime[2,99]
end
utcOffsetSecs = field(utcOffsetTime,':',1) * E.SECS.PER.HOUR
utcOffsetSecs += field(utcOffsetTime,':',2) * E.SECS.PER.MIN
utcOffsetSecs += field(utcOffsetTime,':',3)
E.GTZO.OFFSET = utcOffset:utcOffsetSecs
end
if len(E.GTZO.DLS.OFFSET) then
*// convert offset to seconds
utcOffset = ''
utcOffsetTime = E.GTZO.DLS.OFFSET
if utcOffsetTime[1,1] = E.PLUS ! utcOffsetTime[1,1] = E.MINUS then
*// first char is +/-
utcOffset = utcOffsetTime[1,1]
utcOffsetTime = utcOffsetTime[2,99]
end
utcOffsetSecs = field(utcOffsetTime,':',1) * E.SECS.PER.HOUR
utcOffsetSecs += field(utcOffsetTime,':',2) * E.SECS.PER.MIN
utcOffsetSecs += field(utcOffsetTime,':',3)
E.GTZO.DLS.OFFSET = utcOffset:utcOffsetSecs
end else
if utcOffsetSecs then ;*// use standard time as default - hour
*!! E.GTZO.DLS.OFFSET = utcOffset:(utcOffsetSecs - E.SECS.PER.HOUR)
end
end
if dcount(timeZoneArray,@am) > 4 then
ndo = ''
for i=1 to 4; ndo<i> = timeZoneArray<i>; next i
timeZoneArray = 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 = '.'
timeZoneArray := @am
case @true
timeZoneArray := character
end case
next charPos
E.GTZO.START.TIME = 7200 ;*// assume 2am
if dcount(timeZoneArray,@am) > 8 then
ndo = ''
for i=1 to 8; ndo<i> = timeZoneArray<i>; next i
timeZoneArray = 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 = '.'
timeZoneArray := @am
case @true
timeZoneArray := character
end case
next charPos
E.GTZO.STOP.TIME = 7200 ;*// assume 2am
if dcount(timeZoneArray,@am) > 12 then
ndo = ''
for i=1 to 12; ndo<i> = timeZoneArray<i>; next i
timeZoneArray = 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 = timeZoneArray
end
return(@null)
*--------------------------------------------
SetDateFormat: *// 'ON' = International, 'OFF' = US
*--------------------------------------------
switch = upcase(trim(arguments<1,1>))
begin case
case switch = 'OFF'
setDateFormatCmd = 'DATE.FORMAT OFF'
case switch[1,1] = 'D' ;*// change default date output format
setDateFormatCmd = 'DATE.FORMAT ':arguments<1,1>
case @true
setDateFormatCmd = 'DATE.FORMAT ON'
end case
execute setDateFormatCmd capturing cap
gosub GetDateFormat: ;*// return current date format
return(@null)
*--------------------------------------------
ThatsAllFolks: end
*--------------------------------------------
- Also, see Ray Wurlod's article, Date Conversion Demystified
HomePage>>SourceCode>>BasicSource>>DateUtility