Search code examples
javascriptjquerycountdown

Javascript countdown time incorrect


I have an issue with a Jquery countdown timer. The script counts down to the same time every day. The countdown time is incorrect, it is one hour off. Any ideas how to fix this? It's hosted at JSFiddle.

(function($){
  var date    = new Date(),
      month   = date.getMonth();
      day     = date.getDate(),
      weekDay = date.getDay(),
      hours   = {
        start: new Date(date.getFullYear(), month, day),
        end: new Date(date.getFullYear(), month, day)
      };
  
  // weekDay var [0 = sun, 1 = mon, 2 = tues ... 5 = fri 6 = sat]
  
  // If it's Monday - Friday
  if(weekDay >= 1 && weekDay <= 5){

    // Start at 7am, end at 8pm
    hours.start.setHours(7);
    hours.end.setHours(22);

  // If it's Saturday
  } else if(weekDay == 6){

    // Start at 8am, end at 8pm
    hours.start.setHours(8);
    hours.end.setHours(20);

  // If it's Sunday
  } else {

    // Start at 9am, end at 6pm
    hours.start.setHours(9);
    hours.end.setHours(18);
  }

  function countDown(){
    var date         = new Date(),
        countHours   = ('0' + (hours.end.getHours() - date.getHours())).substr(-2),
        countMinutes = ('0' + (59 - date.getMinutes())).substr(-2),
        countSeconds = ('0' + (59 - date.getSeconds())).substr(-2);

    // If it's currently not within the hours, don't show the countdown
    if(date.getHours() < hours.start.getHours() || date.getHours() > hours.end.getHours()){
      $('.countdown').hide();
    } else if($('.countdown').not(':visible')){
      $('.countdown').show();
    }

    $('.countdown .hours').text(countHours);
    $('.countdown .minutes').text(countMinutes);
    $('.countdown .seconds').html(countSeconds);

  }

  $(function(){
    setInterval(function(){
      countDown();
    }, 1000);
  });
})(jQuery);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="countdown">
<span class="hours"></span>H <span class="minutes"></span>M <span class="seconds"></span>S
</div>

JSFiddle: http://jsfiddle.net/chrisoliver1990/s1nzydfj/36/


Solution

  • The problem is that you aren't correctly calculating the hours and minutes. Assuming you want to finish the countdown at 22:00 and it's currently 10:30, then this is 11 hours and 30 minutes.

    However, your calculation just does hours.end.getHours() - date.getHours() which, using the values above, yields 22 - 10. This is 12, not 11, because the code doesn't take into account the half an hour that already passed.

    The same problem appears with the minutes but it's less apparent. Still, the minutes display is 1 off because it doesn't take into account the seconds that have already passed.

    Instead, you should take a different approach and get the total difference in seconds, then calculate the hours, minutes, and seconds this represents:

    var end = new Date('2020-01-10T22:00:00');
    
    //let's fake it to 10:30:42
    var current = new Date('2020-01-10T10:30:42');
    
    //getTime returns milliseconds, divide by 1000 to get seconds
    var differenceInSeconds = Math.floor((end.getTime() - current.getTime()) / 1000);
    
    //for convenience
    var secondsInHour = 60 * 60;
    var secondsInMinute = 60;
    
    
    var hours = Math.floor(differenceInSeconds / secondsInHour);
    
    //take the hours out of the difference
    differenceInSeconds -= hours * secondsInHour;
    
    var minutes = Math.floor(differenceInSeconds / secondsInMinute);
    
    //take the minutes out of the difference
    differenceInSeconds -= minutes * secondsInMinute;
    
    //the rest is just the seconds left
    var seconds = differenceInSeconds;
    
    console.log(hours, minutes, seconds);

    Adding this time calculation to your code correctly calculates the remaining time:

    (function($){
      var date    = new Date(),
          month   = date.getMonth();
          day     = date.getDate(),
          weekDay = date.getDay(),
          hours   = {
            start: new Date(date.getFullYear(), month, day),
            end: new Date(date.getFullYear(), month, day)
          };
      
      // weekDay var [0 = sun, 1 = mon, 2 = tues ... 5 = fri 6 = sat]
      
      // If it's Monday - Friday
      if(weekDay >= 1 && weekDay <= 5){
    
        // Start at 7am, end at 8pm
        hours.start.setHours(7);
        hours.end.setHours(22);
    
      // If it's Saturday
      } else if(weekDay == 6){
    
        // Start at 8am, end at 8pm
        hours.start.setHours(8);
        hours.end.setHours(20);
    
      // If it's Sunday
      } else {
    
        // Start at 9am, end at 6pm
        hours.start.setHours(9);
        hours.end.setHours(18);
      }
      
      function formatNumber(num) {
        return ('0' + num).substr(-2);
      }
      
      function countDown(){
        var date         = new Date();
        
        var end = new Date('2020-01-10T22:00:00'),
            differenceInSeconds = Math.floor((hours.end.getTime() - date.getTime()) / 1000);
    
        //for convenience
        var secondsInHour = 60 * 60;
        var secondsInMinute = 60;
    
        var countHours = formatNumber(Math.floor(differenceInSeconds / secondsInHour));
    
        differenceInSeconds -= countHours * secondsInHour;
    
        var countMinutes = formatNumber(Math.floor(differenceInSeconds / secondsInMinute));
    
        differenceInSeconds -= countMinutes * secondsInMinute;
        
        var countSeconds = formatNumber(differenceInSeconds);
    
        // If it's currently not within the hours, don't show the countdown
        if(date.getHours() < hours.start.getHours() || date.getHours() > hours.end.getHours()){
          $('.countdown').hide();
        } else if($('.countdown').not(':visible')){
          $('.countdown').show();
        }
    
        $('.countdown .hours').text(countHours);
        $('.countdown .minutes').text(countMinutes);
        $('.countdown .seconds').html(countSeconds);
    
      }
    
      $(function(){
        setInterval(function(){
          countDown();
        }, 1000);
      });
    })(jQuery);
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <div class="countdown">
    <span class="hours"></span>H <span class="minutes"></span>M <span class="seconds"></span>S
    </div>

    (I also introduced a helper function formatNumber to encapsulate the logic for displaying the zero in front of a number or not)