How do I do time comparison on IRIS 2020.1 using the W3C format?
I am trying to compare times on an IRIS instance. The times are in the W3C format (https://www.w3.org/TR/NOTE-datetime). Are there any in-built helper functions in IRIS to support time comparisons in this format or will I need to parse it using functions such as $zdateh and $ztimeh to do the comparisons?
Comments
Hey Keshav,
I'm not aware of a built-in time comparison function. An alternative to using zdateh and ztimeh may be to use ##class(Ens.Util.Time).ConvertDateTime, but I think for the actual comparison you'll need to convert to a matching target format.
Format specifications are documented here:
Perhaps DATEDIFF from a previous question on the developer community would work?
ISO 8601 time format is either yyyy-mm-ddZhh:mm:ss (UTC time) or yyyy-mm-ddThh:mm:ss+TZ (local time)
where TZ is the time zone.
If both times are UTC time, then just compare.
If the times are local times and both times are in the same TZ then just compare
in elsecase either convert both times to UTC or one of the times to the TZ of the another time.
Then just compare.
The compare function is just a $select(), nothing more:
t1, t2 are the times, then
if $select(t1]t2:"t1 later", t1=t2:"equal", 1:"t1 is earlier")or more general:
set result=$select(t1]t2:1, t1=t2:0, 1:-1)
if result>0 write "t1 is later then t2"
if result>=0 write "t1 is later or equal to t2"
...@Vic Sun @Julius Kavay Thanks! I wanted to stay away from using Ens classes because by namespace may not map from ENSLIB. I did the following for conversion and used DATEDIFF for comparison:
ClassMethod ConvertW3CToTimestampUTC(pDateTime As %String) As %TimeStamp
{
Set date = $Piece(pDateTime, "T", 1)
Set time = $Piece(pDateTime, "T", 2)
// Validation
Set containsPlus = ($Find(time, "+") > 0)
Set containsMinus = ($Find(time, "-") > 0)
Set containsZ = ($Find(time, "Z") > 0)
If ((containsPlus + containsZ + containsMinus) > 1) {
$$$ThrowStatus($$$ERROR($$$GeneralError, "Invalid time format. Cannot contain multiple timezone offset formats"))
}
If ((containsPlus + containsZ + containsMinus) = 0) {
$$$ThrowStatus($$$ERROR($$$GeneralError, "Invalid time format. Missing timezone offset"))
}
Set parsedTime = ""
Set dateAdd = ""
// Obtain UTC time
If containsZ {
Set parsedTime = $Extract(time, 1, *-1)
} Else {
Set operator = $Case(containsPlus, 1: "+", : "-")
Set absTime = $Piece(time, operator, 1)
Set offset = $Piece(time, operator, 2)
Set $ListBuild(offsetHours, offsetMinutes) = $ListFromString(offset, ":")
Set offsetHours = "-"_operator_offsetHours
Set offsetMinutes = "-"_operator_offsetMinutes
Set $ListBuild(hours, minutes, seconds) = $ListFromString(absTime, ":")
Set computedMinutes = minutes + offsetMinutes
Set netMinutes = computedMinutes#60
Set hoursAdd = $Case((computedMinutes < 0), 1: -1, : $Case((computedMinutes >= 60), 1: 1, : 0))
Set computedHours = (hours + offsetHours + hoursAdd)
Set netHours = computedHours#24
Set dateAdd = $Case((computedHours < 0), 1: -1, : $Case((computedHours >= 24), 1: 1, : 0))
// Add padding of 0's if needed
If ($Length(netHours) = 1) {
Set netHours = "0"_netHours
}
If ($Length(netMinutes) = 1) {
Set netMinutes = "0"_netMinutes
}
Set parsedTime = netHours_":"_netMinutes_":"_seconds
}
Set dateTime = date_" "_parsedTime
If (dateAdd '= "") {
// Adjust date
Set dateTime = $System.SQL.DATEADD("day", dateAdd, dateTime)
}
Return dateTime
}First, I think, you should check the above method...
You use the date variable two times
set date = $piece(...)
return date_" "...
I'm pretty shure, if the time in Vienna (Europe) is 00:30:00 then the coresponding UTC time is 23:30:00 previous day (standard time). But you never change the date variable!
As a timestamp:
2020-08-31T00:30:00+01:00 (Vienna, 31. August)
2020-08-30Z23:30:00 (UTC, 30.August)
Second (but this is just my very privete view), checking time(stamp) format should be done BEFORE calling the ConvertW3CToTimestampUTC() method and not in the method itself.
The holy trinity of programming is:
1) accept data (data input)
2) check the data
3) if the data is OK use it, else back to input (or return an error)
If you accpet data without checking, it means, every time, you intend to use those data, you have to check them again and again (as above), wich is not very efficient.
@Julius Kavay Thanks for catching that! I had only tested with UTC time when I posted that. Have updated the code in the previous comment to account for roll over of hours/days.
Regarding your second point, I agree that validation should be done before processing the data. I have moved validation to the beginning of the method. Currently I don't have a use case for having validation be in its own method but if I do later, it would be easy to refactor it out from this method.