DateUtility
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
- Also, see Ray Wurlod's article, Date Conversion Demystified
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