While trying to implement some checks in Oracle database with use of XQuery
I need to compare if two dates are the same, but can't do that on obvious manner because casting to xs:date
don't really remove time part of value from xs:dateTime
.
Query per se seems to work good in another environment (e.g. http://www.xpathtester.com/xquery ).
Is I missed something important, or this case is just a bug and need special workaround (converting to string values for compare, compare years, months and dates of both dates separately and so on)?
A small example ...
Suppose we have a simple XML:
<root>
<date_value>2015-09-11T15:25:55</date_value>
</root>
and want compare date_value
with fixed value xs:date('2015-09-11')
ignoring time part.
First, convert content of the node to desired type and remove time part by casting it to xs:date
:
xs:date(xs:dateTime($doc/root/date_value))
If we select this value with XMLQuery()
while passing document above as $doc
, we got expected output:
2015-09-11+00:00
Ok. Seems that time part removed, but comparison fails:
xs:date(xs:dateTime($doc/root/date_value)) eq xs:date('2015-09-11')
returns false
, and if we try to look at difference between values in expression instead of comparing them:
xs:date(xs:dateTime($doc/root/date_value)) - xs:date('2015-09-11')
we see 'PT15H25M55S'
, which are exactly matches time part of date_value
.
Query with all expressions above for testing:
select
XMLCast(
XMLQuery( column_value
passing
xmltype(q'[
<root>
<date_value>2015-09-11T15:25:55</date_value>
</root>
]') as "doc"
returning content
)
as varchar2(4000)
) result_value,
column_value expression
from
table(sys.odcivarchar2list(
q'[ xs:date(xs:dateTime($doc/root/date_value)) ]',
q'[ xs:date('2015-09-11') ]',
q'[ xs:date(xs:dateTime($doc/root/date_value)) eq xs:date('2015-09-11') ]',
q'[ xs:date(xs:dateTime($doc/root/date_value)) - xs:date('2015-09-11') ]'
))
Behavior reproduced on this Oracle versions:
Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
PL/SQL Release 12.1.0.2.0 - Production
CORE 12.1.0.2.0 Production
TNS for IBM/AIX RISC System/6000: Version 12.1.0.2.0 - Production
NLSRTL Version 12.1.0.2.0 - Production
and
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE 11.2.0.3.0 Production
TNS for IBM/AIX RISC System/6000: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production
Thanks to collapsar and Alex Poole for answers which give me main idea about correct workaround. But trying to explain core of problem I oversimplified our use case which includes some date arithmetic and real-world workaround would look like a query below.
select
XMLCast(
XMLQuery(
q'[
let
$date1 := fn:dateTime(
adjust-date-to-timezone(
xs:date(xs:dateTime($doc/root/date_value)),
()
),
adjust-time-to-timezone( xs:time('00:00'), ())
),
$date2 := fn:dateTime(
adjust-date-to-timezone(
xs:date(xs:dateTime($doc/root/date_value2)),
()
),
adjust-time-to-timezone( xs:time('00:00'), ())
)
return
$date1 + xs:yearMonthDuration('P1Y') - xs:dayTimeDuration('P1D')
eq
$date2
]'
passing
xmltype(q'[
<root>
<date_value>2015-09-11T01:02:03-11:00</date_value>
<date_value2>2016-09-10T10:20:30+13:00</date_value2>
</root>
]') as "doc"
returning content
)
as varchar2(4000)
) result_value
from
dual
Lifting the literal date value to a dateTime value does the trick (extracting the proper time-of-day offset from the supplied dateTime value ):
xs:dateTime($doc/root/date_value) eq fn:dateTime(xs:date('2015-09-11'), xs:time(xs:dateTime($doc/root/date_value)))
This solution will also work for inputs lexicalized as dates only.