How to Increment/Decrement a Date?

FWIW a JS solution, using Application('Keyboard Maestro Engine').getvariable()

adjusted Date.kmmacros (19.5 KB)

Source of core function:

// adjustedDateString :: Int -> String -> String
const adjustedDateString = (intDays, strSlashedDayMonthYear) => {
    const
        dteIn = new Date(
            Date.apply(null, strSlashedDayMonthYear.split('/')
                .reverse()
            )
        ),
        dteOut = new Date(dteIn.setDate(dteIn.getDate() + intDays));
    return ['Date', 'Month', 'FullYear']
        .map(x => dteOut['get' + x]() + (x === 'Month' ? 1 : 0))
        .join('/')
};

with tests (every date, at two day intervals, from 20 days before to 20 days later)

(() => {
    'use strict';


    // adjustedDateString :: Int -> String -> String
    const adjustedDateString = (intDays, strSlashedDayMonthYear) => {
        const
            dteIn = new Date(
                Date.apply(null, strSlashedDayMonthYear.split('/')
                    .reverse()
                )
            ),
            dteOut = new Date(dteIn.setDate(dteIn.getDate() + intDays));
        return ['Date', 'Month', 'FullYear']
            .map(x => dteOut['get' + x]() + (x === 'Month' ? 1 : 0))
            .join('/')
    };

    // TESTS -----------------------------------------------------------------

    // enumFromThenToInt :: Int -> Int -> Int -> [Int]
    const enumFromThenToInt = (x1, x2, y) => {
        const d = x2 - x1;
        return Array.from({
            length: Math.floor(y - x2) / d + 2
        }, (_, i) => x1 + (d * i));
    };

    const strDate = Application('Keyboard Maestro Engine')
        .getvariable('instanceBaseDate')

    return enumFromThenToInt(-20, -18, 20)
        .map(d => adjustedDateString(d, strDate))
        .join('\n');
})();
1 Like

Thank you @ComplexPoint

I have a question in making this work. I tried to run the macro you provided and send results to the variable like this:

I am sure I am mishandling the JS script. It doesn't actually return me the modified date and I am not sure how to change it as I am not very familiar with JS. :disappointed:

I wonder how you are capturing the date string ?

(At the moment you seem to be feeding a url rather than a date string to the script)

I tried it with real string beforehand like so:

I capture the string from the Trello view. I also tested it by passing the actual date instead of System Clipboard and it fails too. In Trello it actually erases what was written and doesn't insert anything.

Here is the macro I am using:

test.kmmacros (25.6 KB)

The macro at the start of the thread just runs some tests on a literal date.

First try this one with a fixed date string (and the increment +1), and then experiment with:

  • capturing new date strings to the clipboard
  • adjusting the positive or negative integer value testDelta (to increment or decrement in units of one day)

Single adjustment of one date.kmmacros (19.7 KB)

This too returns nothing on my system. Maybe I do have to mention I am running 10.11.6. Apologise for not specifying it earlier.

I run the script you linked in Sublime text assuming I get 20/1/2018 in a window. But I get nothing. :disappointed:

macOS 0.11.6

Yes, I think that will be the issue.

Try replacing the JS code with this version (recompiled for ES5 JS at by the Closure compiler at Closure Compiler Service)

(function () {
    var a = Application("Keyboard Maestro Engine")
        .getvariable,
        b = a("testDelta");
    return function (a, b) {
        var c = new Date(Date.apply(null, b.split("/")
                .reverse())),
            d = new Date(c.setDate(c.getDate() + a));
        return ["Date", "Month", "FullYear"].map(function (a) {
                return d["get" + a]() + ("Month" === a ? 1 : 0);
            })
            .join("/");
    }(isNaN(b) ? 0 : parseInt(b, 10), a("testDate"));
})();

Single adjustment of one date (JS ES5 version).kmmacros (21.0 KB)

1 Like

Or, more readably, as recompiled for ES5 JS by the Babel JS compiler at https://babeljs.io/repl

(function () {
    'use strict';

    // adjustedDateString :: Int -> String -> String
    function adjustedDateString(intDays, strSlashedDayMonthYear) {
        var dteIn = new Date(
                Date.apply(null, strSlashedDayMonthYear.split('/')
                    .reverse()
                )
            ),
            dteOut = new Date(dteIn.setDate(dteIn.getDate() + intDays));
        return ['Date', 'Month', 'FullYear'].map(function (x) {
                return dteOut['get' + x]() + (x === 'Month' ? 1 : 0);
            })
            .join('/');
    };

    // KEYBOARD MAESTRO -------------------------------------------
    var kmVar = Application('Keyboard Maestro Engine')
        .getvariable,
        strDelta = kmVar('testDelta');

    return adjustedDateString(isNaN(strDelta) ? (
        0
    ) : parseInt(strDelta, 10), kmVar('testDate'));
})();

1 Like

AppleScript version of an adjustedDateString function

-- adjustedDateString :: Int -> String -> String
on adjustedDateString(intDays, strSlashedDayMonthYear)
    set {d, m, y} to splitOn("/", strSlashedDayMonthYear)
    set dteIn to current date
    tell (dteIn)
        set its year to y
        set its month to m
        set its day to d
    end tell
    
    tell (dteIn + (intDays * days))
        my intercalate("/", {its day as string, its month as integer, its year})
    end tell
end adjustedDateString

on run
    tell application "Keyboard Maestro Engine"
        set strDelta to getvariable "testDelta"
        set strDate to getvariable "testBaseDate"
    end tell
    set intDelta to strDelta as integer
    
    adjustedDateString(intDelta, strDate)
end run

-- GENERIC FUNCTIONS --------------------------------------

-- concat :: [[a]] -> [a]
-- concat :: [String] -> String
on concat(xs)
    if length of xs > 0 and class of (item 1 of xs) is string then
        set acc to ""
    else
        set acc to {}
    end if
    repeat with i from 1 to length of xs
        set acc to acc & item i of xs
    end repeat
    acc
end concat

-- intercalate :: [a] -> [[a]] -> [a]
-- intercalate :: String -> [String] -> String
on intercalate(sep, xs)
    concat(intersperse(sep, xs))
end intercalate

-- intersperse(0, [1,2,3]) -> [1, 0, 2, 0, 3]
-- intersperse :: Char -> String -> String
-- intersperse :: a -> [a] -> [a]
on intersperse(sep, xs)
    set lng to length of xs
    if lng > 1 then
        set acc to {item 1 of xs}
        repeat with i from 2 to lng
            set acc to acc & {sep, item i of xs}
        end repeat
        if class of xs is string then
            concat(acc)
        else
            acc
        end if
    else
        xs
    end if
end intersperse

-- splitOn :: String -> String -> [String]
on splitOn(strDelim, strMain)
    set {dlm, my text item delimiters} to {my text item delimiters, strDelim}
    set xs to text items of strMain
    set my text item delimiters to dlm
    return xs
end splitOn
1 Like

See also the date calculations here:

https://wiki.keyboardmaestro.com/Dates_and_Times

1 Like

The applescript solution worked for me. However above ES5 code still didn’t work.

Thank you very much for your help @ComplexPoint

1 Like

Also as I was playing with your scripts I realise Trello is not so dumb after all. If you just insert a number like 23 it would assume the date to be of 23rd of current month. This is super handy as the above solution is not as fast as I would of hoped. I don’t think you can get it to be as fast as Fantastical has it with up and down arrows. Trello interface is a bit slow.

1 Like

On performance, not sure whether this is faster than fetching KM Vars into a script:
(ICUDateTimeFor token)

dateString delta with ICUDateTimeFor.kmmacros (19.6 KB)

These are all very —VERY— complicated. You can use a shell script and some variables to manhandle the date in any way you choose.

The Shell Script function date -v+1d +'%d-%m-%Y' will increment the date by 1 day (+1d), but you could decrement or even change the target to m or y to adjust the month or the year.

Additionally, the date function will allow you to arbitrarily adjust to, say, next Sunday by using date -v+Sunday +'%d-%m-%Y'

It's extremely versatile. Feed it the variables you need it to process and then save to the variables you need to continue your functions.

1 Like

Thanks for sharing.
How would you change a date other than the current date?
Say, for example, I had a KM Variable named "MyDate" whose value is "2018-05-23".

Maybe you've already got a solution, but just for your and others info, here is another cool solution, provided by @Tom:

the shell script to manhandle particular dates on Mac are as follows:

date -j -f "%Y-%m-%d" -v+1d "2018-05-23" +"%Y-%m-%d"

This, as an example, will give you your example date incremented by 1 day (-v+1d)

If you plug properly formatted variables into the statement, you would end up with something like this:

date -j -f "%Y-%m-%d" -v+1d "$KMVAR_MyDate" +"%Y-%m-%d"

This assumes a static 1 day increment, though you could extend a variable to control that as well. Note that in a shell script you need to use the $KMVAR_MyVarName format to get it to parse.

1 Like

Oh, and to clarify the unix, the -j lets the command know you’re supplying a date; the -f tells the command the format in which you’re supplying it; the -v sets the increment (look into this part. it’s wicked powerful); and the final date formatting code specifies the output format, which in your case is the same as the input format.

Hope this helps!

Finally got around to putting it together. Hope this helps.

Adjust Date.kmmacros (20 KB)