Percentage of year remaining, using days as units

I had a macro that gave me the ordinal number of the day-of-year. I wanted to improve that to give me the percentage of the year remaining. I found nothing in the forum.

I tried calculating this from the cardinal day number and the number of days in the year (because I want it to be accurate even on leap years), but while trouble-shooting using a variable as numerical input (this is, it turns out, tricky for a novice) I stumbled on a thread in which Peter supplied an example using the Action "Set Variable to Calculation" and thought I saw a spark in there with which I could bake my bun. Sweet aroma — it worked!

(I set it up initially to calculate percentage of year elapsed, then changed it to calculate percentage of year remaining by subtracting this from 1.)

I have some questions.

  • Can this be bettered, other than by calculating the days from now to the end of year instead of from the beginning of the year and subtracting from 1?
  • Is this accurate? I tested it manually for the first and last days of the year; it seems to be accurate.
  • Will I have any issues with time zone? Afaict, this works with local time and saves me the effort of working in milliseconds and GMT and offsets.
  • This is just for my information — I want it to be right, but it doesn't have to be accurate to the day (though I did check how it rounds to whole numbers).

And also: I overwrote my original macro. What is the easiest way to retrieve it from a backup? (I tried duplicating the Macro and renaming it and then undoing all the changes to the original, but the first undo action is "Undo Duplicate")

1 Like

Why would that be "better" if it gives the exact same answer? But yes, I give you the formula below for the "better" approach.

Why are you passing YEAR, MONTH and DAY to the TIME function but not including HOUR, MINUTE and SECOND? You could be off by as much as one day, which is 0.3%.

In any case, I think you can do the math differently, like this:

((TIME(YEAR()+1,1,1))-TIME(YEAR() ,MONTH(),DAY()))/(TIME(YEAR(),12,31)-TIME(YEAR(), 1,1))

I don't see why any time zone would return a different result from GMT. The percentage of year past and remaining should be identical for all time zones. The time zone cannot change the result.

There seems to be a second mistake in your calculation, resulting in a one day error. You are calculating from Jan 1 to Dec 31 as your divisor. You should be calculating from Jan 1 to Jan 1 of the next year. That way you include every day, rather than one day less than a year.

Sketch of a script action variant:

Percentage of days left in this year.kmmacros (3.9 KB)

const
    now = new Date(),
    year = now.getFullYear(),
    day = 24 * 60 * 60 * 1000,
    
    // Midnight to midnight
    yearStart = new Date(`${year}-01-01`),
    yearEnd = new Date(`${1 + year}-01-01`),

    daysLeft = Math.floor((yearEnd - now) / day),
    allDays = (yearEnd - yearStart) / day,

    percent = (daysLeft / allDays)
    .toLocaleString(undefined, {
        style: "percent",
        minimumFractionDigits: 0
    });

return JSON.stringify({
    daysLeft,
    allDays,
    percent
});

Note that you can get Jan 1 next year with TIME(YEAR(),12,32). Keyboard Maestro is forgiving with such things. The 32nd day of December is the first day of January.

3 Likes

For giggles:

Percentage of Year Remaining.kmmacros (2.7 KB)

I would add that there's a design decision to be made -- when does a day "complete"? At 12 noon on the first of January this year, did you still have 366 days left or 365? At 00:01 on the 31st December will you have 0 days left or 1?

What I can't remember is how to (or if you can) include text tokens in a calculation field, to avoid the filter step. Gurus?

Um…%ICUDateTime%D%?

You could add some leap year logic if you need that kind of precision.

I don't understand -- %ICUDateTime%D% returns "day of the year", so leap years are already accounted for and "days" is exactly the precision OP asked for!

What am I missing?

Maybe he was referring to calculating the precise percentage of the total days in the given year, which would differ in leap year. I'm sure there are more elegant ways to get there, but FWIW this shows 12/31 of this year is the 365th day.

%ICUDateTimeFor%TIME(YEAR(), 12, 31)%D%

That's what I already have in mine, except with the inclusion of "at 12 noon" to allow (as much as possible) for different time zones.

If you are only seeing 365 instead of 366, try including 12 for the hour.

But I've a feeling @devoy was actually replying to the OP, not me, so I'm probably leading the thread astray!

Ah, I see now. I thought you were just commenting on this:

Um…%ICUDateTime%D% ?

That's funny I cobbled mine together when I could have just re-read your earlier post :person_facepalming: