PastorGL inherited some front-end code. This front-end code only talks to a single, in-house developed back-end. Unfortunately, that single backend wasn’t developed with any sort of consistency in mind. So, for example, depending on the end-point, sometimes you need to pass fields back and forth as productID, sometimes it’s id, productId, or even _id.

Annoying, but even worse is dealing with the dreaded date datatype. JSON, of course, doesn’t have a concept of date datatypes, which leaves the web-service developer needing to make a choice about how to pass the date back. As a Unix timestamp? As a string? What kind of string? With no consistency on their web-service design, the date could be passed back and forth in a number of formats.

Now, if you’re familiar with the JavaScript Date datatype, you’d know that it can take most date formats as an input and convert them into a Date object, which gives you all the lovely convenience methods you might need. So, if for example, you wanted to convert a date string into a Unix timestamp, you might do something like this:

    var d = new Date(someDataThatProbablyIsADateStringButCouldAlsoBeANumber); //Could also use Date.parse
    return d.getTime();

That would cover 99% of cases, but PastorGL’s co-worker didn’t want to cover just those cases, and they certainly didn’t want to try and build any sort of consistency into the web service. Not only that, since they knew that the web service was inconsistent, they even protected against date formats that it doesn’t currently send back, just in case it starts doing so in the future. This is their solution:

/**
 * Converts a string into seconds since epoch, e.g. tp.str2timestamp('December 12, 2015 04:00:00');
 * @param str The string to convert to seconds since epoch
 * @returns {*}
 */
function str2timestamp(str) {
    if (typeof(str) == "undefined" || str.length == 0) {
        return;
    }

    if (typeof(str) != "string") {
        str = "" + str;
    }

    str = str.trim();

    // already a unix timestamp
    if (str.match(/^[0-9]{0,10}$/)) {
        // TODO: fix this before january 19th, 2038 at 03:14:08 UTC
        return parseInt(str);
    }

    // most likely a javascript Date timestamp that is in milliseconds
    if (str.match(/^[0-9]{13,}$/)) {
        return parseInt(str) / 1000;
    }

    var ts = Date.parse(str);
    if (ts) {
        return ts / 1000;
    }

    // fix 00:XX AM|PM & 00:XX:XX AM|PM
    str = str.replace(/00:([0-9]{2}(:[0-9]{2})?\s*[AP]M)/i, "12:$1").replace(/([0-9]{2})([AP|M])/i, "$1 $2");

    // remove any "on, at, @, or -" from the date
    str = str.replace(/\s*(at|@|\-|on|\|)\s*/gi, " ");

    str = str.replace(/\s*(mon(day)?|tue(s?day)?|wed(nesday)?|thu((rs)?day)?|fri(day)?|sat(urday)?|sun(day)?)\s*/gi, "");

    str = str.replace(/([0-9]{1,2})(st|nd|rd|th)/, "$1");

    // replace bad time zone
    if (str.match(/\s+ET$/)) {
        if (d.getTimezoneOffset() == 240) {
            str = str.replace(/\s+ET$/, " EDT");
        } else {
            str = str.replace(/\s+ET$/, " EST");
        }
    }

    str = str.trim();

    var ts;

    ts = Date.parse(str);
    if (ts) {
        return ts / 1000;
    }

    // jan/3/2001
    if (m = str.match(/!^([a-z]+)[-/ ]([0-9]+)[-/ ]([0-9]+)(.*)$!i/)) {
        str = m[2] + " " + m[1] + " " + m[3] + m[4];
    } else if (m = str.match(/!^([0-9]+)[-/ ]([a-z]+)[-/ ]([0-9]+)(.*)$!i/)) {
        // 3/jan/2008
        str = m[1] + " " + m[2] + " " + m[3] + m[4];
    }

    ts = Date.parse(str);
    if (ts) {
        return ts / 1000;
    }
};

I particularly like that, if it’s not a string already, they turn it into one, because regexes are the best way to count the digits in a string, apparently.

In the end, is TRWTF this code, or the inconsistent representations of the web service endpoints?

The Mexican girl from the taco commercial who says, 'Why not both?'

[Advertisement] BuildMaster allows you to create a self-service release management platform that allows different teams to manage their applications. Explore how!