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

Reply via email to