FWIW a slight generalisation:
use AppleScript version "2.4"
use framework "Foundation"
use framework "JavaScriptCore"
use scripting additions
-- Unit keys: any gapless substring of "ymwdhns" (years, months, month-weeks, days, hours, mins, secs)
-- NB 'n' = minutes
on run
set xs to {"1951-02-01", "1950-02-01", "1940-02-01", "0001-02-01"}
-- "ym" = years months
set units to "ym"
map(compose(timeDiffString(units)'s |λ|(current date), dateFromISO), xs)
--> {"67y 11mo", "68y 11mo", "78y 11mo", "2,017y 11mo"}
-- "ymw" = years months weeks
set units2 to "ymw"
map(compose(timeDiffString(units2)'s |λ|(current date), dateFromISO), xs)
--> {"67y 11mo 1w", "68y 11mo 1w", "78y 11mo 1w", "2,017y 11mo 1w"}
end run
-- String of Unit keys -> To Date -> From Date -> Interval String
-- timeDiffString :: String -> Date -> Date -> String
on timeDiffString(ks)
set recUnitOptions to {y:4, m:8, w:4096, d:16, h:32, n:64, s:128}
set fmtr to current application's NSDateComponentsFormatter's new()
script unitFlags
on |λ|(a, k)
set mb to lookupDict(k, recUnitOptions)
if Nothing of mb then
a
else
a + (Just of mb)
end if
end |λ|
end script
fmtr's setAllowedUnits:foldl(unitFlags, 0, characters of ks)
script
on |λ|(dteTo)
script
on |λ|(dteFrom)
(fmtr's stringFromDate:dteFrom toDate:dteTo) as text
end |λ|
end script
end |λ|
end script
end timeDiffString
-- GENERIC FUNCTIONS -------------------------------------------------
-- https://github.com/RobTrew/prelude-applescript
-- bindJSC (>>=) :: JSC a -> (a -> JSC b) -> JSC b
on bindJSC(mJSC, mf)
{type:"JSC", jsc:mJSC's jsc, value:¬
unwrap((mJSC's jsc's ¬
evaluateScript:(|λ|(value of mJSC) of mReturn(mf)))'s toObject())}
end bindJSC
-- compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
on compose(f, g)
script
property mf : mReturn(f)
property mg : mReturn(g)
on |λ|(x)
mf's |λ|(mg's |λ|(x))
end |λ|
end script
end compose
-- dateFromISO :: ISO8601 String -> Date
on dateFromISO(s)
script dateFromString
on |λ|(v)
"new Date('" & v & "')"
end |λ|
end script
value of bindJSC(pureJSC(s), dateFromString)
end dateFromISO
-- foldl :: (a -> b -> a) -> a -> [b] -> a
on foldl(f, startValue, xs)
tell mReturn(f)
set v to startValue
set lng to length of xs
repeat with i from 1 to lng
set v to |λ|(v, item i of xs, i, xs)
end repeat
return v
end tell
end foldl
-- Just :: a -> Maybe a
on Just(x)
{type:"Maybe", Nothing:false, Just:x}
end Just
-- lookupDict :: a -> Dict -> Maybe b
on lookupDict(k, dct)
set ca to current application
set v to (ca's NSDictionary's dictionaryWithDictionary:dct)'s objectForKey:k
if v ≠ missing value then
Just(item 1 of ((ca's NSArray's arrayWithObject:v) as list))
else
Nothing()
end if
end lookupDict
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
tell mReturn(f)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs, i, xs)
end repeat
return lst
end tell
end map
-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
if class of f is script then
f
else
script
property |λ| : f
end script
end if
end mReturn
-- pureJSC :: a -> {type::String, jsc::JSContext, value::a}
on pureJSC(x)
{type:"JSC", jsc:current application's JSContext's new(), value:x}
end pureJSC
-- Nothing :: Maybe a
on Nothing()
{type:"Maybe", Nothing:true}
end Nothing
-- unwrap :: NSObject -> a
on unwrap(objCValue)
if objCValue is missing value then
missing value
else
set ca to current application
item 1 of ((ca's NSArray's arrayWithObject:objCValue) as list)
end if
end unwrap