How to save correct time in database? - javascript

I have one object called appointment which has two properties: StartDate and EndDate.
When I make POST request I send these values using ISOString time .
this.appointment.StartDate.toISOString()
On the server-side, I received these properties with correct values. Also, it seems to be correct when I create model in order to save appointment to the database. I used .ToUniversalTime() method.
var newAppointment = new Appointment()
{
StartDate =Convert.ToDateTime(model.StartDate).ToUniversalTime(),
EndDate = Convert.ToDateTime(model.EndDate).ToUniversalTime(),
SpecialityId = speciality.Id,
LocationId = location.Id,
PatientId = patient.Id,
UserId = user.Id,
Observations = model.Observations
};
But in database I found another values. Can explain somebody why is this behaviour ?
For instance, I used 2017.09.01 11:00 for StartDate and in database i found 2017-09-01 08:00
The server and database is located in the westeurope.

A few things:
Don't call ToUniversalTime in a web application. It's designed to convert from the server's local time zone to UTC. The server's time zone should be irrelavent to your application. Web applications should never use ToUniversalTime, ToLocalTime, DateTime.Now, TimeZoneInfo.Local, DateTimeKind.Local or any other method that uses the time zone of the computer it's running on.
Ideally, on the server side, your model.StartDate and model.EndDate would already be DateTime objects, because they'd have been deserialized that way. Therefore, you probably don't need to call Convert.ToDateTime. If they are strings, then I would adjust your model class accordingly.
On the client side, assuming StartDate and EndDate are JavaScript Date objects, and they were created using local time values (that is, the time zone of the browser), when you call toISOString, you're not just getting a string in ISO 8601 format - it is also converting it from the browser's time zone to UTC.
In your example, the UTC time is 3 hours ahead of UTC for the date and time shown. From your profile, I see you are located in Romania, which is indeed UTC+3 for this date, because it is currently observing Eastern European Summer Time. When Summer Time ends (on October 29, 2017 at 04:00), it will return to UTC+2. For this reason, you cannot simply add three hours to all values.
If you want to send local time values from the client, you should send them in ISO 8601 format, without any Z or offset, for example 2017-09-01T11:00. There are several ways to achieve this:
The best way is to not have them in a Date object to begin with. For example, if your input uses the <input type="datetime-local" /> input type (as specified in HTML5), the .value property is not a Date object, but rather a string in ISO 8601 format.
If you can't avoid a Date object, then create a local ISO string, like this:
function dateToLocalISOString(date) {
var offset = date.getTimezoneOffset();
var shifted = new Date(date - offset * 60 * 1000);
return shifted.toISOString().slice(0, -1);
}
OR, using Moment.js:
moment(yourDateObject).format("YYYY-MM-DD[T]HH:mm:ss.SSS")
Lastly, you will probably read advice from others about storing these as UTC. Don't listen. The advice "always use UTC" is shortsighted. Many scenarios require local time. Scheduling appointments is a primary use case for local time. However, if you need to act on that appointment, you'll use the current UTC time, and you'll also need some information about the time zone for the appointment so you can convert from UTC to the appointment's time zone. For example, if this is something like an in-person doctor's office appointment, then it's safe to assume the time zone of the doctor's office. But if it's an appointment for an online meeting, then you'll have to capture the user's time zone separately and apply it on the back end where appropriate.

The problem is with your current timezone.
What your application does is get current timezone (+3) in this case.
Now it got your timezone but it will convert to utc time. So what will happen, your current time will be -3 hours.
If you not adjust to summer and winter time then you can simply add 3 hours to the datetime. Otherwise you have to get the offset of your timezone and add that to the current datetime value.
Take care if you use this application in different timezones. For example You life in +3 and some else life in +2 timezone.

Related

Working with different timezones in Javascript

I am working on a cloud based application which deals extensively with date and time values, for users across the world.
Consider a scenario, in JavaScript, where my machine is in India (GMT+05:30), and I have to display a clock running in California's timezone (GMT-08:00).
In this case I have to get a new date object,
let india_date = new Date()
add it's time zone offset value,
let uts_ms = india_date.getTime() + india_date.getTimezoneOffset()
add california's timezone offset value,
let california_ms = utc_ms + getCaliforniaTimezoneOffsetMS()
and finally the date object.
let california_date: Date = new Date(california_ms)
Is there any way to directly deal with these kinds of time zones without having to convert the values again and again?
First, let's talk about the code in your question.
let india_date = new Date()
You have named this variable india_date, but the Date object will only reflect India if the code is run on a computer set to India's time zone. If it is run on a computer with a different time zone, it will reflect that time zone instead. Keep in mind that internally, the Date object only tracks a UTC based timestamp. The local time zone is applied when functions and properties that need local time are called - not when the Date object is created.
add it's timezone offset value
let uts_ms = india_date.getTime() + india_date.getTimezoneOffset()
This approach is incorrect. getTime() already returns a UTC based timestamp. You don't need to add your local offset. (also, the abbreviation is UTC, not UTS.)
Now add california's timezone offset value
let california_ms = utc_ms + getCaliforniaTimezoneOffsetMS()
Again, adding an offset is incorrect. Also, unlike India, California observes daylight saving time, so part of the year the offset will be 480 (UTC-8), and part of the year the offset will be 420 (UTC-7). Any function such as your getCaliforniatimezoneOffsetMS would need to have the timestamp passed in as a parameter to be effective.
and finally the date object
let california_date: Date = new Date(california_ms)
When the Date constructor is passed a numeric timestamp, it must be in terms of UTC. Passing it this california_ms timestamp is actually just picking a different point in time. You can't change the Date object's behavior to get it to use a different time zone just by adding or subtracting an offset. It will still use the local time zone of where it runs, for any function that requires a local time, such as .toString() and others.
There is only one scenario where this sort of adjustment makes sense, which is a technique known as "epoch shifting". The timestamp is adjusted to shift the base epoch away from the normal 1970-01-01T00:00:00Z, thus allowing one to take advantage of the Date object's UTC functions (such as getUTCHours and others). The catch is: once shifted, you can't ever use any of the local time functions on that Date object, or pass it to anything else that expects the Date object to be a normal one. Epoch shifting done right is what powers libraries like Moment.js. Here is another example of epoch shifting done correctly.
But in your example, you are shifting (twice in error) and then using the Date object as if it were normal and not shifted. This can only lead to errors, evident by the time zone shown in toString output, and will arise mathematically near any DST transitions of the local time zone and the intended target time zone. In general, you don't want to take this approach.
Instead, read my answer on How to initialize a JavaScript Date to a particular time zone. Your options are listed there. Thanks.
JavaScript Date objects store date and time in UTC but the toString() method is automatically called when the date is represented as a text value which displays the date and time in the browser's local time zone. So, when you want to convert a datetime to a time zone other than your local time, you are really converting from UTC to that time zone (not from your local time zone to another time zone).
If your use case is limited to specific browsers and you are flexible on formatting (since browsers may differ in how they display date string formats), then you may be able to use toLocaleString(), but browsers like Edge, Android webview, etc do not fully support the locales and options parameters.
Following example sets both the locale and timezone to output the date in a local format that may vary from browser to browser.
const dt = new Date();
const kolkata = dt.toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' });
const la = dt.toLocaleString('en-US', { timeZone: 'America/Los_Angeles' });
console.log('Kolkata:', kolkata);
// example output: Kolkata: 19/3/2019, 7:36:26 pm
console.log('Los Angeles:', la);
// example output: Los Angeles: 3/19/2019, 7:06:26 AM
You could also use Moment.js and Moment Timezone to convert date and time to a time zone other than your local time zone. For example:
const dt = moment();
const kolkata = dt.tz('Asia/Kolkata').format();
const la = dt.tz('America/Los_Angeles').format();
console.log(kolkata);
// example output: 2019-03-19T19:37:11+05:30
console.log(la);
// example output: 2019-03-19T07:07:11-07:00
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.23/moment-timezone-with-data.min.js"></script>
Well, you really do kind of have to convert anytime you want to change the display, but it's not as bad as you think.
First, store all time as UTC. Probably using the milliseconds format, e.g. Date.UTC().
Second, do all manipulation / comparison using that stored info.
Third, if your cloud-based application has an API that API should only talk in terms of UTC as well, though you could provide the ISO string if you prefer that to the MS, or if you expect clients to handle that better.
Fourth and finally, only in the UI should you do the final conversion to the local date/time string, either with the method you're describing or using a library such as momentjs
new Date creates a Date object with a time value that is UTC. If you can guarantee support for the timeZone option of toLocaleString (e.g. corporate environment with a controlled SOE), you can use it to construct a timestamp in any time zone and any format, but it can be a bit tedious. Support on the general web may be lacking. A library would be preferred in that case if you need it to work reliably.
E.g. to get values for California, you can use toLocaleString and "America/Los_Angeles" for the timeZone option:
var d = new Date();
// Use the default implementation format
console.log(d.toLocaleString(undefined, {timeZone:'America/Los_Angeles'}));
// Customised format
var weekday = d.toLocaleString(undefined, {weekday:'long', timeZone:'America/Los_Angeles'});
var day = d.toLocaleString(undefined, {day:'numeric', timeZone:'America/Los_Angeles'});
var month = d.toLocaleString(undefined, {month:'long', timeZone:'America/Los_Angeles'});
var year = d.toLocaleString(undefined, {year:'numeric', timeZone:'America/Los_Angeles'});
var hour = d.toLocaleString(undefined, {hour:'numeric',hour12: false, timeZone:'America/Los_Angeles'});
var minute = d.toLocaleString(undefined, {minute:'2-digit', timeZone:'America/Los_Angeles'});
var ap = hour > 11? 'pm' : 'am';
hour = ('0' + (hour % 12 || 12)).slice(-2);
console.log(`The time in Los Angeles is ${hour}:${minute} ${ap} on ${weekday}, ${day} ${month}, ${year}`);
Getting the timezone name is a little more difficult, it's difficult to get it without other information.

Persisting DateTimeOffset to server

I'm trying my best to figure out how to persist a datetimeoffset string to the server so that it correctly binds to a DateTimeOffset property.
I've tried using something like this:
moment.utc().format() + ' +' + new Date().getTimezoneOffset() / 60 + ':00';
This was showing as 6/12/2017 22:27 +4:00 and I was sending that to the server as a string but it seems to fail being parsed to a DateTimeOffset object every time.
I'm using Nancy for my web api framework at the moment, but I wonder the same thing for Web API as well.
Currently I'm just sending back the offset and setting the DateTimeOffset property like this:
dto.CommentDate = new DateTimeOffset(DateTime.UtcNow).ToOffset(new TimeSpan(dto.Offset, 0, 0));
Is there a better way to do this? Can I send the whole date instead of just the offset?
A few things:
+04:00 is an offset used in parts of Russia, and a few places in the Middle-East. From the location mentioned on your blog, I think you meant -04:00, which is used for Eastern Daylight Time in the US (among others). (The JavaScript getTimezoneOffset method is inverted with this regard.)
+ ':00' assumes that all time zone offsets are in terms of whole hours. In reality, many are :30 and a few are :45.
In a DateTimeOffset, the DateTime portion is always the local time, not the UTC time. In other words, the offset is already applied.
For example, if the local time is 2017-06-16 12:00:00 in the US Eastern time zone, then we'd represent that as 2017-06-16T12:00:00-04:00 (in ISO 8601 format). The corresponding UTC time would be represented as 2017-06-16T16:00:00Z.
Sending the UTC time plus the the local offset is not allowed in ISO 8601 format. The only format I'm aware of that uses that convention is the older "ASP.NET JSON Date Format", that came out of things like JavaScriptSerializer class in .NET. The corresponding value to the previous example would be /Date(1497628800000-0400)/. As you can see, the timestamp portion is a millisecond-resolution version of Unix Time, which is UTC based. The offset has not been applied, and is thus extraneous. This format considered deprecated now, so please don't do this.
You appear to be using moment.js on the client side. This can easily generate the string you need to send.
// get the current time, as an ISO 8601 string, with whole-second resolution
moment().format() // "2017-06-16T12:00:00-04:00"
// or, with millisecond resolution
moment().format('YYYY-MM-DD[T]HH:mm:ss.SSSZ') // "2017-06-16T12:00:00.000-04:00"
On the server side, just expose your property as a DateTimeOffset. Don't try to construct it yourself.

Converting moment UTC to Local date time

I put a UTC .NET/JSON date from .net on client side. When I run the following command:
moment(value.Planet.when).utc()
The returned date from webservice:
"/Date(1469271646000)/"
I get a date in the _d parameter showing the current accurate UTC date with GMT+0300 on right side.
I want to convert this time to local time on the user machine and what ever I do, I always get the time 3 hours back.
I do this:
moment(value.Planet.when).local().format('YYYY-MM-DD HH:mm:ss')
and I get the same date time as the UTC. I don't understand how can I get momentjs to show the UTC time relative to the local time. I checked that the momentjs object is indeed UTC.
I thought that if I pass the moment.utc() function the UTC date that I've got from the webservice (originally from the database), I can just run the local() function and I'll get the accurate hour relative to my area, but it didn't work.
You can use moment(date).format('YYYY-MM-DDTHH:mm:ss');
Eg:- if you date "/Date(1469271646000)/"
ip-> moment(1469271646000).format('YYYY-MM-DDTHH:mm:ss');
op-> "2016-07-23T16:30:46"
Do not use the _d property. It is for internal use only. See this answer, the user guide, or Maggie's blog post on the subject.
As far as you question of how to convert to local time, you don't actually need to convert at all. You're already parsing the input value in local mode, so you can just use it directly:
var m = moment("/Date(1469271646000)/"); // gives you a moment object in local mode.
var s = m.format(); // lets you format it as a string. Pass parameters if you like.
var d = m.toDate(); // gives you a Date object if you really need one
Try to avoid using Date objects unless they're required by some other controls or libraries you're using. Most operations can be done strictly on moment objects.

Javascript - displaying locale time from mySQL TimeDate

I am having an issue with displaying the correct time. I have a php script that when a button is clicked it inserts the CURRENT_TIMESTAMP into the database. The server is located in Arizona, I am in PST. When I call the time in my script it shows Arizona time, but I need it to show the users time. So 2015-02-18 16:06:28 Arizona time, MY time is 2015-02-18 15:06:28.
How do i get the correct time. I am using moment.js, but no matter how i format it it shows the incorrect time. I am not sure but is DST, not being considered?
var time_in = time_in;//format 2015-02-18 16:17:33
var timeIn = moment.utc(time_in, "HH:mm a").format("HH:mm a");
Moment.js parses the date as a locale date-time. So when you do moment.utc(time_in), you're converting it to UTC according to your local time (PST), shifted forward or backwards.
So what you need to do is do a moment.fn.utcOffset. Arizona is UTC-07:00, so we would want to add +7 to the offset. You can do the same using moment.fn.zone but that's getting deprecated.
var utcTime = moment.utc('2015-02-18 16:06:28').utcOffset(+7).format('YYYY-MM-DD HH:mm:ss')
// returns "2015-02-18 23:06:28" which is the UTC time
Now you have the moment in UTC, you can convert it to the client localtime:
moment(moment.utc(utcTime).toDate()).format('YYYY-MM-DD HH:mm:ss')
// returns '2015-02-18 15:06:28' (which is PST)
moment.utc(utcTime).toDate() above just converts the utc time to your local time, then formatting it with momentjs
EDIT: If possible, you should use unix timestamp when sending to server, then you don't have to deal with UTC or timezones. You can convert to local time with moment.unix(unixTimestamp).format('YYYY-MM-DD HH:mm:ss')
It looks like you are using Javascript to get the time of the client, but then not passing that to the PHP. I'm not sure how your app is structured, but you could create an input tag with the type="hidden". Then using Javascript, find the element and set it's value to Date().
Here is an example: http://jsfiddle.net/43jfefuq/
Now when you submit this form with PHP, the value in the field will be the client's local time.

CouchDB filtering UTC dates by month and year

I save a UTC timestamp for every document in one of my Couch databases.
I want to query and filter those documents based on a specific month of a year. For this purpose I created a view with the following map function:
function(doc) {
var date = new Date(doc.activity.date);
emit([doc.user, date.getUTCMonth(), date.getUTCFullYear()], doc.activity.distance);
}
I query this view with for example: ?key=["1edd367d08770ea34586dbe6dc03ea2c",3,2013], which works perfectly fine as long as the UTC and local time is in the same month.
What I am asking is how can I ensure that a query like this returns all documents of a month, where the month is defined by the clients local time and not UTC?
The client is a JavaScript based Web-Client, but the query is handled through a NodeJS API in the back end.
What I am asking is how can I ensure that a query like this returns all documents of a month, where the month is defined by the clients local time and not UTC?
Find the start of the client's month and the start of the client's next month. These will form the range to query. For example:
var now = new Date();
var start = new Date(now.getFullYear(), now.getMonth(), 1);
var end = new Date(now.getFullYear(), now.getMonth() + 1, 1);
Then you will need to decide the best way to get this as UTC to pass to the database. For example:
var startUTC = start.toISOString();
var endUTC = end.toISOString();
(I am not sure of the specifics for CouchDB, so adjust if necessary.)
Then you need to do a range query in your database. You can't just match on year and month because your data is in UTC and the edges will not necessarily line up with your client's time zone.
I'm not familiar with Couch's syntax, but in SQL it would be something like this:
... WHERE activityDate >= startUTC AND activityDate < endUTC
Since we passed the start of the next month as the end date, make sure that perform an exclusive < and not an inclusive <= operation. Only the start date should be inclusive.
What I am asking is how can I ensure that a query like this returns
all documents of a month, where the month is defined by the clients
local time and not UTC?
If the client sends his local time, he must do so with a reference as to which timezone he is located in.
Otherwise, he must use UTC or GMT.
Without including the timezone, you would basically have to detect what timezone the client is located in and make assumptions which would not work out (e.g. a laptop located on a italian IP may be set with american time).
See: http://en.wikipedia.org/wiki/ISO_8601#Time_zone_designators
The ISO 8601 format has time zone designator that would allow clients to send their localtime but with the attached time offset.
Couchdb supports this ISO 8601 format:
http://comments.gmane.org/gmane.comp.db.couchdb.user/14485
http://wiki.apache.org/couchdb/IsoFormattedDateAsDocId .
Remaining would be for you to do your conversions/comparisons etc to UTC/Unix time/ etc... whatever format works for you in order to be able to compare the KEY with the DATA.
The main point is that your comparing datetimes that are converted to the same timeoffset or UTC (no time offset).

Categories