3
\$\begingroup\$

I'm making a function that takes the date a page was last edited, accessed through MediaWiki's API, compares it to the current date, and returns how long ago the page was edited, e.g. 1 year/2 days/etc.

It's essentially a massive if/else statement, and I was wondering if there was a better way to implement it.

var timestamp,
    utcArr,
    today,
    utcArr = [],
    timestamp2,
    timestamp3,
    timestamp4,
    i,
    mwArr = [],
    lastEdited;

today = new Date();
utcArr.push(today.getUTCFullYear());
utcArr.push(today.getUTCMonth() + 1); // returns value 0-11
utcArr.push(today.getUTCDate());
utcArr.push(today.getUTCHours());
utcArr.push(today.getUTCMinutes());

/**
 * Time function
 * For formatting the returned value for timestamp
 * example timestamp var: '2013-04-27T14:29:53Z'
 */
function time() {
        timestamp2 = timestamp.replace(/(\:|T|Z)/g, '-'); // use g to replace every occurrence, not just one
        timestamp3 = timestamp2.split('-');
        timestamp4 = timestamp3.filter(function (e) { // remove what's left from Z
                return e;
        });
        for (i = 0; i < timestamp4.length; i++) {
                mwArr.push(parseFloat(timestamp4[i]));
        }

        /**
         * Compares dates in array and produces the difference between them
         * @todo Find a better way to do this
         */
        if ((utcArr[0] - mwArr[0]) === 0) { // years
                if ((utcArr[1] - mwArr[1]) === 0) { // months
                        if ((utcArr[2] - mwArr[2]) === 0) { // days
                                if ((utcArr[3] - mwArr[3]) === 0 { // hours
                                        if ((utcArr[4] - mwArr[4]) === 0) { // minutes
                                                lastEdited = '1 minute';
                                        } else {
                                                lastEdited = utcArr[4] - mwArr[4] + ' minutes';
                                        }
                                } else if ((utcArr[3] - mwArr[3]) === 1 && (utcArr[4] - mwArr[4]) < 59) {
                                        lastEdited = utcArr[4] - mwArr[4] + ' minutes';
                                } else if ((utcArr[3] - mwArr[3]) === 1) {
                                        lastEdited = '1 hour';
                                } else {
                                        lastEdited = utcArr[3] - mwArr[3] + ' hours';
                                }
                        } else if ((utcArr[2] - mwArr[2]) === 1 && (utcArr[3] - mwArr[3]) < 24) {
                                lastEdited = utcArr[3] - mwArr[3] + ' hours';
                        } else if ((utcArr[2] - mwArr[2]) === 1) {
                                lastEdited = 1 day
                        } else {
                                lastEdited = utcArr[2] - mwArr[2] + ' days';
                        }
                } else if ((utcArr[1] - mwArr[1]) === 1 && (utcArr[2] - mwArr[2]) < 30) { // we'll just use 30 as it's easier than coding in each month individually
                        lastEdited = utcArr[2] - mwArr[2] + ' days';
                } else if ((utcArr[1] - mwArr[1]) === 1) {
                        lastEdited = '1 month';
                } else {
                        lastEdited = utcArr[1] - mwArr[1] + ' months';
                }
        } else if ((utcArr[0] - mwArr[0]) === 1 && (utcArr[1] - mwArr[1]) < 12 ) {
                lastEdited = utcArr[1] - mwArr[1] + ' months';
        } else if ((utcArr[0] - mwArr[0]) === 1) {
                lastEdited = '1 year'
        } else {
                lastEdited = utcArr[0] - mwArr[0] + ' years';
        }

        // empty the array or everything turns out the same
        mwArr.length = 0;
}

The time function is used about half a dozen times within an AJAX request which returns the edit date in the format "2013-04-27T14:29:53Z" which is used in the function to return lastEdited which is then appended to various ids in a table. I've yet to add complete support for 1 day/month/etc. but that'll just be adding in more if/else statements.

I did consider nested switch statements, but wasn't quite sure how to go about it.

\$\endgroup\$

migrated from stackoverflow.com May 4 '13 at 11:00

This question came from our site for professional and enthusiast programmers.

3
\$\begingroup\$

I looked at the source code of moment.js for inspiration:

http://momentjs.com/

And I'm trying to be as declarative as possible, using data to define the time-units I want to use:

unitMillisecondFactors = {
    'Millisecond': 1000,
    'Second': 60,
    'Minute': 60,
    'Hour': 24,
    'Day': 356,
    'Year': 10,
    'Decade': 1
};

The Javascript Date object can compute the difference between two timestamps in milliseconds. Looking at such a difference the algorithm goes like this: we currently have units Milliseconds. The factor for getting from milliseconds to seconds is 1000. is the number we'er looking at smaller then that factor? Then we're done, we return a result by rounding down and appending the unit. If we're not done, then we devide the number by the factor and try with the next unit.

function compute_time(timestamp) {
    var n = new Date() - new Date(timestamp);
    for (var unit in unitMillisecondFactors) {
        var factor = unitMillisecondFactors[unit];
        if (n < factor) {
            n = Math.floor(n);
            if (n == 1) {
                return s + "more than " + n + " " + unit + " ago\n";
            } 
            else {
                return s + "more than " + n + " " + unit + "s ago\n";
            }
        }
        n = n / factor;
    }
}

That's it. See http://jsfiddle.net/bjelline/S23V2/ for a working example with some extra output to see the steps of computation.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.