Working with UTC and current time zone - angularjs

I'm working on a project that was used only in one country but now is in using in several countries.
So I'm working in some DateTime issues, as you can image.
I'm using angular js for my frontend, python for my backend and Postgres as my database in this project.
To avoid any problem with DateTime and try to make more easy to work with the timezones I'm saving the DateTime in the database as UTC.
from DateTime import DateTime
# inside a class of my entity
self.start_date = datetime.utcnow()
This is working fine, the problem is when I try to convert the date back.
For example.
If my application is running in a country with GMT -1, when the user
asks to save the entity and it's 2016-07-13 15:00:00, in the database
(using the UTC now()) the DateTime will become 2016-07-13 16:00:00.
But when I try to get back the value I have two scenarios:
If I don't do anything, I'll receive the DateTime like it's on the database, "2016-07-13 14:00:00"
If I try to convert to the local timezone, I'm getting like 2016-07-13 17:00:00. The time was increased by 1 and not decrease was I expected.
I'm trying to use the momentjs library to work with dates, but nothing seems to work.
I'm wondering if what I supposed to do is get the GMT, like (-01:00) then do some math with the DateTime that comes from the database, like sub or sum the GMT hours difference.

Solution:
My solution for this was, store everything as UTC in my database.
Retrieve the datetime as UTC and covert to browsers timezone using momentjs library.
Doing like this.
moment.tz(moment.utc(datetime), moment.tz.guess());

Related

Angular - Use of i18n and l10n for date time and timezone management

On server side DateTime is saved as per UTC (2016-03-24 17:45:12) and to client it is always returned DateTime as UTC. There are different users from world.
What is the best way to do date-time display management with i18n and l10n in angular?
Also i want that it should display date-time as per timezone of the user.
There are lots of feeds shown from all over the world like StackOverflow Posts. From server side always DateTime are passed in UTC time zone. On client side, date time can be displayed in two formats like below.
Showing date time of post as per time zone
Showing time-ago facility like (Just Now, Minute Ago, Hour Ago, Day Ago, Month Ago, Year Ago, 2 Years ago, 3 Years ago, etc)
This type of date times displayed across system multiple times for posts. So what is best way to use angular to have very less code for managing this.
How/When/Why to use Filters, Directives, Expressions for this?
(P.S. I am new to angular.)
Update:
More explanation:
Post object can be single page or array of objects.
And post date time will be like UTC: (2016-03-24 17:45:12)
So there are two cases which can be displayed like:
5 Months Ago
2016-03-24 11:15 PM (IST time)
You can trust the users' browsers. To display a date, you can use myDate.toLocaleString(). It will display the date using user's locale settings.
For "time-ago" format, you can use AngularJS version of moment.js: https://github.com/urish/angular-moment#am-time-ago-directive

Correct Way to Pass Dates from Angular to Web API 2

I have an Angular application that takes dates (date only not time) and posts them to a Web API 2 REST service. We are running into an issue when someone from India uses the application due to timezone issues.
Currently the Angular app is converting the date into ISO8601 format in UTC timezone and sending them to Web API. When the data is received on the Web API side, the date ends up being incorrect. If 6/21/2016 was put into the form, the date ends up coming over as 6/20/2016. The desired solution is to have the actual date value entered in the form be the date value received by the API.
One proposed solution is to treat the dates as strings instead of Dates and then just pass the date portion. This just seems like a hack to me and doesn't seem like the "correct" way of doing it.
What is the correct way of handling this situation?
Given the fact that the application has a lot of date field inputs is there an easy way to implement the solution across all Date input values?
The date format yyyy-MM-dd will be accepted in US / India so you can pass it so to your WebAPI. In JavaScript before posting you can alter the date like this.
$scope.MyDate = $filter('date')($scope.MyDate, 'yyyy-MM-dd', timezone);
If you want time too, the format will be yyyy-MM-ddTHH:mmZ

Date from angular(timezone) to server (utc) then utc to Timezone

I have an issue with getting dates back in the right timezone.
Firs at all in the local machine, everything works fine but not in the server: the server is hosted in USA and the clients mostly are in Australia.
So a date is sent from angular app("12/23/2015 11:00:00 AM") to the server, the server store a date as utc in the database, until this point everything is working(I checked and the date is stored in the right utc)
book.StartDateTime = TimeZoneInfo.ConvertTimeToUtc(DateTime.SpecifyKind(book.StartDateTime.Value, DateTimeKind.Unspecified), ToolsHelper.OlsonTimeZoneToTimeZoneInfo(locationDetails.TimeZone)); // book.CreatedDate.Value.ToUniversalTime();
The issue is:
When a client request some dates stored in the database. The date stored in the database is return back to the client like this:
bookview.StartDateTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.SpecifyKind(bookli.StartDateTime.Value, DateTimeKind.Utc), ToolsHelper.OlsonTimeZoneToTimeZoneInfo(deCompany.TimeZone));
I checked and at this point the date is "12/23/2015 11:00:00 AM" -> the conversion is right in the server(in the server side I put a log),
But in the angular is shown as "12/23/2015 10:00:00 PM"
So apparently the issue is when the date is transferred by the api to the client, maybe when is converted to JSON
I have tried different ways nothing work, I have removed "DateTime.SpecifyKind", I convert the date to string then back to datetime format and nothing seems to work.
What could I do?
A few things:
Your example is not complete, so I can only speculate on some areas. It would be better to show both sides, including how you load and parse the data in Angular, and what the data looks like over the wire.
You shouldn't be sending dates back and forth in a locale-specific format like "12/23/2015 11:00:00 AM". You might use those in your UI, but they're not appropriate over the wire (in your JSON). Instead, you should be using ISO8601/RFC3339, such as "2015-12-23T11:00:00Z". (You probably are already doing this if you're using WebAPI.)
A DateTime object when serialized to ISO8601 format is coupled with the associated DateTimeKind in the Kind property.
If the Kind is Utc, then the ISO8601 string will end with a Z.
If the Kind is Local, then the ISO8601 string will end with the machine-local offset for that timestamp, such as -08:00.
If the Kind is Unspecified, then the ISO8601 string will not have either Z or offset - which means that it cannot unambiguously represent a specific moment in time.
This is ultimately the cause of the error. You're converting the DateTime to another time zone, which leaves it with Unspecified kind, which then gets serialized without an offset - so on the client side that gets interpreted (probably) in the local time zone of the browser.
A better approach is to use DateTimeOffset instead of DateTime. Then you don't have to worry about Kind, and the offset is always present. If you change your bookview.StartDateTime to a DateTimeOffset type, you can do something like this to fix the problem:
DateTimeOffset dto = new DateTimeOffset(bookli.StartDateTime.Value, TimeSpan.Zero);
bookView.StartDTO = TimeZoneInfo.ConvertTime(dto, ToolsHelper.OlsonTimeZoneToTimeZoneInfo(deCompany.TimeZone));
That will ensure that the offset gets persisted in the data.
On the client side, pay careful attention to how the ISO string gets parsed. if it's loaded into a Date object, then it will indeed be converted to the client's time zone. Instead, you might take a look at moment.js for client-side time formatting. In particular, use moment.parseZone to keep the value in the same offset that it was presented. For example:
var s = moment.parseZone("2015-12-31T11:00:00+00:00").format("L LT"); // "12/31/2015 11:00 AM"
In commented code, you also showed a call to DateTime.ToUniversalTime - be very careful with that. If the source kind is Unspecified, it is treated as Local. Therefore, the local time zone of the computer is reflected in the converted value. It's better to avoid ToUniversalTime and ToLocalTime entirely. Use only the conversion methods on TimeZoneInfo.
ToolsHelper.OlsonTimeZoneToTimeZoneInfo isn't something we know about either. But I'll assume it does a CLDR mapping similar to this one. However, if you're working with Olson time zones anyway, the better approach would be to not use TimeZoneInfo at all. Instead, use Noda Time, with it's native support for tzdb time zones.

Javascript time saved incorrectly in sql server table

new Date(moment().year(), moment().month(), moment().day(), vm.newHearing().HearingTime().split(":")[0], vm.newHearing().HearingTime().split(":")[1]).toLocaleString()
The client side value for a date column is 11/5/2013 10:15:00 AM. The time is selected from HTML5 time input control.
When I check in database after saving the entity, it shows me incorrect time value:
11/5/2013 3:15:00 PM
It appears that you are using moment.js, which is fine except you aren't using it properly
Try this instead:
moment(vm.newHearing().HearingTime(), "HH:mm").toISOString()
That will pass the selected time, on the current day, from the user's local time zone, converted to UTC time and in ISO format.
Now that might not be exactly what you want to do. Depending on your requirements, you might instead want this:
moment.utc(vm.newHearing().HearingTime(), "HH:mm").toISOString()
Which is almost the same thing except that it assumes the input time is already in UTC.
Or you might want this:
moment(vm.newHearing().HearingTime(), "HH:mm").format("YYYY-MM-DDTHH:mm:ss")
This one doesn't try to convert to UTC at all.
For all choices, I emit the date string in ISO8601 format. Since you are sending it back to the server, this is the best choice. When you used toLocaleString, that generates a format that is appropriat for display only.

Why doesn't appengine auto-convert datetime to UTC when calling put()

Here's what I'm trying to do: the user submits a time in pacific, once submitted I use .replace to set the timezone to Pacific.
Pacific = time.USTimeZone(-8, "Pacific", "PST", "PDT")
addEvent.date = addEvent.date.replace(tzinfo=Pacific)
Once i've set the tzinfo, I'm doing a put. According to the python documentation of google appengine it says:
"If the datetime value has a tzinfo attribute, it will be converted to the UTC time zone for storage. Values come back from the datastore as UTC, with a tzinfo of None. An application that needs date and time values to be in a particular time zone must set tzinfo correctly when updating the value, and convert values to the timezone when accessing the value."
However, when I do a put(), i get the following error:
WARNING 2012-10-06 21:10:14,579 tasklets.py:399] initial generator _put_tasklet(context.py:264) raised NotImplementedError(DatetimeProperty date can only support UTC. Please derive a new Property to support alternative timezones.)
WARNING 2012-10-06 21:10:14,579 tasklets.py:399] suspended generator put(context.py:703) raised NotImplementedError(DatetimeProperty date can only support UTC. Please derive a new Property to support alternative timezones.)
Please note I am using NDB
Ok, so after doing that I assumed that maybe NDB doesn't automatically convert it into UTC. So then I tried to convert it to UTC using the following code:
class UTC(tzinfo):
def utcoffset(self, dt):
return timedelta(0)
def tzname(self, dt):
return str("UTC")
def dst(self, dt):
return timedelta(0)
and now I still get the same error even after I convert the pacific time to UTC and set the tzinfo name as "UTC".
Could really use a ton of help here...
thanks!
The solution is to remove the tzinfo completely from the time after converting to UTC.
timestamp = timestamp.replace(tzinfo=None)
Here my working example:
if my_date.utcoffset():
my_date = (my_date - my_date.utcoffset()).replace(tzinfo=None)
I ran into this using http://hnrss.org/. This was my solution.
import datetime
from dateutil import parser
your_date_string = 'Mon, 24 Oct 2016 16:49:47 +0000'
your_date = parser.parse(your_date_string)
# your_date is now a datetime.datetime object
your_date_minus_tz = your_date.replace(tzinfo=None)
Now try your put() and you should see 2016-10-24 16:49:47 in the Cloud datastore.

Resources