42

I'm wondering if it's possible to use AngularStrap's datepicker without it keeping the user's locale's timezone information. In our application we want to handle Contract objects that have an expiration date.

When adding or editing the contract object, there is a datepicker field for selecting the date. The following thing happens:

  1. The user selects the date (e.g. 2013-10-24)
  2. Angular binds the javascript date object to the ng-model field
  3. The binded date object is in the user's timezone (e.g. GMT+3)
  4. The user submits the form
  5. The date gets sent to the server using Angular's $http service

In step 5 the date is converted to UTC format. The selected date was GMT+3 2013-10-24 at midnight, but the UTC conversion changes the date to 2013-10-23 at 9pm.

How could we prevent the conversion, or use UTC dates during the whole process? We don't want the contract's date to change based on the user's local timezone. Instead, we want the date to be always 2013-10-24, no matter what timezone.

Our current solution was to make small changes to the AngularStrap library so that the date won't change when sent to the server.

If we could get the user's selected timezone in the server, we could make another conversion there, but the server doesn't have that information.

All ideas are appreciated!

3 Answers 3

47

The issue isn't AngularStrap. Its just how javascript dates work and how JSON formats them for transmission. When you turn a javascript date object into a JSON string, it formats the string as UTC.

For example, I'm in Utah and it is now 07:41 on 2013-10-24. If I create a new javascript date and print it to the console it will say:

Thu Oct 24 2013 07:41:19 GMT-0600 (MDT)

If I stringify that same date (using JSON.stringify(date), I get:

"2013-10-24T13:41:47.656Z"

which you can see is not in my current timezone, but is in UTC. So the conversion is happening just before the form gets sent to the server when it gets converted from a javascript object to a JSON string.

The easiest way to do it would be to just change the date to a string of your own choosing prior to sending the date to the server. So instead of letting JSON change the date to UTC, (assuming you don't care about the time of day) you could just do something like this:

var dateStrToSend = $scope.date.getUTCFullYear() + '-' + ($scope.date.getUTCMonth() + 1) +  '-' + $scope.date.getUTCDate();

That will give you a UTC-based string that looks like '2013-10-24' and then you can send that to the server, instead of the JSON format which includes the time info. Hopefully that helps.

UPDATE: As @Matt Johnson said, there are two ways to do it. You said: How could we prevent the conversion, or use UTC dates during the whole process?. If you want to use UTC, then use my above explanation. If you want to just "prevent the conversion", you could use the following:

var dateStrToSend = $scope.date.getFullYear() + '-' + ($scope.date.getMonth() + 1) +  '-' + $scope.date.getDate();
Sign up to request clarification or add additional context in comments.

2 Comments

Good explanation, but to avoid the problem the OP described you should not use UTC in the last step.
Thanks for the detailed explanation! I guess we were hoping we could automate the whole date-to-string conversion and still keep Angular automatic data binding. But this is more than enough for our purposes.
17

A bit late but I spent my afternoon on this and someone might find it useful.

Another way to do this declaratively is to use the dateType, dateFormat and modelDateFormat attributes. Set these in either the config or the HTML e.g

angular.module('app').config(function ($datepickerProvider) {
    angular.extend($datepickerProvider.defaults, {
        dateFormat: 'dd-MMMM-yyyy',
        modelDateFormat: "yyyy-MM-ddTHH:mm:ss",
        dateType: "string"
    });
});

DateFormat is the format the date will be displayed to the user in the date picker while modelDateFormat is the format it will be converted to before being bound to your model.

I also had default values coming from the server which I needed to be bound to the datepicker on page load. I therefore had to update the format the server serialized dates in JSON to match the modelDateFormat. I am using Web API so I used the below.

var jsonSettings = Formatters.JsonFormatter.SerializerSettings;
jsonSettings.DateFormatString = "yyyy-MM-ddTHH:mm:ss";

5 Comments

This is a great solution if you want to handle all your clients the same (e.g for enterprise apps where you don't care about time zones). On the client side your dates get stored as strings formatted as specified by modelDateFormat property (unfortunately undocumented). Ui representation can then be localized separately by specifying dateFormat property.
Cannot inject the $datepickerProvider. Unknown provider: $datepickerProvider
Have you added the mgcrea.ngStrap module? Is the datepicker working at all or is only the above implementation failing?
This answer has nothing to do with what the op asked. With this formatting it doesn't stop to convert to UTC and still posts wrong date to server.
@Midnight What is it doing exactly on your side? It definitely ignored the time zone and stopped subtracting one day on my side and posted the date that I required to the server.
11

The "Angular way" is to use the $filter service to format the date returned by the datepicker.

Example (HTML):

{{inpDate | date: 'dd-MM-yyyy'}}

Example (JS):

$scope.processDate = function(dt) {
return $filter('date')(dt, 'dd-MM-yyyy');
}

Plunker here

4 Comments

Yeah, I did. I was trying to say that using $filter lets you lop off the time part and use the date in whatever format you need. @"we want the date to be always 2013-10-24, no matter what timezone" How is this not relevant?
The problem is that when the "filtered" date gets sent to the server it gets serialized, and thus back into UTC with the unwanted time-adjustment changing the day of the month. My hack is to set the time to noon when filtering it, like this: $filter('date')(dt, 'yyyy-MM-ddT12:00:00.000Z'); Then the local-time won't screw things up. Note I'm not sure if it works in all time-zones. If you are in time-zone +12 and have extra summer-time adjustment, it will not work.
@WeNeigh Just because it looks like the date has no time zone information doesn't mean that the time zone information isn't there when it gets sent to the server
Much like the accepted answer, the filtered date returns a string like "02-02-2016". This does not have any time zone information. WYSIWYG. Serializing it into a JSON string should not affect it at all. Additional Plunkr: plnkr.co/edit/2UrsN5qgTPEqhQjS0F3O?p=preview

Your Answer

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.