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();
})();
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();
})();
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.
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
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();
})();