Search code examples
angularcountdowntimer

angular - how to loop count down with current time


I have an array of times (futuretimes) so I need to show a countdown timer in MM:SS format in decrement order by comparing with the current time(HH:MM). so when the timer finishes then it will jump to find the next highest timer from new current HH:MM

How to do this ?

    const futuretimes = ["5:00 AM" , "5:30 AM" , "5:45 AM" , "6:00 AM" , "6:15 AM" , "6:30 AM" , "6:40 AM" , "6:50 AM" , "7:00 AM" , "7:08 AM" , "7:16 AM" , "7:24 AM" , "7:35 AM" , "7:40 AM" , "7:45 AM" , "7:50 AM" , "7:55 AM" , "8:00 AM" , "8:05 AM" , "8:10 AM" , "8:15 AM" , "8:20 AM" , "8:25 AM" , "8:30 AM" , "8:34 AM" , "8:38 AM" , "8:42 AM" , "8:46 AM" , "8:50 AM" , "8:54 AM" , "8:58 AM" , "9:02 AM" , "9:06 AM" , "9:10 AM" , "9:15 AM" , "9:20 AM" , "9:25 AM" , "9:30 AM" , "9:35 AM" , "9:40 AM" , "9:45 AM" , "9:50 AM" , "9:55 AM" , "10:00 AM" , "10:06 AM" , "10:12 AM" , "10:18 AM" , "10:26 AM" , "10:34 AM" , "10:40 AM" , "10:50 AM" , "11:00 AM" , "11:10 AM" , "11:20 AM" , "11:30 AM" , "11:40 AM" , "11:50 AM" , "12:00 PM" , "12:10 PM" , "12:20 PM" , "12:30 PM" , "12:40 PM" , "12:50 PM" , "1:00 PM" , "1:10 PM" , "1:20 PM" , "1:30 PM" , "1:40 PM" , "1:50 PM" , "2:00 PM" , "2:10 PM" , "2:20 PM" , "2:30 PM" , "2:40 PM" , "2:50 PM" , "3:00 PM" , "3:10 PM" , "3:20 PM" , "3:30 PM" , "3:40 PM" , "3:50 PM" , "4:00 PM" , "4:08 PM" , "4:16 PM" , "4:24 PM" , "4:32 PM" , "4:40 PM" , "4:45 PM" , "4:50 PM" , "4:55 PM" , "5:00 PM" , "5:05 PM" , "5:10 PM" , "5:14 PM" , "5:18 PM" , "5:22 PM" , "5:26 PM" , "5:30 PM" , "5:34 PM" , "5:38 PM" , "5:42 PM" , "5:46 PM" , "5:50 PM" , "5:55 PM" , "6:00 PM" , "6:05 PM" , "6:10 PM" , "6:15 PM" , "6:20 PM" , "6:25 PM" , "6:30 PM" , "6:35 PM" , "6:40 PM" , "6:45 PM" , "6:50 PM" , "6:55 PM" , "7:00 PM" , "7:05 PM" , "7:10 PM" , "7:15 PM" , "7:20 PM" , "7:25 PM" , "7:30 PM" , "7:35 PM" , "7:40 PM" , "7:45 PM" , "7:50 PM" , "7:55 PM" , "8:00 PM" , "8:06 PM" , "8:12 PM" , "8:18 PM" , "8:24 PM" , "8:30 PM" , "8:36 PM" , "8:42 PM" , "8:48 PM" , "8:54 PM" , "9:00 PM" , "9:08 PM" , "9:16 PM" , "9:24 PM" , "9:32 PM" , "9:40 PM" , "9:50 PM" , "10:00 PM" , "10:15 PM" , "10:30 PM" , "10:45 PM" , "11:00 PM"];

Expected output: E.g: Suppose if the current time HH:MM is 5:31AM then the countdown timer should pick a next highest HH:MM time comparing with the current time HH:MM from above-defined array(i.e futuretimes) which is just highest time, so in this example it should pick 5:45 AM from array because it is next highest value from current time(5:31AM) and then countdown timer should be shown and once the current countdown timer finishes then it will take next highest from array as compared to the new current time etc.

I tried to get the current time in seconds and but stuck how to make for loop between these arrayoftime and pick the highest one to start timercountdown:

    const currentMinute = new Date().getMinutes();
    const currenthour = new Date().getHours(); 

    const totalseconds=3600*currenthour +60*currentMinute;//current time in seconds

    // now stuck how to do further

Solution

  • Here's one implementation in angular using observables.

    Stackblitz: https://stackblitz.com/edit/angular-sbosff

    <small>Now</small>
    <p>{{ now$ | async | date:'h:mm:ss a' }}</p>
    <hr>
    <small>Next Time</small>
    <ng-container>
      <p>{{ (nextTime$ | async)?.time }}</p>
    </ng-container>
    <hr>
    <ng-container>
      <small>Countdown to Next</small>
      <p>{{ timeToNext$ | async }}</p>
    </ng-container>
    <hr>
    <small>All Times</small>
    <ng-container *ngFor="let timeOption of futuretimes">
      <p>{{ timeOption }}</p>
    </ng-container>
    
    import { Component } from '@angular/core';
    import { Observable, interval, combineLatest } from 'rxjs';
    import { startWith, map } from 'rxjs/operators';
    
    interface StringTimeWithDate {
      time: string;
      asDate: Date;
    }
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: [ './app.component.css' ]
    })
    export class AppComponent  {
      now$: Observable<Date>;
      nextTime$: Observable<StringTimeWithDate>;
      timeToNext$: Observable<string>;
      futuretimes = [
        '5:00 AM',
        '5:30 AM',
        '5:45 AM',
        '6:00 AM',
        '6:15 AM',
        '6:30 AM',
        '6:40 AM',
        '6:50 AM',
        '7:00 AM',
        '7:08 AM',
        '7:16 AM',
        '7:24 AM',
        '7:35 AM',
        '7:40 AM',
        '7:45 AM',
        '7:50 AM',
        '7:55 AM',
        '8:00 AM',
        '8:05 AM',
        '8:10 AM',
        '8:15 AM',
        '8:20 AM',
        '8:25 AM',
        '8:30 AM',
        '8:34 AM',
        '8:38 AM',
        '8:42 AM',
        '8:46 AM',
        '8:50 AM',
        '8:54 AM',
        '8:58 AM',
        '9:02 AM',
        '9:06 AM',
        '9:10 AM',
        '9:15 AM',
        '9:20 AM',
        '9:25 AM',
        '9:30 AM',
        '9:35 AM',
        '9:40 AM',
        '9:45 AM',
        '9:50 AM',
        '9:55 AM',
        '10:00 AM',
        '10:06 AM',
        '10:12 AM',
        '10:18 AM',
        '10:26 AM',
        '10:34 AM',
        '10:40 AM',
        '10:50 AM',
        '11:00 AM',
        '11:10 AM',
        '11:20 AM',
        '11:30 AM',
        '11:40 AM',
        '11:50 AM',
        '12:00 PM',
        '12:10 PM',
        '12:20 PM',
        '12:30 PM',
        '12:40 PM',
        '12:50 PM',
        '1:00 PM',
        '1:10 PM',
        '1:20 PM',
        '1:30 PM',
        '1:40 PM',
        '1:50 PM',
        '2:00 PM',
        '2:10 PM',
        '2:20 PM',
        '2:30 PM',
        '2:40 PM',
        '2:50 PM',
        '3:00 PM',
        '3:10 PM',
        '3:20 PM',
        '3:30 PM',
        '3:40 PM',
        '3:50 PM',
        '4:00 PM',
        '4:08 PM',
        '4:16 PM',
        '4:21 PM',
        '4:32 PM',
        '4:40 PM',
        '4:45 PM',
        '4:50 PM',
        '4:55 PM',
        '5:00 PM',
        '5:05 PM',
        '5:10 PM',
        '5:14 PM',
        '5:18 PM',
        '5:22 PM',
        '5:26 PM',
        '5:30 PM',
        '5:34 PM',
        '5:38 PM',
        '5:42 PM',
        '5:46 PM',
        '5:50 PM',
        '5:55 PM',
        '6:00 PM',
        '6:05 PM',
        '6:10 PM',
        '6:15 PM',
        '6:20 PM',
        '6:25 PM',
        '6:30 PM',
        '6:35 PM',
        '6:40 PM',
        '6:45 PM',
        '6:50 PM',
        '6:55 PM',
        '7:00 PM',
        '7:05 PM',
        '7:10 PM',
        '7:15 PM',
        '7:20 PM',
        '7:25 PM',
        '7:30 PM',
        '7:35 PM',
        '7:40 PM',
        '7:45 PM',
        '7:50 PM',
        '7:55 PM',
        '8:00 PM',
        '8:06 PM',
        '8:12 PM',
        '8:18 PM',
        '8:24 PM',
        '8:30 PM',
        '8:36 PM',
        '8:42 PM',
        '8:48 PM',
        '8:54 PM',
        '9:00 PM',
        '9:08 PM',
        '9:16 PM',
        '9:24 PM',
        '9:32 PM',
        '9:40 PM',
        '9:50 PM',
        '10:00 PM',
        '10:15 PM',
        '10:30 PM',
        '10:45 PM',
        '11:00 PM'
      ];
      futureTimesAsDate: StringTimeWithDate[];
    
      constructor() {
        // map time to date and ensure sort ascending
        this.futureTimesAsDate = this.futuretimes
          .map(time => ({ time, asDate: this.timeStringToDate(time) }))
          .sort((a, b) => (a.asDate > b.asDate ? 1 : -1));
    
        this.now$ = interval(1000).pipe(
          startWith(null),
          map(() => new Date())
        );
    
        this.nextTime$ = this.now$.pipe(
          map((now: Date) => this.futureTimesAsDate.find(timeDate => timeDate.asDate > now))
        );
    
        this.timeToNext$ = combineLatest(this.now$, this.nextTime$, (now, nextTime) => {
          if (!now || !nextTime) {
            return null;
          }
    
          const diff = nextTime.asDate.valueOf() - now.valueOf();
          const millisPerSec = 1000;
          const millisPerMin = millisPerSec * 60;
    
          const min = Math.floor(diff / millisPerMin);
          const msLeftover = Math.floor(diff % millisPerMin);
          const secLeftover = Math.ceil(msLeftover / millisPerSec);
    
          const formattedMin = min < 10 ? `0${min}` : min;
          const formattedSec = secLeftover < 10 ? `0${secLeftover}` : secLeftover;
          const minMsToNext = `${formattedMin}:${formattedSec}`;
    
          return minMsToNext;
        });
      }
    
      ngOnInit() {}
    
      private timeStringToDate = dateString => {
        const [time, period] = dateString.split(' ');
        const [hour, minutes] = time.split(':').map(numString => Number(numString));
        const hourOutOf24 = period.toLowerCase() === 'pm' && hour !== 12 ? hour + 12 : hour;
    
        const dateForDateString = new Date();
        dateForDateString.setHours(hourOutOf24, minutes, 0, 0);
    
        return dateForDateString;
      }
    }