1

I would like to get the date (without timezone or time) from differently formatted strings that are passed into my function. The problem I am having is that the dates are not processing correctly due to the timezone I am in. Here is the current function I have:

function pgFormatDate(date) {
    /* Via http://stackoverflow.com/questions/3605214/javascript-add-leading-zeroes-to-date */
    function zeroPad(d) {
        return ("0" + d).slice(-2)
    }

    if (date) {
        var parsed = new Date(date)
        return [parsed.getUTCFullYear(), zeroPad(parsed.getMonth() + 1), zeroPad(parsed.getDate())].join("-");
    } else {
        return null;
    }
}

And here is the makeshift test harness I have created for it (all "tests" should log true if the function is working.

var test1Result = dateConvertsCorrectly("Fri Jul 07 2017 22:10:08 GMT-0500 (CDT)", "2017-07-07"); // Currently working and logging true, but break when I try to fix the others
var test2Result = dateConvertsCorrectly("Fri Jul 07 2017 02:10:08 GMT-0500 (CDT)", "2017-07-07"); // Currently working and logging true, but break when I try to fix the others
var test3Result = dateConvertsCorrectly("2017-07-07", "2017-07-07"); // Currently not working and logging false
var test4Result = dateConvertsCorrectly("Fri Jul 06 2017 22:10:08 GMT-0500 (CDT)", "2017-07-06"); // Currently working and logging true, but break when I try to fix the others
var test5Result = dateConvertsCorrectly("2017-07-06T02:59:12.037Z", "2017-07-06"); // Currently not working and logging false
var test6Result = dateConvertsCorrectly("2017-06-07", "2017-06-07"); // Currently not working and logging false

console.log('test 1 passed:', test1Result);
console.log('test 2 passed:', test2Result);
console.log('test 3 passed:', test3Result);
console.log('test 4 passed:', test4Result);
console.log('test 5 passed:', test5Result);
console.log('test 6 passed:', test6Result);


function pgFormatDate(date) {
    /* Via http://stackoverflow.com/questions/3605214/javascript-add-leading-zeroes-to-date */
    function zeroPad(d) {
        return ("0" + d).slice(-2)
    }

    if (date) {
        var parsed = new Date(date)
        return [parsed.getUTCFullYear(), zeroPad(parsed.getMonth() + 1), zeroPad(parsed.getDate())].join("-");
    } else {
        return null;
    }
}

function dateConvertsCorrectly (input, expectedOutput) {
    return pgFormatDate(input) === expectedOutput;
}

Tests 3, 5, and 6 are all failing in the CDT timezone. But I would like the code to work regardless of timezone (I really just want to keep the year, month, and day that are submitted). Using moment hasn't worked because the dates don't fit into the accepted date types allowed by moment and I get an error, so I would like to be able to do this with vanilla javascript.

Here is the jsFiddle: https://jsfiddle.net/lukeschlangen/bkyquu7j/

Luke Schlangen
  • 3,722
  • 4
  • 34
  • 69
  • 1
    Can you give us an example of exactly which test is failing and in which time zone? – Mate Solymosi Jul 08 '17 at 16:07
  • Thanks for asking! Tests 3, 5, and 6 are all failing in the CDT timezone. I'll add that to the question! – Luke Schlangen Jul 08 '17 at 16:12
  • 1
    Generally speaking, using `new Date` is not recommended for parsing dates because its implementation differs across browsers. Have you tried manually specifying the format string for moment? https://stackoverflow.com/a/28002368/1242470 – Mate Solymosi Jul 08 '17 at 16:18
  • I hadn't, this seems really cool! But would this work if the format might differ from case to case? (eg Test4 vs Test6?) – Luke Schlangen Jul 08 '17 at 16:21
  • 1
    You can put the supported format strings into an array, and then try the parsing with each element until one of them gives you a valid moment (check with `m.isValid()`). – Mate Solymosi Jul 08 '17 at 16:23
  • I like that! Will I need to build the loop to do that and will it throw errors for every one that fails? Or does moment accept arrays of all possible input types? – Luke Schlangen Jul 08 '17 at 16:25
  • 1
    I think you'll have to try them one by one. If I remember correctly, moment won't throw an error if it can't parse a date; instead, it returns a special "invalid" moment object, which you can test for using `m.isValid`. – Mate Solymosi Jul 08 '17 at 16:26
  • Ok, I'll give it a shot when I have a chance and see what I can get! – Luke Schlangen Jul 08 '17 at 16:27

2 Answers2

2

Without using moment, I got manual regex parsing working for this dataset in vanilla JS.

It's not perfect, and will likely choke if you start throwing differently formatted date strings at it, but it works for this dataset.

Fiddle here.

The month parse is a big ugly switch that looks like this:

switch (true) { 
  case /Jan/i.test(date):
    parse.Month = "01";
    break;
  case /-01-/.test(date):
    date = date.replace(/-01/, '') + ' '; 
    parse.Month = "01";
    break;

But the year and date parses are reasonable enough regex matching.

 if (/\d{4}/.test(date)) {
      parse.Year = date.match(/\d{4}/)[0];
      } else {
      console.log("Year parse fail");
      }

 if (/\D\d{2}\D/.test(date)) { 
        parse.Day = date.match(/\D\d{2}\D/)[0].substring(1,3);
      } else {
      console.log("Day parse fail");
      }

The trick turned out to be parsing the month first and then removing the month data from the string, otherwise the date regex would find that first - which amusingly enough was giving the same set of errors as in the question.

hastypudding
  • 66
  • 1
  • 3
0

Big thanks to Máté Solymosi for the suggestion of custom moment date formats. I did end up using moment and here is what I got working:

function pgFormatDate(date) {
    if (date) {
        if (moment(date.substring(4,15), 'MMM DD YYYY').isValid() && date.substring(4,15).length === 11) {
            return moment(date.substring(4,15), 'MMM DD YYYY').format('YYYY-MM-DD');
        } else if (moment(date.substring(0,10), "YYYY-MM-DD").isValid() && date.substring(0,10).length === 10) {
            return date.substring(0,10); 
        } else {
            throw 'Date not formatted correctly';
        }
    } else {
        throw 'Date must exist for formatting to occur'
    }
}
Luke Schlangen
  • 3,722
  • 4
  • 34
  • 69