Which is the best way to display the "sent time" of message to user?
Is saving with "server side time() function" better or saving with "client side getTime()" and updating that value with ajax is better?
if server and client are in different timezones, either set the timezone to one of them to be same as the other or use a timezone difference (e.g +2 hours) and adjust all dates accrordingly to one of them (by adding/subtracting the timezone diff)
alternatively the client can always display dates as sent by the server and you are done
e.g if you have the server timezone difference just display it in the client as such (and you dont need anything else, if that is good for your use case)
2016-01-03 17:12:00 (+1 GMT)
For your example:
add the timezone diff (+ 1 hour), in the client (or the server, but not both)
e.g in the server (php DateTime)
$server_date = date('Y-m-d H:i:s');
$client_date = DateTime::createFromFormat( 'Y-m-d H:i:s', $server_date );
$client_date->add( new DateInterval('PT'.(60*60).'S') ) // + 1 hour
$client_date = $client_date->format('Y-m-d H:i:s');
or in the client (js use a library like moment.js or DateX)
var client_date = new Date(timestamp(server_date, 'Y-m-d H:i:s')+60*60*1000); // + 1 hour;
// timestamp is supposed to parse the date and return a unix timestamp in milliseconds
// for example if you use DateX, you can do:
// client_date = new DateX(DateX.fromString(server_date,'Y-m-d H:i:s').getTime()+60*60*1000).format('Y-m-d H:i:s');
A third option is for the server to always sent UTC (GMT) dates and let the client convert them to local/timezoned dates (sth similar to the above, only the client uses its own timezone offset instead of being hardcoded, e.g +1 Hour)
The server is the only machine you have control over, so it is the only machine you can rely on. But beware of daylight savings time.
I'd recommend storing all dates on the server in UTC, and then converting to local time in the browser. Doing this will also help you account for daylight savings time changes, which occur at different times around the world.
To illustrate this, if you were doing this server side, then the following C# code converts UTC to the local time, accounting for daylight savings times:
DateTime utc = DateTime.UtcNow;
TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
DateTime localDateTime = TimeZoneInfo.ConvertTimeFromUtc(utc, zone);
I know this is a PHP question, but I hope this code helps.
Related
I need to make illusion of working in the selected by the user timezone. The problem that server and client code are stick to javascript date. So to achieve the requirement I have made mannualy mapping from utc to the date on the client side:
dateToServer(date) {
const momentDate = moment(date);
let serverDate = null;
if (momentDate.isValid() && date) {
const browserUtcOffset = momentDate.utcOffset();
serverDate = momentDate
.utc()
.subtract(this.clientTimeZoneOffset, 'minutes')
.add(browserUtcOffset, 'minutes')
.toDate();
}
return serverDate;
}
dateToClient(date) {
const momentDate = moment(date);
let uiDate = null;
if (momentDate.isValid() && date) {
const browserUtcOffset = momentDate.utcOffset();
uiDate = momentDate
.utc()
.subtract(browserUtcOffset, 'minutes')
.add(this.clientTimeZoneOffset, 'minutes')
.toDate();
}
return uiDate;
}
I'm adding/subtracting the browserUtcOffset, because it is adding/subtracting automatically by browser when the date is go between server and client.
It was working well, but this solution is missing handling of the DST. I'd like to check is DST active for the date and then add DST offset if need.
Here C# code, that can do this:
string timeZone = "Central Standard Time";
TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
DateTime date = new DateTime(2011, 3, 14);
Console.WriteLine(timeZoneInfo.BaseUtcOffset.TotalMinutes); // -360
Console.WriteLine(timeZoneInfo.GetUtcOffset(date).TotalMinutes); // -300
Console.WriteLine(timeZoneInfo.IsDaylightSavingTime(date)); // true
Console.WriteLine(timeZoneInfo.DaylightName);
Console.WriteLine(timeZoneInfo.SupportsDaylightSavingTime);
I have found the isDST in the momentjs, and when I have my windows local timezone to CST and check moment([2011, 2, 14]).isDST(); in browser console I see the true. How you can see the isDST is depends on the browser local time.
Next step try to use moment-timezone to do smth like I have done in C#. Unfortunately I don't understand how to achieve this. The first problem that as start point I have: UTC time, Base offset(-360 in C# sample), timezone name: Central Standard Time, but timezones in the moment-timezone are different.
moment.tz.names().map(name => moment.tz.zone(name)).filter(zone => zone.abbrs.find(abbr => abbr === 'CST') != null) this code returns 68 timezones.
Why there each of them have many abbrs? What mean untils? All I want to check if UTC time in the selected timezone, which is "Central Standard Time", the daylight saving day active. See C# sample one more time :)
string timeZone = "Central Standard Time";
TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
DateTime date = new DateTime(2011, 3, 14);
Console.WriteLine(timeZoneInfo.BaseUtcOffset.TotalMinutes); //-360
Console.WriteLine(timeZoneInfo.GetUtcOffset(date).TotalMinutes); //-300
Console.WriteLine(timeZoneInfo.IsDaylightSavingTime(date)); //true, I have checked is daylightsavingtime for the date
Console.WriteLine(timeZoneInfo.DaylightName);
Console.WriteLine(timeZoneInfo.SupportsDaylightSavingTime);
The application has been written on the angularjs 1.6.3.
A few things:
The Date object in JavaScript tracks a specific UTC-based point in time. You can see that timestamp with a call to .valueOf() or .getTime(). It is only certain functions and constructor parameters that work with the local time zone (such as .toString()). That local time zone is applied at the time the function is called. One cannot substitute a different time zone (except in the options object passed to toLocaleString). Thus, the Date object can not be converted from one time zone to another. Because your two functions both take in a Date object and return a Date object, all you are doing in the middle is picking a different point in time. This is also seen inside the function where you use the add and subtract methods of Moment. They manipulate the represented point in time - they do not change the time zone.
By passing this.clientTimeZoneOffset, You appear to have conflated a time zone with a time zone offset. These are separate concepts, as a time zone may go through multiple different offsets, due to DST but also due to changes in standard time, where they have occurred in history. See also "Time Zone != Offset" in the timezone tag wiki. It is not useful to pass just the client's offset, as that offset only applies to a single point in time. You cannot use it for time zone conversions, because it doesn't tell you anything about which offsets are used for other points in time.
Instead, pass a time zone identifier. On Windows in .NET, these look like "Central Standard Time" (representing both standard and daylight time despite the name), and in JavaScript and most other operating systems, IANA time zone names are used. They look like "America/Chicago". This is also covered in the timezone tag wiki.
If your .NET code is using Windows identifiers, you can use my TimeZoneConverter library to convert from Windows to IANA, then send that string to the browser as the time zone.
With Moment-Timezone, you can simply check the DST like so:
moment.tz([2011, 2, 14], 'America/Chicago').isDST()
Again, you should be using IANA time zone IDs, and TimeZoneConverter can provide them if you're using Windows time zones server-side.
You could consider using Noda Time on the server-side, with its TZDB time zone provider. That would allow you to use IANA time zones on both sides. This is also the case with TimeZoneInfo when run on .NET Core on Linux or Mac OSX.
The reason you see so many entries when you searched for abbreviations with CST is that time zone abbreviations are ambiguous. You may have meant US Central Standard Time, but you might also have meant Cuba Standard Time or China Standard Time, or a variety of other places that use that abbreviation.
Regarding the untils array, you generally don't need to concern yourself with that. It is part of the internal data that Moment-Timezone uses to pick the correct point in time for choosing a time zone offset and abbreviation.
You said at the end, something slightly different:
All I want to check if UTC time in the selected timezone, which is "Central Standard Time", the daylight saving day active.
The example I gave earlier assumed you were starting with that time zone's local time. If you're starting with the UTC time, then it's like this:
moment.utc([2011, 2, 14]).tz('America/Chicago').isDST()
Of course, you can pass various other supported inputs where I pass the array. The Moment docs can help you with available options.
Check please the answer of Matt Johnson for this question. It was very usefull for me.
Vocabulary
UI date - the date that user see on the html date input when
selecting the date. UI date is the native js date.
Server date - the UTC date that is stored in the
database.
timezone id - string, that identified the timezone. They are different for windows(.net), IANA(javascript) and rails conventions.
Problem
The main problem f.e. when a man logged in on the PC which is located in the timezone +3, but setting for his account is -6. The angularjs and kendo is using on the project and the kendo time is working only with native js date. And native js date is always in the browser timezone, for this example +3. But I should setup the stuff like it in -6. F.e. example user has selected time 07:00, the UTC will be 04:00 (07:00 - 3), but as for his account the timezone is -6, the utc should be 13:00 (07:00 + 6). And this conversations are applied automatically when we converting native js date(UI date) to UTC and backward. So decision to count the offset on the server and get rid off browser timezone offset: utcTime = UItime + browserOffset - clientTimezoneBaseOffset - daylightSavingTimeOffset.
However there is problem when need to get UI time back, f.e. today 06.06.2014 and the DST is true for this date, but when we are getting 03.03.2014 date from server we don't know if the DST was active for 03.03.2014.
Answer
In project on the server the .net, so in database the window's timezone IDs are stored. I have done it the next way: count on server DST for date ranges in the range from minimum date on the server and save on the client in the local storage. I like that in this case
the server is one source of truth, so the calculations can be performed on any client without manipulating the timezones. And no need to convert between IANA, Windows and rails timezones. But also there is a problem: need to precalculate DST in range from DateTime.MinValue to DateTime.MaxValue, currently to speed up I'm calculating DST for range from 01.01.2000 to DateTime.Now - it is enough when converting value from database to UI, because in database the min date is on the 2008 year, but is not enough for html input, because user can select value greater than DateTime.Now and lower then 01.01.2000. To fix it I'm, planning with use of TimeZoneConverter send to client the IANATimezoneId, and for cases when provided date(UI or server) is not in the range [01.01.2000, DateTime.Now] belong on the moment.utc(date).tz(IANATimezoneId).isDST().
This is new code on the server side
private class DaylightSavingTimeDescriptor
{
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public bool IsDaylightSavingTime { get; set; }
}
private string GetDaylightSavingTimeDescriptorsJson(string timeZone)
{
string daylightSaveingTimeDescriptorsJson = String.Empty;
if(timeZone != null)
{
List<DaylightSavingTimeDescriptor> dstList = new List<DaylightSavingTimeDescriptor>();
TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
DateTime startDate = new DateTime(2000, 1, 1);
DateTime dateIterator = startDate;
bool isDST = timeZoneInfo.IsDaylightSavingTime(startDate);
while (dateIterator < DateTime.Now)
{
bool currDST = timeZoneInfo.IsDaylightSavingTime(dateIterator);
if (isDST != currDST)
{
dstList.Add(new DaylightSavingTimeDescriptor()
{
EndTime = dateIterator.AddDays(-1),
IsDaylightSavingTime = isDST,
StartTime = startDate
});
startDate = dateIterator;
isDST = currDST;
}
dateIterator = dateIterator.AddDays(1);
}
daylightSaveingTimeDescriptorsJson = Newtonsoft.Json.JsonConvert.SerializeObject(dstList);
}
return daylightSaveingTimeDescriptorsJson;
}
And this is modified dateToServer, dateToClient on client side
export default class DateService{
constructor (authService) {
const authData = authService.getAuthData();
this.clientTimeZoneOffset = Number(authData.timeZoneOffset);
this.daylightSavingTimeRanges = authData.daylightSavingTimeRanges ? JSON.parse(authData.daylightSavingTimeRanges) : [];
}
getDaylightSavingTimeMinutesOffset(utcDate) {
const dstRange = this.daylightSavingTimeRanges.find(range => {
const momentStart = moment(range.startTime).utc();
const momentEnd = moment(range.endTime).utc();
const momentDate = moment(utcDate).utc();
return momentStart.isBefore(momentDate) && momentEnd.isAfter(momentDate);
});
const isDaylightSavingTime = dstRange ? dstRange.isDaylightSavingTime : false;
return isDaylightSavingTime ? '60' : 0;
}
dateToClient(date) {
const momentDate = moment(date);
let uiDate = null;
if (momentDate.isValid() && date) {
const browserUtcOffset = momentDate.utcOffset();
uiDate = momentDate
.utc()
.subtract(browserUtcOffset, 'minutes')
.add(this.clientTimeZoneOffset, 'minutes')
.add(this.getDaylightSavingTimeMinutesOffset(momentDate.utc()), 'minutes')
.toDate();
}
return uiDate;
}
dateToServer(date) {
const momentDate = moment(date);
let serverDate = null;
if (momentDate.isValid() && date) {
const browserUtcOffset = momentDate.utcOffset();
serverDate = momentDate
.utc()
.subtract(this.clientTimeZoneOffset, 'minutes')
.add(browserUtcOffset, 'minutes')
.subtract(this.getDaylightSavingTimeMinutesOffset(momentDate.utc()), 'minutes')
.toDate();
}
return serverDate;
}
}
PS
I want to get rid off offsets and use moment-timezone on the client and timezone converter on the server, but it will be later if customer ask, because I have never tried it before and I'm not sure that it will be working well, and current solution is working. Also the offsets anyway will be used for dateinputs components (angularjs), because they are using kendo-dateinput which ng-model is JS Date in browser local time, but I'm providing another timezone so need to transformations inside component.
PPS Best solution from my point of view, if timezone converter and moment-timezone will be working as expected
Anyway the whole app is operating the JS Date, so when user in Moscow with America in profile selects the datetime in kendo-input, or see setup kendo-scheduler, or display the date in the kendo-table, I need to manipulate with offsets. But instead of passing directly the client offset, I'm planning to pass IANA timezone id from the server with help of timezone converter and get the offset I need directly from moment-timezone.
I am sending Json data via a REST Service to my client. This client should use the data to Display it.
My client uses JavaScript.
I am converting the date in the following way:
var from = new Date(myJsonDate.match(/\d+/)[0] * 1);
The JSON looks like this:
...="From":"\/Date(1450134000000)\/" ...
My problem is that the dates are correct in Germany but are off by one day in Brazil (e.g. showing Sunday instead of Monday in Brazil).
Does this code use time zones and calculates this accordingly?
How could I turn this off?
I want that the date is displayed exactly how i have sent it.
The operations with dates in JavaScript have a time zone variation in which the client machine is configured.
Right opportunity had to fix a function that showed difference between dates and nobody knew because. When you instance a date, the return her appears as: “Thu Feb 14 2008 08:41:27 GMT-0300 (Official Hour of Brazil)”
Note that in date has the GMT (Greenwich Mean Time) that indicates in which time zone the date is configured.
I’ll show as avoid the difference of time caused by this in operations with date. To this we have create a function that convert the date always to the time zone that if wait.
var calculateTimeZone = function(date, offset) {
var miliseconds_with_utc = date.getTime() + (date.getTimezoneOffset() * 60000);
return new Date(miliseconds_with_utc + (3600000 * offset));
}
Note that in the line 3, we invoke the method getTime() that convert the local moment of date to a number represented by miliseconds since January 1st, 1970 (Unix Epoch). We get the current time zone that is set in browser by method geTimezoneOffset() of API the date in JavaScript and we multiply by miliseconds of time of a hour. We add then the two values.
Why a hour?
Why this is the time that represents each time zone. By default this method return this time zone in minutes, by this the convertion in hour is necessary.
For to arrive this number 60000 you have that remember that 1 second have 1000 miliseconds and which 1 minute have 60 seconds, then converting minutes for miliseconds we multiply 60*1000 = 60000.
This moment we have the UTC (Coordinated Universal Time) represented by variable “utc” by sum of local moment the time zone in miliseconds.
We need now get a date starting this UTC added with the time zone of destiny, how by example a date expressed in time zone +5 transforming in time zone of brazil (Hour of Brazilian).
Note that in line 5 we got an offset (Time Zone Representation) in hour and converting to miliseconds. Remember that here 1 second have 1000 miliseconds and which 1 hour have 3600 seconds, then convert hour in miliseconds should multiply 1000 * 3600 = 3600000.
We add this result with the value of variable “utc” and we got the moment to the time zone wanted. Thenceforth we create a new date with based in long appropriate and return this new date.
In this way we can maintain of integrity desired in application when we need expressed a date in right time zone.
Does this code use time zones and calculates this accordingly?
No. Passing a number to the Date constructor is interpreted as a time value, i.e. milliseconds since 1970-01-01T00:00:00Z. Regardless of the settings of the client, it will create a Date for exactly the same instant in time.
However, by default, Date.prototype.toString uses the host system settings to apply an offset to the displayed values as "local" time.
How could I turn this off?
Modify the script engine. It's part of the ECMAScript standard so any implementation that doesn't do it is non–compliant.
I want that the date is displayed exactly how i have sent it.
Either:
Send it as a plain string, not as a date
Also send the time zone offset of the source so you can apply it at the other end to keep the date the same.
ECMAScript offsets have an opposite sense to most standards, they're -ve for east and +ve for west, so to get a Date with local settings that has the same as the source system:
var d = new Date(timevalue);
d.setMinutes(d.getMinutes() + d.getTimezoneOffset() - sourceTimezoneOffset);
Where sourceTimezoneOffset is the offset of the source system in minutes, +ve for west and -ve for east.
Usually dates related to a specific time zone, so as pointed out, the date in one place might be different to the date in another place at the same instant in time.
If you are not doing any modifications in dates when sending it from server side, the date will be in the timezone where the server is hosted.
So, if your server is hosted in Germany, dates will be in Germany's timezone.
There would be 2 ways to solve this:
Send dates to client in user-timezone from server in the response.
Make adjustments in your client application to implement appropriate
date conversion.
I am making a site, and where the users will be uploading questions and answers in it. Now I need to get the user's timezone which i can store it in variable $TimeZoneNameTo instead of just "Asia/Kathmandu". is there anyway, where the time zone as a string will be detected and stroed in variable $TimeZoneNAmeTo, so for any user of any timezone, the datetime will be converter to his/her timezone instead of UTC while displaying.
$TimeZoneNameFrom="UTC";
$TimeZoneNameTo="Asia/Kathmandu";
echo "uploaded on".$upload_date;
echo"<br>";
echo date_create($upload_date, new DateTimeZone($TimeZoneNameFrom))
->setTimezone(new DateTimeZone($TimeZoneNameTo))->format("Y-m-d H:i:s");
If all you want to do is present times based on the user's system settings, then just send UTC time values to the client. The value should be milliseconds since 1970-01-01T00:00:00Z. UNIX uses seconds since the same epoch, so you just need a UNIX UTC timestamp multiplied by 1000:
var newLocalDate = new Date(UNIXutcTimeValue * 1000);
Now just present it in a user readable form, say using Date.prototype.toLocaleString:
alert(newLocalDate.toLocaleString());
e.g. 2014-06-06T12:00:00Z is 1402056000 seconds or 1402056000000 milliseconds, so in Safari:
alert(new Date(1402056000000).toLocaleString()) // 6 June 2014 20:00:00 AWST
Of course you can always use Date methods to format the string anyway you want.
Answering the question you ask :
On modern browsers you can fetch the timezone client side using
var tz = Intl.DateTimeFormat().resolved.timeZone;
If your browser doesn't have Intl (IE10- and Safari), then it's a little more complicated. I made a small library for that using moment.js : https://github.com/Canop/tzdetect.js
Answering the problem you have :
Most of the times you don't have to know the client's timezone and you can simply let his browser format a UTC timestamp you send. See RobG's answer.
Sorry if the title is a little convoluted. I'm bashing my head against the floor with times in NodeJS / Javascript. I can get the current UTC time like this:
var currentTime = Date.now();
I can get the current time for a user who is, for example, in the -3 timezone like this:
var offsetTime = Date.now() + (numTimeZone * 3600000);
But how do I get the local user time at, say, 6am, converted to UTC?
Practical application:
What I'm trying to do is create an auto-emailer which sends an email to a user at 6am in their local time. My server is in one timezone and they will be in another, so I'm trying to standardise it against UTC so every minute I can set my server to check the currentUTC time, then check what the user's 6am time is converted to UTC (local6am), and if the currentUTC > local6am then an email should be sent.
What's the best way to achieve this? Preferably without using a library if possible.
Utc to Local
moment.utc('2014-02-19 05:24:32 AM').toDate();
Local to utc
Read this documentation.
MomentJS is parsing the date as a locale date-time. If no hour is given, it is assuming midnight.
Then, you convert it to UTC, so it is shifted, according to your local time, forward or backwards. If your are in UTC+N, then you will get the previous date.
moment(new Date('02-19-2014')).utc().format("YYYY-MM-DD HH:mm").toString()
moment(new Date('02-19-2014 12:00')).utc().format("YYYY-MM-DD HH:mm").toString()
(or)
You can try this:
moment.utc('07-18-2013', 'MM-DD-YYYY')
moment.utc('07-18-2013', 'MM-DD-YYYY').format('YYYY-MM-DD')
You do not need to call toString explicitly.
I have a javascript application which receives data from ASP.NET WebService in JSON format. My application has a lot of manipulations with dates which it also receives from WebService.
WebService stores all dates in EST timezone and sends them in such format:
{"Date":"\/Date(1319205600000+0300)\/"}
to my javascript application.
On the client side I should display all dates also in EST timezone irrespectively of browser's timezone. So if I receive from the server the representation of:
10/21/2011 10:00
I should display exactly the same time to the user.
So to convert dates I do something like this:
function convert_date(millisec) {
var date = new Date(millisec),
local_offset = date.getTimezoneOffset(),
server_offset = -5 * 60, //EST offset
diff = (local_offset + server_offset) * 60000;
return new Date(millisec + diff);
}
But the point is that server_offset not always should be -5. It can be -4 depending on DST value. I've tried to do server_offset = (date.isDST() ? -4 : -5) * 60 instead but I haven't found any solution for capturing isDST() which works fine for all the local client timezones. Most of them work fine for that local browser's timezone which has the same value of DST as EST timezone but would fail in the case of GMT+5:30 timezone for example.
So are there any way to determine whether DST would be applied for some specific date in EST timezone from javascript?
Or maybe I've missed something?
If you can modify the web service, I would have it also return a flag indicating whether or not it was daylight saving time in the server's timezone (EST).
Assuming you can't do this, you can determine whether it is daylight saving time according to this information for DST in the United States:
DST starts on the second Sunday of March and it ends on the first Sunday of November.
The caveat being that this could change in the future (I wasn't even aware it had changed in 2007).
I am not really sure how exactly you are getting the ticks value you pass to the function, which I think you have something weird going on there.
But if the question is how to write the isDST() function, then that should be pretty simple.
Date.prototype.isDST = function()
{
var dt = new Date(this.getYear(), 1, 1);
return dt.getTimezoneOffset() > this.getTimezoneOffset();
}
edit: This isn't polished/tested for negative offsets.