Converting from dayTimeDuration to yearMonthDuration is a really strange thing
to do in general
( so I am guessing that is why its not made easy !)
Its very use case specific if 1 day difference counts as 0 or 1 month.
Leap years start to matter as well - depending on if you mean a duration of a
month of days ...
or a duration a unit (month)
Start adding and subtracting days and you can end ujp all over the place -
Even ignoring DST and TZ it can be a mess, add DST and TZ and *location* its
*really* a mess
But that is why you found you cant really use days ... or if you do you need to
use something like functx:days-in-month()
FYI: Heres a simpler version of the function using some functx module functions
(included with ML)
It was interesting to try to shorten it and see where leap year changes make a
difference ... they dont if you do it right (your code handled leap years
implicitly by only doing month arithmetic).
Since your not using Days, your certianly not going to be using hours, minuts
or seconds so I changed to xs:date
It could be further simplified by using xs:gMonthYear ... since anything else
is extra information that leads one to think
days actually matter ...
( I switch $d1,$d2 - which is the only way I could make my brain work ... e.g.
duration = $d1 to $d2 aka $d2-$d1
)
---------------------------------------
xquery version "1.0-ml";
import module namespace functx="http://www.functx.com" at
"/MarkLogic/functx/functx-1.0-nodoc-2007-01.xqy";
declare variable $onemonth := xs:yearMonthDuration("P1M"); (: save typing :)
(: Fully type the function signature to make sure your not mixing
dayTimeDuration and yearMonthDuration
Much of the complexity is handling $d1 < $d2 ... which may not really be
needed
:)
declare function local:yearMonthDuration( $d1 as xs:date , $d2 as xs:date ) as
xs:yearMonthDuration
{
(: obscure way to test if d1 and d2 are the same month and year - avoid
infinite recursion :)
if( ($d1 cast as xs:gYearMonth) eq ( $d2 cast as xs:gYearMonth ) ) then
xs:yearMonthDuration("P0M")
else
if( $d2 lt $d1 ) then
-1 * local:yearMonthDuration( $d2 , $d1 )
else
local:yearMonthDuration( $d1 + $onemonth , $d2 ) + $onemonth
};
local:yearMonthDuration( xs:date("2016-02-29") , xs:date("2016-03-01") ),
local:yearMonthDuration( xs:date("2015-02-28") , xs:date("2015-03-01") ),
local:yearMonthDuration( xs:date("2024-03-27"), xs:date("2015-01-27") ),
local:yearMonthDuration( xs:date("2016-02-29") , xs:date("2021-02-28") ),
local:yearMonthDuration( xs:date("2016-02-29"), xs:date("2016-02-28") ),
local:yearMonthDuration( xs:date("2016-01-31"), xs:date("2016-02-28") )
---- Simplier if you can asume $d1 < $d2
declare function local:yearMonthDuration( $d1 as xs:date , $d2 as xs:date ) as
xs:yearMonthDuration
{
if( ($d1 cast as xs:gYearMonth ) ge ( $d2 cast as xs:gYearMonth ) )
then xs:yearMonthDuration("P0M")
else
$onemonth + local:yearMonthDuration( $d1 + $onemonth , $d2 )
};
-----------------------------------------------------------------------------
David Lee
Lead Engineer
MarkLogic Corporation
[email protected]
Phone: +1 812-482-5224
Cell: +1 812-630-7622
www.marklogic.com<http://www.marklogic.com/>
From: [email protected]
[mailto:[email protected]] On Behalf Of William Sawyer
Sent: Thursday, February 19, 2015 9:48 PM
To: MarkLogic Developer Discussion
Subject: Re: [MarkLogic Dev General] xs:yearMonthDuration in 1.0-ml
Thank you for the quick responses. After reviewing those approaches and the
not getting the expected results for my use case. I came up with the following
function. For my use case I needed to ignore days and calculate the duration
based solely from the year and month of each date.
declare function yearMonthDuration($date1 as xs:dateTime, $date2 as
xs:dateTime) {
let $negative := if ( $date1 lt $date2 ) then ( "-" ) else ()
let $year1 := fn:year-from-dateTime($date1)
let $year2 := fn:year-from-dateTime($date2)
let $month1 := fn:month-from-dateTime($date1)
let $month2 := fn:month-from-dateTime($date2)
let $totalMonths := fn:abs( (($year1 - $year2 ) * 12) + ($month1 - $month2)
)
let $years := $totalMonths idiv 12
let $months := $totalMonths mod 12
return (
xs:yearMonthDuration( $negative || "P" || $years || "Y" || $months ||
"M" )
)
};
-Will
On Thu, Feb 19, 2015 at 6:10 PM, Danny Sokolsky
<[email protected]<mailto:[email protected]>> wrote:
I'm not sure if this helps, but here is something similar using a technique
like the functx function does:
http://www.xqueryfunctions.com/xq/functx_yearmonthduration.html
xquery version "1.0-ml";
let $date1 := xs:dateTime(xs:date("2015-03-27"))
let $date2 := xs:dateTime(xs:date("2024-03-27"))
let $test := xs:duration($date2 - $date1)
let $years := fn:round(fn:days-from-duration($test) div 365)
let $months := fn:round((fn:days-from-duration($test) div 365) div 12)
return
( xs:yearMonthDuration('P1M') * $months +
xs:yearMonthDuration('P1Y') * $years)
But duration math is strange so I might be doing something wrong.
-Danny
-----Original Message-----
From:
[email protected]<mailto:[email protected]>
[mailto:[email protected]<mailto:[email protected]>]
On Behalf Of Florent Georges
Sent: Thursday, February 19, 2015 4:26 PM
To: MarkLogic Developer Discussion
Subject: Re: [MarkLogic Dev General] xs:yearMonthDuration in 1.0-ml
That function never found its way from the working drafts to the
recommendation. You can achieve something similar by using the following (yes,
by using the average number of days in a month, which is fragile and works fine
only for large numbers of days, over several
years):
let $days := xs:dayTimeDuration("-P3278D") div xs:dayTimeDuration('P1D')
let $months := $days idiv 30.43684991666667
return
$months * xs:yearMonthDuration('P1M')
Regards,
--
Florent Georges
http://fgeorges.org/
http://h2oconsulting.be/
On 20 February 2015 at 01:02, William Sawyer wrote:
> I have two dates I am trying to subtract and get a xs:yearMonthDuration. In
> "0.9-ml" there is a function you can call but in "1.0-ml" the documentation
> says to subtract the dates. But how do I specify that I want a
> xs:yearMonthDuration instead of a xs:dayTimeDuration.
> MarkLogic Version: 7.0-2.3
>
> Thanks,
> -Will
>
>
> xquery version "1.0-ml";
>
> let $date1 := xs:dateTime(xs:date("2015-03-27"))
> let $date2 := xs:dateTime(xs:date("2024-03-27"))
> let $test := $date1 - $date2
> return $test
> ***************
> xs:dayTimeDuration("-P3288D")
>
>
> xquery version "0.9-ml"
>
> let $date1 := xs:dateTime(xs:date("2015-03-27"))
> let $date2 := xs:dateTime(xs:date("2024-03-27"))
> let $test := fn:subtract-dateTimes-yielding-yearMonthDuration($date1,
> $date2)
> return $test
> **************
> xs:yearMonthDuration("-P8Y11M")
>
> _______________________________________________
> General mailing list
> [email protected]<mailto:[email protected]>
> http://developer.marklogic.com/mailman/listinfo/general
>
--
Florent Georges
http://fgeorges.org/
http://h2oconsulting.be/
_______________________________________________
General mailing list
[email protected]<mailto:[email protected]>
http://developer.marklogic.com/mailman/listinfo/general
_______________________________________________
General mailing list
[email protected]<mailto:[email protected]>
http://developer.marklogic.com/mailman/listinfo/general
_______________________________________________
General mailing list
[email protected]
http://developer.marklogic.com/mailman/listinfo/general