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 d...@marklogic.com Phone: +1 812-482-5224 Cell: +1 812-630-7622 www.marklogic.com<http://www.marklogic.com/> From: general-boun...@developer.marklogic.com [mailto:general-boun...@developer.marklogic.com] 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 <danny.sokol...@marklogic.com<mailto:danny.sokol...@marklogic.com>> 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: general-boun...@developer.marklogic.com<mailto:general-boun...@developer.marklogic.com> [mailto:general-boun...@developer.marklogic.com<mailto:general-boun...@developer.marklogic.com>] 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 > General@developer.marklogic.com<mailto:General@developer.marklogic.com> > http://developer.marklogic.com/mailman/listinfo/general > -- Florent Georges http://fgeorges.org/ http://h2oconsulting.be/ _______________________________________________ General mailing list General@developer.marklogic.com<mailto:General@developer.marklogic.com> http://developer.marklogic.com/mailman/listinfo/general _______________________________________________ General mailing list General@developer.marklogic.com<mailto:General@developer.marklogic.com> http://developer.marklogic.com/mailman/listinfo/general
_______________________________________________ General mailing list General@developer.marklogic.com http://developer.marklogic.com/mailman/listinfo/general