Does anyone have a good suggestion for an already built duration or date calculator macro?

I want to calculate the difference between 2 dates for example

**Start date**: 06/23/2022 -- **End Date**: 08/22/2022

Result 61 days or 2 months 1 day etc.

Does anyone have a good suggestion for an already built duration or date calculator macro?

I want to calculate the difference between 2 dates for example

**Start date**: 06/23/2022 -- **End Date**: 08/22/2022

Result 61 days or 2 months 1 day etc.

It depends a bit on how you want to capture or enter the two dates.

In an *Execute JavaScript for Automation* action, some of the basic elements might have this kind of shape:

```
(() => {
"use strict";
return (new Date("08/22/2022") - new Date("06/23/2022")) / (
1000 * 60 * 60 * 24
);
})();
```

(JS date differences are integers representing milliseconds, and the DateTime values created above are midnight at the start of the calendar day)

For example:

Compound difference between two dates.kmmacros (6.7 KB)

```
(() => {
"use strict";
// Rob Trew @2016, @2022
// main :: IO ()
const main = () => {
const localNames = [
"weeks", "days", "hr", "min", "sec"
];
const
kme = Application("Keyboard Maestro Engine"),
[fromSeconds, toSeconds] = [
"FromDateTime", "ToDateTime"
]
.map(k => parseInt(kme.getvariable(k), 10)),
diffSeconds = toSeconds - fromSeconds,
diffDays = diffSeconds / (24 * 60 * 60),
compound = compoundDuration(
localNames
)(
toSeconds - fromSeconds
);
return `${diffDays} days = ${compound}`;
};
// ---------------- COMPOUND DURATION ----------------
// compoundDuration :: [String] -> Int -> String
const compoundDuration = labels =>
nSeconds => weekParts(nSeconds)
.map((v, i) => [v, labels[i]])
.reduce((a, x) =>
a.concat(
x[0] ? [
`${x[0]} ${x[1] || "?"}`
] : []
), []
)
.join(", ");
// weekParts :: Int -> [Int]
const weekParts = nSeconds =>
[0, 7, 24, 60, 60]
.reduceRight((a, x) => {
const
r = a[0],
mod = x !== 0 ? r % x : r;
return [
(r - mod) / (x || 1),
[mod, ...a[1]]
];
}, [nSeconds, []])[1];
// MAIN ---
return main();
})();
```

2 Likes

Or, (variant JS, with an updated `compoundDuration`

function),

and buttons to reset `From`

or `To`

to now.

Compound difference between two dates.kmmacros (12 KB)

(Hover cursor over the code and click the `Copy`

icon in top right corner)

```
(() => {
"use strict";
// Rob Trew @2022
// Ver 0.2 (an updated compound duration function)
// main :: IO ()
const main = () => {
const localNames = [
"wk", "d", "hr", "min", "sec"
];
const
kme = Application("Keyboard Maestro Engine"),
[fromSeconds, toSeconds] = [
"FromDateTime", "ToDateTime"
]
.map(k => parseInt(kme.getvariable(k), 10)),
diffSeconds = toSeconds - fromSeconds,
diffDays = diffSeconds / (24 * 60 * 60),
compound = compoundDuration(localNames)(
diffSeconds
);
return `${diffDays} days = ${compound}`;
};
// ---------------- COMPOUND DURATION ----------------
// compoundDuration :: [String] -> Int -> String
const compoundDuration = localNames =>
// A report on compound duration of a quantity of
// seconds using five name strings for the local
// equivalents of "wk", "d", "hr", "min", "sec".
nSeconds => mapAccumR(r => ([k, n]) => {
const v = n !== 0 ? (r % n) : r;
return [
(r - v) / (n || 1),
0 < v ? `${v} ${k}` : ""
];
})(nSeconds)(
zip(localNames)([0, 7, 24, 60, 60])
)[1]
.filter(Boolean)
.join(", ");
// --------------------- GENERIC ---------------------
// mapAccumR :: (acc -> x -> (acc, y)) -> acc ->
// [x] -> (acc, [y])
const mapAccumR = f =>
// A tuple of an accumulation and a list
// obtained by a combined map and fold,
// with accumulation from right to left.
acc => xs => [...xs].reduceRight(
([a, b], x) => second(
v => [v].concat(b)
)(
f(a)(x)
),
[acc, []]
);
// second :: (a -> b) -> ((c, a) -> (c, b))
const second = f =>
// A function over a simple value lifted
// to a function over a tuple.
// f (a, b) -> (a, f(b))
([x, y]) => [x, f(y)];
// zip :: [a] -> [b] -> [(a, b)]
const zip = xs =>
// The paired members of xs and ys, up to
// the length of the shorter of the two lists.
ys => Array.from({
length: Math.min(xs.length, ys.length)
}, (_, i) => [xs[i], ys[i]]);
// MAIN ---
return main();
})();
```

2 Likes

How do I get it to return:

Number of days

Number of weeks

Number of months

Number of years

More than just the 60, the number of days?

Number of months

First you would need to **define** that.

- It means a calendar month with a variable number of days ? Now you need calendrical lookups or calculations.
- It means a period of four weeks ? Just divide the number of weeks by 4.

Weeks ?

It already gives those:

Number of years ?

Calendar years, or multiples of 365 days ?

If calendar years then you need calendrical lookups or computations.

Calendrical Calculations: The Ultimate Edition: Reingold, Edward M., Dershowitz, Nachum

1 Like

As a first sketch, using JavaScript dates:

Compound difference between two dates (including calendar years and months).kmmacros (13 KB)

```
const
jsFrom = new Date(fromSeconds * 1000),
jsTo = new Date(toSeconds * 1000),
calendarYearDelta = (
jsTo.getFullYear() - jsFrom.getFullYear()
),
calendarMonthDelta = (
jsTo.getMonth() - jsFrom.getMonth()
),
[years, months] = 0 > calendarMonthDelta ? [
calendarYearDelta - 1,
12 + calendarMonthDelta
] : [
calendarYearDelta,
calendarMonthDelta
];
return [
`${diffDays} days = ${compound}`,
`\n\n${years} years and ${months} calendar months.`
];
```

```
(() => {
"use strict";
// Rob Trew @2022
// Ver 0.2 (an updated compound duration function)
// Ver 0.3 (added JS calendar date deltas for yr, month)
// main :: IO ()
const main = () => {
const localNames = [
"wk", "d", "hr", "min", "sec"
];
const
kme = Application("Keyboard Maestro Engine"),
[fromSeconds, toSeconds] = [
"FromDateTime", "ToDateTime"
]
.map(k => parseInt(kme.getvariable(k), 10)),
diffSeconds = Math.abs(toSeconds - fromSeconds),
diffDays = diffSeconds / (24 * 60 * 60),
compound = compoundDuration(localNames)(
diffSeconds
);
const
jsFrom = new Date(fromSeconds * 1000),
jsTo = new Date(toSeconds * 1000),
calendarYearDelta = (
jsTo.getFullYear() - jsFrom.getFullYear()
),
calendarMonthDelta = (
jsTo.getMonth() - jsFrom.getMonth()
),
[years, months] = 0 > calendarMonthDelta ? [
calendarYearDelta - 1,
12 + calendarMonthDelta
] : [
calendarYearDelta,
calendarMonthDelta
];
return [
`${diffDays} days = ${compound}`,
`\n\n${years} year(s) and `,
`${months} calendar month(s).`
].join("");
};
// ---------------- COMPOUND DURATION ----------------
// compoundDuration :: [String] -> Int -> String
const compoundDuration = localNames =>
// A report on compound duration of a quantity of
// seconds using five name strings for the local
// equivalents of "wk", "d", "hr", "min", "sec".
nSeconds => mapAccumR(r => ([k, n]) => {
const v = n !== 0 ? (r % n) : r;
return [
(r - v) / (n || 1),
0 < v ? `${v} ${k}` : ""
];
})(nSeconds)(
zip(localNames)([0, 7, 24, 60, 60])
)[1]
.filter(Boolean)
.join(", ");
// --------------------- GENERIC ---------------------
// mapAccumR :: (acc -> x -> (acc, y)) -> acc ->
// [x] -> (acc, [y])
const mapAccumR = f =>
// A tuple of an accumulation and a list
// obtained by a combined map and fold,
// with accumulation from right to left.
acc => xs => [...xs].reduceRight(
([a, b], x) => second(
v => [v, ...b]
)(
f(a)(x)
),
[acc, []]
);
// second :: (a -> b) -> ((c, a) -> (c, b))
const second = f =>
// A function over a simple value lifted
// to a function over a tuple.
// f (a, b) -> (a, f(b))
([x, y]) => [x, f(y)];
// zip :: [a] -> [b] -> [(a, b)]
const zip = xs =>
// The paired members of xs and ys, up to
// the length of the shorter of the two lists.
ys => Array.from({
length: Math.min(xs.length, ys.length)
}, (_, i) => [xs[i], ys[i]]);
// MAIN ---
return main();
})();
```

1 Like