0

I'm trying to implement a component which performs a hh:mm:ss countdown for my project. first I used this library for calculating and rendering time for me. it worked perfect on any devices but iOS which return

NaN:NaN:NaN

. I assumed it's a problem with this module so I tried to implement my own countdown component. so I tried the code below:

import React from 'react';
import PropTypes from 'prop-types';

class Countdown extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            days: 0,
            hours: 0,
            min: 0,
            sec: 0
        };
    }

    componentDidMount() {
        // update every second
        let i = 0;
        this.interval = setInterval(() => {
            let date = this.calculateCountdown(this.props.date, ++i);
            date ? this.setState(date) : this.stop();
        }, 1000);
    }

    componentWillUnmount() {
        this.stop();
    }

    calculateCountdown(endDate , count) {
        let diff = (endDate - (count * 1000)) / 1000;
        // clear countdown when date is reached
        if (diff <= 0) return false;

        const timeLeft = {
            years: 0,
            days: 0,
            hours: 0,
            min: 0,
            sec: 0,
            millisec: 0
        };

        // calculate time difference between now and expected date
        if (diff >= (365.25 * 86400)) { // 365.25 * 24 * 60 * 60
            timeLeft.years = Math.floor(diff / (365.25 * 86400));
            diff -= timeLeft.years * 365.25 * 86400;
        }
        if (diff >= 86400) { // 24 * 60 * 60
            timeLeft.days = Math.floor(diff / 86400);
            diff -= timeLeft.days * 86400;
        }
        if (diff >= 3600) { // 60 * 60
            timeLeft.hours = Math.floor(diff / 3600);
            diff -= timeLeft.hours * 3600;
        }
        if (diff >= 60) {
            timeLeft.min = Math.floor(diff / 60);
            diff -= timeLeft.min * 60;
        }
        timeLeft.sec = Math.floor(diff);

        return timeLeft;
    }

    stop() {
        clearInterval(this.interval);
    }

    addLeadingZeros(value) {
        value = String(value);
        while (value.length < 2) {
            value = '0' + value;
        }
        return value;
    }

    convertToPersianNumber = (time) => {
        let persianDigits = '۰۱۲۳۴۵۶۷۸۹';
        let persianMap = persianDigits.split('');
        return time.replace(/\d/g, m => persianMap[parseInt(m)]);
    };

    render() {
        const countDown = this.state;

        return (
            <div className="Countdown">
                <span className="Countdown-col">
                    <span className="Countdown-col-element">
                        {countDown.hour}
                        {/*{this.convertToPersianNumber(this.addLeadingZeros(countDown.hours))}:*/}
                    </span>
                </span>

                <span className="Countdown-col">
                      <span className="Countdown-col-element">
                          {countDown.min}
                          {/*{this.convertToPersianNumber(this.addLeadingZeros(countDown.min))}:*/}
                      </span>
                </span>

                <span className="Countdown-col">
                      <span className="Countdown-col-element">
                          {countDown.sec}
                          {/*{this.convertToPersianNumber(this.addLeadingZeros(countDown.sec))}*/}
                      </span>
                </span>
            </div>
        );
    }
}

Countdown.propTypes = {
    date: PropTypes.number.isRequired
};

Countdown.defaultProps = {
    date: 0
};

export default Countdown;

in this component I pass my end time in mili seconds and calculate time by an interval , finally I convert it to persian strings and print it.

<div className={styles.bottomContainer}>
                    {/*<Moment>{remainTime}</Moment>*/}
    <CountDown onComplete={() => this.setState({ timerEnd: true })}
               date={remainTime}/>
</div>

I still have the same problem I had before: in iOS devices I get

NaN:00:00

what is my problem if you can help me? I have to add that I cleared converting and tried to printing just numbers and it didn't work;

Community
  • 1
  • 1
  • It works on other devices, but not on iOS? – Lazar Nikolic Sep 09 '18 at 09:47
  • 1
    Please show how you're using the `Countdown` element. We need to see the `date` prop supplied. But note that your `propTypes` says it's a number, while your default makes it a `Date` object. `new Date(dateInstance)` was [not historically reliable](https://stackoverflow.com/questions/1090815/how-to-clone-a-date-object-in-javascript#comment47658106_1090817), although the spec now says it should work and it works on my iPad. But the default should be a number if you're saying it's a number with `propTypes`: `Date.now()`. – T.J. Crowder Sep 09 '18 at 10:39
  • @T.J.Crowder I updated the question. I edited the component which I removed all Date functions and calculates using another way. and also I added the use of Countdown instance – MohammadJavad Seyyedi Sep 09 '18 at 10:53
  • 1
    All that does is beg the question: What's `remainTime`?! Please update your question with a [mcve] demonstrating the problem, ideally a **runnable** one using Stack Snippets (the `[<>]` toolbar button). Stack Snippets support React, including JSX; [here's how to do one](http://meta.stackoverflow.com/questions/338537/). – T.J. Crowder Sep 09 '18 at 10:54
  • @T.J.Crowder remainTime is a value in miliSeconds for example 1784000 – MohammadJavad Seyyedi Sep 09 '18 at 10:58
  • @T.J.Crowder finally your first answer helped me. I wish you could redo its removing – MohammadJavad Seyyedi Sep 09 '18 at 12:47
  • @MohammadJavadSeyyedi - Really? I thought `date` was a number, so that answer wouldn't be correct. I've un-deleted it, but ... does it really answer the question? – T.J. Crowder Sep 09 '18 at 12:54
  • @T.J.Crowder yeah, outside the Countdown Component, I was converting a date to milisecond using Date.parse(). argument inside this function was not in correct format and I fixed it using your hint – MohammadJavad Seyyedi Sep 10 '18 at 08:39

1 Answers1

3

You're relying on new Date to parse the date string. You haven't shown us what format that string is in, but the only format that new Date is required to parse is the ISO-8601-inspired one defined in the specification, such as "2018-09-09T09:46:33.089Z" (that's just an example). All modern browsers are also known to parse the U.S. format "MM/DD/YYYY" (regardless of locale), but that isn't specified. iOS Safari is known not to handle some formats that other browsers do handle.

So, either:

  • Ensure you use the ISO-8601 format when specifying date, or
  • Parse the string yourself
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875