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.

200_success
145k22 gold badges190 silver badges478 bronze badges
asked May 24, 2017 at 22:50
\$\endgroup\$

1 Answer 1

1
\$\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().

answered May 25, 2017 at 0:38
\$\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\$ Commented Nov 23, 2017 at 3:40

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.