Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I am using Node.js to query MongoDB and have a field that is a Date (ISODate). In Node.js after querying the Date format that is returned looks like this

DT : 2014-10-02T02:36:23.354Z

What I am trying to figure out is how based on the code i have below, can efficiently convert the DT field from UTC to Local Time ISOString. In other words if local time is EDT then something like this

DT : 2014-10-02T23:36:23.354Z

I don't think there is anything I can do in the query itself from Mongo. Should I traverse the Array result set and manually change the dates? Is there a better approach here? I am sending the response to an HTTP client.

collection.find(query,options).toArray(function (err, items) {
          if (err) {
             logAndSendDebugError(500, "Error issuing find against mongo Employees Collection -" + type, res);
           } else {
              var response = {
                               'type': type,
                               'employees': items
                             };
               res.jsonp(response);
           }
 });
share|improve this question

2 Answers 2

up vote 2 down vote accepted

I would have thought the most efficient way to handle dates between the client and server would be to use the EJSON package. This covers a few things that are skirted around in other discussion here and the importance is placed on maintaining "type fidelity" when handling JSON conversion.

So what you are getting right now is the result of a "string" which is called from the "Date" object in response to a JSON.stringify call. Whatever the method being used, this is essentially what is happening where the .toJSON() method is being called from the "Date" prototype.

Rather than muck around with the prototypes or other manual processing of coversions, the EJSON package allows you to call EJSON.stringify instead which has some built in behavior to preserve types, where specifically the generated JSON string would look like this for a Date element:

{ "myCreatedDate": { "$date": 1412227831060 } }

The value there is an epoch timestamp, essentially obtained from the .valueOf() prototype method, but the field is given a special structure automagically as it were. The same is true for types other than dates as well.

The corresponding "client" processing which you can add with simple includes to your web application in the browser, e.g:

<script src="components/ejson/base64.js"></script>
<script src="components/ejson/ejson.js"></script>

This allows a same EJSON object to be present where you can process the received JSON with EJSON.parse. The resulting JavaScript Object is maintained as a "Date" type when the de-serialize is done.

var obj = EJSON.parse( "{ \"myCreatedDate\": { \"$date\": 1412227831060 } }" );

{
    myCreatedDate: /* Actually a Date Object here */
}

So now in your client browser, you have a real Date object without any other processing. Any .toString() method called on that object is going to result in a value represented in a way that matches the current locale settings for that client.

So if you use this to pass the values around between server and client in a way that is going to maintain an actual "Date" object, then the correct Object values are maintained on either client and server and needs no further conversion.

Very simple to include in your project and it takes a lot of the heavy lifting of maintaining "timezone" conversions off your hands. Give it a try.

Probably worth noting that the "core" of this comes from a MongoDB specification for Extended JSON Syntax. So aside from this (partial) implementation in the EJSON package, the same "type identifiers" are supported in several MongoDB tools as well as within several driver implementations with a custom JSON parser that will automatically convert the types. Notably the Java and C# drivers have this capability shipped with the driver libraries.

It's fairly easy to follow the convention outlined in that link, and it is intended to "map" to the BSON type specifications as well. At the worst, you can always "inspect" the results from a standard JSON parser and implement custom routines to "re-instantiate" the "types". But as noted, the software is already in place with several libraries.

share|improve this answer
    
The only caveat here is that if the JSON is to be used by other systems, they might not know how to deal with $date. It might have been better to call it timevalue or similar. But that's OT here. :-) –  RobG Oct 3 '14 at 6:15
    
@RobG Added a little more information for what it is worth. There is actually a specification that this is built on, so it's not just a stand-alone idea. There are existing libraries for other language platforms. MongoDB itself will "complain loudly" if you attempted to store a "key" beginning with $ as it is considered to be reserved. –  Neil Lunn Oct 3 '14 at 6:35
    
Thanks for the extras. –  RobG Oct 3 '14 at 8:20

In ES5, Date.parse should be able to parse that format, however it isn't reliable. Manually parsing it isn't hard:

// Parse ISO 8601 UTC string like 2014-10-02T23:36:23.354Z
function parseISOUTC(s) {
  var b = s.split(/\D/);
  return new Date(Date.UTC(b[0], --b[1], b[2], b[3], b[4], b[5], b[6]));
}

The date instance that is created will have a local timezone offset calculated from system settings. To get a UTC ISO 8601 string from a local date you can use toISOString:

var date = new Date();
console.log(date.toISOString());

Note that toISOString is ES5 so may need a polyfill if this is used in older browsers. See MDN Date.prototype.toISOString().

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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