2

I currently have a function that will convert a string to a Date object, the string may contain or not time information, if it doesn't I set hours and minutes to 0:

const stringToDate = string => {
  const [, y, m, d, h = 0, min = 0] = string.match(/(\d{4})-(\d{2})-(\d{2})(?: (\d{2}):(\d{2}))?/)
  return new Date(y, parseInt(m) - 1, d, h, min)
}

console.log(stringToDate("2017-07-09"))

I wonder if it would be more performant and or more reliable if I would just rely on Date object to do it for me like so:

// Replace '-' with '/' for Safari.
const stringToDate = string => new Date(string.replace(/-/g, '/')) 

console.log(stringToDate("2017-07-09"))

I am aware the Date methods behave differently according to browser (e.g. Safari can't read '-'), and I am also concerned about daylight saving possible errors. Are both ways safe?

Could not find an answer in Converting a string to a date in JavaScript

halfer
  • 19,824
  • 17
  • 99
  • 186
antoni
  • 5,001
  • 1
  • 35
  • 44
  • 2
    What is better depends on your expected inputs. As in the question you linked, I would also suggest using moment.js – Y.L Nov 15 '19 at 05:55
  • Thanks for suggestion @Y.L. I am the author of a no-dependency library, it is already working but I am just concerned about performance and reliability. – antoni Nov 15 '19 at 05:59
  • As @Y.L mentioned `moment.js` is a great option for a production grade application because it's built with 0 dependencies, open-source and backed up by around 500 contributors. You can use that code rather than taking risk of writing your own code. –  Nov 15 '19 at 06:14
  • 1
    I have a good knowledge of moment.js which I use in many project. but this question has nothing to see with moment as the title suggests. – antoni Nov 15 '19 at 06:18
  • 1
    You should always strive to have your strings in ISO-8601 format - this is the most reliable and cross-browser compatible format as it will include the timezone so the proper daylight-savings will be performed. – IVO GELOV Nov 15 '19 at 09:15
  • Yes @IVOGELOV, thanks for your comment that's quite right,.. food for thoughts. But unfortunately in this case I can't force users to input such complex format: they are allowed to provide a native JS Date, or a string with this format 'yyyy-mm-dd hh:mm'. – antoni Nov 15 '19 at 09:32
  • 1
    @IVOGELOV—you should reference ECMA-262 format, not ISO 8601. While one format supported by ECMA-262 is ISO 8601 compliant, the opposite is not true, i.e. there are many ISO formats that are not supported by ECMA-262, e.g. 2019-11-15T12:57:18.408+0530 and 2019-11-15 12:57:18.408+05:30 are both ISO 8601 compliant but not ECMA-262 compliant and will result in an invalid date in some browsers. – RobG Nov 15 '19 at 12:56
  • @RobG You are completely right. If the date/time comes from the user - then you should make a trade-off. The option for advanced users is to use 2 fields (one which strictly expects a date in YYYY-MM-DD format, and another which strictly expects HH24:MM time) and the option for novice users is to provide date-picker and time-picker widgets/components. You already know that you should not rely on the browser to properly interpret something like "December 19 2019 13:40pm GMT+0200" – IVO GELOV Nov 16 '19 at 11:58

3 Answers3

1

TL;DR

Using new Date() is faster, both methods are reliable - tested on Chrome, Firefox, Safari.

I wrote test cases on 5 years for the 2 functions. Then I have run benchmark tests on Chrome, Safari, and Firefox to compare them, here are the results:

https://jsperf.com/string-to-date-regexp-vs-new-date/1

enter image description here

Same results on jsbench.me:

enter image description here

We can see that using regexp is slower for all the cases, (and that Safari is slower than other browsers for this operation).

I also added a check in both cases to validate that the calculated date from stringToDate function is always correct on 5 years (that obviously crosses 10 DLS dates and at least 1 leap year).

If the returned date would be erroneous, the loop would have ended in an error, here are my tests:

  1. Via regexp
const stringToDate = string => {
  const [, y, m, d, h = 0, min = 0] = string.match(/(\d{4})-(\d{2})-(\d{2})(?: (\d{2}):(\d{2}))?/)
  return new Date(y, parseInt(m) - 1, d, h, min)
}

let tmpDate = new Date()
for (let i = 0; i <= 365 * 5; i++) {
  let y = tmpDate.getFullYear()
  let m = tmpDate.getMonth()
  let d = tmpDate.getDate()

  tmpDate = new Date(y, m, d + 1, 0, 0)
  y = tmpDate.getFullYear()
  m = tmpDate.getMonth() + 1
  d = tmpDate.getDate()

  const tmpDateFormatted = `${y}-${m < 10 ? '0' : ''}${m}-${d < 10 ? '0' : ''}${d}`
  const calculatedDate = stringToDate(tmpDateFormatted)
  // console.log(calculatedDate)
  
  if (calculatedDate.getTime() !== tmpDate.getTime()) {
    console.error('Wrong date.', calculatedDate, 'should be', tmpDate)
  }
}
  1. Via new Date()
// Replace '-' with '/' for Safari.
const stringToDate = string => new Date(string.replace(/-/g, '/')) 

let tmpDate = new Date()
for (let i = 0; i <= 365 * 5; i++) {
  let y = tmpDate.getFullYear()
  let m = tmpDate.getMonth()
  let d = tmpDate.getDate()

  tmpDate = new Date(y, m, d + 1, 0, 0)
  y = tmpDate.getFullYear()
  m = tmpDate.getMonth() + 1
  d = tmpDate.getDate()

  const tmpDateFormatted = `${y}-${m < 10 ? '0' : ''}${m}-${d < 10 ? '0' : ''}${d}`
  const calculatedDate = stringToDate(tmpDateFormatted)
  // console.log(calculatedDate)
  
  if (calculatedDate.getTime() !== tmpDate.getTime()) {
    console.error('Wrong date.', calculatedDate, 'should be', tmpDate)
  }
}

Hope it helps someone!

Community
  • 1
  • 1
antoni
  • 5,001
  • 1
  • 35
  • 44
0

Your regex implies that you are using non-standard date time formats which fail for example in Safari. And even if you corrected the format, you would still have the problem that Safari's implementation of parsing times without timezones is wrong (i.e. not according to the specification).

new Date('2019-11-15 10:06'); // DO NOT USE: fails in Safari

Therefore it is better to stick with your stringToDate function to create date objects.

str
  • 42,689
  • 17
  • 109
  • 127
  • Thanks, btw I already said that it is not usable in Safari as is, that's why my function replaces the `-` with `/`. This is working fine in safari. But any idea about performances? – antoni Nov 15 '19 at 09:24
  • I was not talking about slashes vs. hyphens but the white space between date and time. Why do you care about performance when one of the approaches is wrong anyway? Using a simple, single regex is negligible performance-wise. – str Nov 15 '19 at 09:40
  • unlike `new Date('2019-11-15 10:06')`, `new Date('2019/11/15 10:06')` does not fail in Safari. It converts to correct Date. – antoni Nov 16 '19 at 04:01
  • Yet it is not a valid date format according to the specification. It might break in certain browsers, certain browser versions, or it might even break in the next version of Safari. Although that is highly unlikely, it is not impossible. If you want a reliable, backward-compatible, and future-proof solution you should not use the `Date` constructor like that. – str Nov 16 '19 at 09:03
0

You have answered your question:

I wonder if it would be more performant and or more reliable if I would just rely on [the built–in parser] to do it for me…

I am aware the Date [parsers] behave differently according to browser…

So you already know that using the built–in parser is unreliable.

There are only two formats supported by ECMA-262, and at least one current browser fails to parse one of them, and older browsers that also fail. So your choices are:

  1. Use a simple function to parse a specific format string where you are certain of the result
  2. Use a library and parser where you supply the format to parse and are again certain of the result
  3. Hope that whatever implementation runs your code happens to parse it correctly on the basis that the couple of browsers you tried did so, ignoring the huge number of other existing and future implementations that might run your code

Not really a hard choice.

RobG
  • 142,382
  • 31
  • 172
  • 209