skip to Main Content

I have a setInterval that shows the current date and time, and now I am extending the code so it will also display the percentage of progress in the calendar year. At the time of writing we are at 29%. This calculation works fine (see the final variable below). My question is:

How to get the time that is left until that percentage becomes 30%? And once we arrive at that point: the time left until it becomes 31%, …etc?

I know that a percentage increment happens on this scale roughly every three and half days, exactly 87.5 hours.

Here is my code:

setInterval(function () {
  const dt = new Date();
  document.getElementById("datetime").innerHTML =
    ("0" + dt.getDate()).slice(-2) +
    "." +
    ("0" + (dt.getMonth() + 1)).slice(-2) +
    "." +
    dt.getFullYear() +
    " " +
    ("0" + dt.getHours()).slice(-2) + 
    ":" +
    ("0" + dt.getMinutes()).slice(-2) +
    ":" +
    ("0" + dt.getSeconds()).slice(-2)
}, 1);

const start = new Date(2023,12,1); 
const end = new Date(2024,12,1);
const today = new Date();
const total = end - start;
let progress = today - start;
const final = Math.round(progress / total * 100 ) + "%"
const finalCalc = document.querySelector("#percentage")
finalCalc.innerHTML = final;
Current time: <span id="datetime"></span><br>
Progress: <span id="percentage"></span><br>
Until next %-increment: <span id="remaining">?</span>

The output I would like to see added and keep updated would be for example:

the next percentage increment will occur in xxx amount of hours

How can I achieve that?

3

Answers


  1. You can do this calculation using the millisecond values of the dates

    (I’ll let you refine this calculation according to your time zone.)

    const
      ms_start  = new Date(2024,0,1,0,0,0,0).getTime()  // Milliseconds since Jan 1, 1970, 00:00:00.000 GMT
    , ms_end_1p = (new Date(2024,11,31,23,59,59,999).getTime() - ms_start) / 100 
      ;
    let percentTime = Math.round((Date.now() - ms_start) / ms_end_1p);
    
    console.log( 'percentTime =', percentTime, '%');
    Login or Signup to reply.
  2. Below, is a refactorisation of your code, with some newly added functions, to improve readability. Also, this new code updates all stats every ms, and not just the current time, as you had in your question:

    // Constants for all countdowns
    const start = new Date(2023,12,1); 
    const end = new Date(2024,12,1);
    const daysThisYear = daysInYear();
    const timeForEntireIncrement = (24*daysThisYear/100);
    const currentTimeElement = document.querySelector("#datetime");
    const percentageProgressElement = document.querySelector("#percentage");
    const timeForIncrementElement = document.querySelector("#remaining");
    
    // This is a helper function to get the exact hours needed for a 1% increase (taking into account leap years and all)
    function daysInYear() {
        const year = new Date().getFullYear();
        return new Date(year, 1, 29).getDate() === 29 ? 366 : 365;
    }
    
    
    // This is a helper function to convert the hours left for the next 1% increase in decimal form (i.e., 33.58765 hours) to a format that can be rendered on screen
    function convertDecimalHours(decimalHours) {
        var date = new Date(decimalHours * 3600 * 1000);
        var days = Math.floor(decimalHours / 24);
        var hours = (date.getUTCHours()).toLocaleString('en-US', {minimumIntegerDigits: 2, useGrouping:false});
        var minutes = (date.getUTCMinutes()).toLocaleString('en-US', {minimumIntegerDigits: 2, useGrouping:false});
        var seconds = (date.getUTCSeconds()).toLocaleString('en-US', {minimumIntegerDigits: 2, useGrouping:false});
        
        return {
            days: days,
            hours: hours,
            minutes: minutes,
            seconds: seconds
        };
    }
    
    // This function solely focuses on rendering the stats into the DOM
    function renderStats(currentTime, progress, timeForIncrement){
        currentTimeElement.innerHTML =
            ("0" + currentTime.getDate()).slice(-2) +
            "." +
            ("0" + (currentTime.getMonth() + 1)).slice(-2) +
            "." +
            currentTime.getFullYear() +
            " " +
            ("0" + currentTime.getHours()).slice(-2) + 
            ":" +
            ("0" + currentTime.getMinutes()).slice(-2) +
            ":" +
            ("0" + currentTime.getSeconds()).slice(-2);
        //Adjust the X in .toFixed(X) to increase/decrease accuracy of displayed percentage
        percentageProgressElement.innerHTML = progress.toFixed(6) + "%";
        timeForIncrementElement.innerHTML = timeForIncrement.days + " day(s) and " + timeForIncrement.hours + ":" + timeForIncrement.minutes  + ":" + timeForIncrement.seconds;
    }
    
    // Loop that updates all stats every ms
    setInterval(function () {
        let currentTime = new Date();
        let progress =  (currentTime - start) / (end - start) * 100;
        let decimalTimeForIncrement = timeForEntireIncrement * (Math.ceil(progress) - progress);
        renderStats(currentTime, progress, convertDecimalHours(decimalTimeForIncrement))
    }, 1);
    <b>Current time:</b> <span id="datetime"></span><br>
    <b>Progress:</b> <span id="percentage"></span><br>
    <b>Until next %-increment:</b> <span id="remaining">?</span>

    Though I have added comments throughout the code, don’t hesitate to ask if something feels off!!!

    Hope this helped! And may the code be with you…

    Login or Signup to reply.
  3. First of all, realise that new Date(2024,12,1) is the same as new Date(2025,0,1), as the month argument is zero-based.

    Secondly, I would not use round in your percentage formula, as that would mean you’d hit 100% about two days before the target date. Use floor instead.

    You can take the percentage, add one and then reverse the formula to know at which time the percentage will increase with one unit.

    Here is a snippet:

    const start = new Date(2024,0,1); 
    const end = new Date(2025,0,1);
    
    const outTime = document.querySelector("#datetime");
    const outPct = document.querySelector("#percentage");
    const outRem = document.querySelector("#remaining");
    
    setInterval(function () {
        const today = new Date();
        outTime.textContent = today.toLocaleString("bg").replace(/ D+/g, " ");
        if (today >= end) {
            outPct.textContent = "100%";
            outRem.textContent = "";
            return;
        }
        const total = end - start;
        const progress = today - start;
        const pct = Math.floor(progress / total * 100);
        outPct.textContent = pct + "%";
        const next = (pct+1) / 100 * total;
        const countdown = Math.ceil((next - progress) / 1000);
        const hours = Math.floor(countdown / 3600);
        const minutes = Math.floor((countdown % 3600) / 60);
        const seconds = countdown % 60;
        outRem.textContent = `${hours} hours, ${minutes} minutes and ${seconds} seconds`
                             .replace(/b(1 w+)sb/g, "$1");
    }, 1);
    Current time: <span id="datetime"></span><br>
    Progress: <span id="percentage"></span><br>
    Until next %-increment: <span id="remaining"></span><br>

    It will be hard to test the correctness like this, as it takes a long time before a percentage increase occurs, so maybe use two dates that are closer to each other to do that, like just 1 hour apart (the start of the current hour and the end of it):

    const start = new Date();
    start.setMinutes(0, 0, 0); // Start of the current hour
    const end = new Date();
    end.setMinutes(60, 0, 0); // End of the current hour
    
    const outTime = document.querySelector("#datetime");
    const outPct = document.querySelector("#percentage");
    const outRem = document.querySelector("#remaining");
    
    setInterval(function () {
        const today = new Date();
        outTime.textContent = today.toLocaleString("bg").replace(/ D+/g, " ");
        if (today >= end) {
            outPct.textContent = "100%";
            outRem.textContent = "";
            return;
        }
        const total = end - start;
        const progress = today - start;
        const pct = Math.floor(progress / total * 100);
        outPct.textContent = pct + "%";
        const next = (pct+1) / 100 * total;
        const countdown = Math.ceil((next - progress) / 1000);
        const hours = Math.floor(countdown / 3600);
        const minutes = Math.floor((countdown % 3600) / 60);
        const seconds = countdown % 60;
        outRem.textContent = `${hours} hours, ${minutes} minutes and ${seconds} seconds`
                             .replace(/b(1 w+)sb/g, "$1");
    }, 1);
    Current time: <span id="datetime"></span><br>
    Progress: <span id="percentage"></span><br>
    Until next %-increment: <span id="remaining"></span><br>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search