1
\$\begingroup\$

I'm trying to convert a Date object into a weird format, like so:

03/30/2017 11:37:14:082 -0500

The issue that really trips me up is the timezone offset. There's no way that screams at me as easy to do, so here's my implementation (in coffeescript, hopefully it's easy enough to understand without intimate knowledge of coffeescript):

  timeStamp = new Date()
  offsetHours = timeStamp.getTimezoneOffset() / 60 * 100
  if offsetHours <= -1000
    offsetString = "+" + Math.abs(offsetHours)
  else if offsetHours == 0
    offsetString = "+0000"
  else if offsetHours < 0 and offsetHours > -1000
    offsetString = "+0" + Math.abs(offsetHours)
  else if offsetHours >= 1000
    offsetString = "-" + offsetHours
  else
    offsetString = "-0" + offsetHours
  timeStampString = (timeStamp.getMonth() + 1) + '/' + timeStamp.getDay() +
    '/' + timeStamp.getFullYear() + " " + timeStamp.getHours() + ":" +
    timeStamp.getMinutes() + ":" + timeStamp.getSeconds() + ":" +
    timeStamp.getMilliseconds() + " " + offsetString

I realize my implementation isn't handling time-zones that have 30 minute offsets (which will get fixed) but I am looking to see if anyone can poke any other holes they see here. This code kind of smells to me and I feel like there should be an easier way to accomplish this.

I've spoofed the date prototype for JS to test a variety of time-zones and haven't found anything weird yet. This is to support interop with another application so "use a different scheme" unfortunately isn't an option. I'd like to avoid using libraries as well.

\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

First of all, your given sample features zero-padded 2-digit months and milliseconds which you currently don't reproduce.

The mentioned 'smell' is probably caused by

  1. the 'manual' zero-padding of the timezone offset and its many if-else branches
  2. the many hardcoded numbers and strings
  3. the long illegible string concatenation

We can get rid of the self-made zero-padding by using String.padStart() or - if compatibility is a concern - one of its many alternative implementations.

We can't really get rid of the many hardcoded constants, but we can name them to make them meaningful and less magic in appearance:

const seperators = ['/', '/', ' ', ':', ':', ':', ' ', '', '', ''];

We can then replace the large string concatenation expression by leveraging one of those array's reduce or map method:

function formatDate(date) {
  const values = [
    date.getMonth() + 1,
    date.getDate(),
    date.getFullYear(), 
    date.getHours(),
    date.getMinutes(),
    date.getSeconds(),
    date.getMilliseconds(), 
    date.getTimezoneOffset() > 0 ? '-' : '+',
    Math.abs(date.getTimezoneOffset() / 60),
    Math.abs(date.getTimezoneOffset() % 60)
  ];
  const digits = [2, 2, 4, 2, 2, 2, 3, 0, 2, 2];
  const seperators = ['/', '/', ' ', ':', ':', ':', ' ', '', '', ''];
  
  return values.map((value, i) => 
    value.toString().padStart(digits[i], '0') + seperators[i]
  ).join('');
}

console.log(formatDate(new Date()));

That's it. I don't see how we can leverage any other built-in method to shorten above task. Using JavaScript's built-in Date.toLocaleTimeString('en-US', options) gets us pretty far, but it is missing timezone offset and millisecond options and introduces unwanted separators:

const options = {
  year: 'numeric',
  month: '2-digit',
  day: '2-digit',
  hour: '2-digit',
  minute: '2-digit',
  second: '2-digit',
  hour12: false
};

console.log(new Date().toLocaleString('en-US', options));

PS: Above code should translate pretty easily to coffeescript, but as I am not very well versed in that language, I chose to give code samples in vanilla JS.

Edit: Instead of getDay() you probably want to use getDate().

\$\endgroup\$
2
  • \$\begingroup\$ Thanks! Your implementation will probably make it into some open-source production code. I really appreciate your help. \$\endgroup\$ Commented Aug 15, 2017 at 6:06
  • \$\begingroup\$ @CamdenClark Thanks! I just noticed that you probably wanted to use getDate() instead of getDay() in your code. getDay() returns the day of the week with 0 for sunday, and getDate() returns the day of the month as expected. \$\endgroup\$
    – le_m
    Commented Nov 23, 2017 at 3:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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