Home » Javascript » recursion – JavaScript recursive function for date/time messages

recursion – JavaScript recursive function for date/time messages

Posted by: admin February 23, 2020 Leave a comment

Questions:

I am working on an application that shows a nest of cells – which have different date/time properties. I have found the functionality here is working – but I am unsure how to improve the code below to turn it into a recursive function.

I was thinking of wrapping part of the loop into a function and using a callback at the point of digging deeper – but I didn’t succeed

something like this as a poc

recursiveFunction = (startTime, endTime, unit1, unit2, callback) => {

                if (timeRan(startTime, unit1) < 0) {
                    return this.msgStartIn(startTime, unit1);
                }
                if (timeRemaining(endTime, unit1) < 0) {
                    return this.msgFinishedAgo(endTime, unit1);
                } else {


                    //ends today in 1 unit1
                    if (timeRemaining(endTime, unit1) === 0) {
                        if (timeRan(startTime, unit2) < 0) {
                            return this.msgStartIn(startTime, unit2);
                        }
                        if (timeRemaining(endTime, unit2) < 0) {
                            return this.msgFinishedAgo(endTime, unit2);
                        } else {

                             callback()

                        }
                    }
                    return this.msgRemaining(endTime, unit1);
                }
}

code below


msgStartIn = (startTime, unit) => {
    return (
        'Start in ' +
        Math.abs(timeRan(startTime, unit)) +
        ' ' + unit
    );
};

msgFinishedAgo = (endTime, unit) => {
    return (
        'Finished ' +
        Math.abs(timeRemaining(endTime, unit)) +
        ' ' + unit + ' ago'
    );
};

msgRemaining = (endTime, unit) => {
    return (
        this.toTitleCase(unit) + ' remaining ' +
        timeRemaining(endTime, unit)
    );
};


getCampaignProgress = (startTime, endTime) => {

            if (timeRan(startTime, 'days') < 0) {
                return this.msgStartIn(startTime, 'days');
            }
            if (timeRemaining(endTime, 'days') < 0) {
                return this.msgFinishedAgo(endTime, 'days');
            } else {


                //ends today in 1 day
                if (timeRemaining(endTime, 'days') === 0) {
                    if (timeRan(startTime, 'hours') < 0) {
                        return this.msgStartIn(startTime, 'hours');
                    }
                    if (timeRemaining(endTime, 'hours') < 0) {
                        return this.msgFinishedAgo(endTime, 'hours');
                    } else {

                        //ends today in 1 hour
                        if (timeRemaining(endTime, 'hours') === 0) {
                            if (timeRan(startTime, 'minutes') < 0) {
                                return this.msgStartIn(startTime, 'minutes');
                            }

                            if (timeRemaining(endTime, 'minutes') < 0) {
                                return this.msgFinishedAgo(endTime, 'minutes');
                            } else {

                                //ends today in 1 minute
                                if (timeRemaining(endTime, 'minutes') === 0) {
                                    if (timeRan(startTime, 'seconds') < 0) {
                                        return this.msgStartIn(startTime, 'seconds');
                                    }

                                    if (timeRemaining(endTime, 'seconds') < 0) {
                                        return this.msgFinishedAgo(endTime, 'seconds');
                                    } else {
                                        return this.msgRemaining(endTime, 'seconds');
                                    }
                                }
                                return this.msgRemaining(endTime, 'minutes');
                            }
                        }
                        return this.msgRemaining(endTime, 'hours');
                    }
                }
                return this.msgRemaining(endTime, 'days');
            }


};
How to&Answers:

If I understand your requirements correctly, something like the following certainly seems cleaner.

This accepts three parameters: the start and end times of your event and the current time. They can be either Date objects or timestamps (like Date.now). You can even mix or match if that’s easier. (That’s because we are either comparing with < or subtracting, either of which will convert Dates to timestamps as necessary.)

const messages = {
  notStarted: 'Start in {time} {units}',
  remaining: '{titleUnits} remaining: {time}',
  completed: 'Finished {time} {units} ago'
}

const periods = [
  {units: 'days',    titleUnits: 'Days',    ms: 1000 * 60 * 60 * 24 },
  {units: 'hours',   titleUnits: 'Hours',   ms: 1000 * 60 * 60 },
  {units: 'minutes', titleUnits: 'Minutes', ms: 1000 * 60 },
  {units: 'seconds', titleUnits: 'Seconds', ms: 1000 },
  {units: 'unknown', titleUnits: 'Unknown', ms: -1}
]

const getTime = (msg, start, finish) => {
  const {units, titleUnits, ms} = periods .find (({ms}) => finish - start > ms)
  const time = Math .round ((finish - start) / ms)
  return messages[msg] 
    .replace(/\{time\}/g, time)
    .replace(/\{units\}/g, units)
    .replace(/\{titleUnits\}/g, titleUnits)
}

const timeMessage = (begin, end, current) => 
  begin > end 
    ? 'uh oh -- do we need to throw here?'
  : current < begin
    ? getTime ('notStarted', current, begin)
  : current < end
    ? getTime ('remaining', current, end)
  : // else
    getTime ('completed', end, current)


console .log (timeMessage (new Date (2020, 1, 21, 18, 34, 52), new Date (2020, 1, 21, 20, 34, 52), new Date (2020, 1, 21, 24, 37, 21)))
console .log (timeMessage (new Date (2020, 1, 21, 18, 34, 52), new Date (2020, 1, 21, 24, 37, 21), new Date (2020, 1, 21, 24, 19, 52)))
console .log (timeMessage (new Date(2020, 1, 27, 24, 34, 52), new Date (2020, 1, 27, 24, 37, 21), new Date (2020, 1, 21, 18, 34, 52)))

It’s more explicit, and I think makes it easier to change the text, which is concentrated in a fairly declarative fashion in the structures at the top.

The math to show the correct count is a guess. I do a simple rounding.

There is probably some possible issues on boundaries. What happens if now is exactly the end time, for instance? Those can be fixed, but I’ll leave that as an exercise for the reader.

If desired, all this could be compressed into a single function like this:

const timeMessage = (begin, end, current) => {
  const {msg, start, finish} = begin > end 
    ? {msg: 'Uh oh -- do we need to throw here?', start: begin, finish: end}
  : current < begin
    ? {msg: 'Start in {time} {units}', start: current, finish: begin}
  : current < end
    ? {msg: '{titleUnits} remaining: {time}', start: current, finish: end}
  : // else
    {msg: 'Finished {time} {units} ago', start: end, finish: current}
  const {units, titleUnits, ms} = [
    {units: 'days',    titleUnits: 'Days',    ms: 1000 * 60 * 60 * 24 },
    {units: 'hours',   titleUnits: 'Hours',   ms: 1000 * 60 * 60 },
    {units: 'minutes', titleUnits: 'Minutes', ms: 1000 * 60 },
    {units: 'seconds', titleUnits: 'Seconds', ms: 1000 },
    {units: 'unknown', titleUnits: 'Unknown', ms: -1}
  ] .find (({ms}) => finish - start > ms)
  const time = Math .round ((finish - start) / ms)
  return msg 
    .replace(/\{time\}/g, time)
    .replace(/\{units\}/g, units)
    .replace(/\{titleUnits\}/g, titleUnits)
}

While this version has a smaller line count, I prefer the first one. I think it’s cleaner.