I am facing an issue in timezone. Right now I am saving time zone from client side and stored all DateTime in UTC. It's working properly but when I am trying to convert DateTime for UTC behind time zones like CST, EST, EDT it showing wrong data.
Issue -
Let's assume if I did any task at 10 PM EDT and it would be saved in DB as 2 AM(as per UTC) but when I am trying to fetch data for a day and passing current UTC date.
My question is If am trying to fetch data for a day like 11 midnight( from EST) to the current time, but my conversion from UTC to EST is wrong due to UTC 12midnight is yesterday's 8 PM(as EDT 4hr behind from UTC). (From Date[UTC convert to EDT] - 06/07/2017 08:00pm) and To Date - 06/07/2017 11:00 pm) Due to this conversion I am getting data from 8 pm to 11 pm only <- I am expecting from date is 06/07/2017 04:00 AM as per UTC.
Code -
Below is the code for conversion. In from date I have taken utcnow.date only and from a date
Javascript code -
function setTimezoneCookie() {
try {
var timezone_cookie = "timezoneoffset";
var timeZoneName = "timezonename"
var tz = jstz.determine();
var aa = tz.name();
// if the timezone cookie not exists create one.
if (!$.cookie(timezone_cookie)) {
// create a new cookie
$.cookie(timezone_cookie, new Date().getTimezoneOffset());
$.cookie(timeZoneName, aa);
}
else {
var storedOffset = parseInt($.cookie(timezone_cookie));
var currentOffset = new Date().getTimezoneOffset();
if (storedOffset !== currentOffset) {
$.cookie(timezone_cookie, new Date().getTimezoneOffset());
$.cookie(timeZoneName, aa);
location.reload();
}
else {
$.cookie(timeZoneName, aa);
}
}
}
c# code -
fromDate =Convert.ToDateTime(fromDate).ToClientTimeZoneinDateTime().ToString();
toDate = Convert.ToDateTime(toDate).ToClientTimeZoneinDateTime().ToString();
ObjectParameter totalRecords = new ObjectParameter("TotalRecords", typeof(int));
var DetailsList = objDetailsList.GetDetails(loginUserId,locationId, userId, taskType, pageIndex, numberOfRows, sortColumnName, sortOrderBy, textSearch, totalRecords, fromDate, toDate);
if (DetailsList.Count() > 0)
{
string output = BuildJQGridResults(DetailsList, numberOfRows, pageIndex, Convert.ToInt32(totalRecords.Value));
response.Write(output);
}
else
{
JQGridResults result = new JQGridResults();
List<JQGridRow> rows = new List<JQGridRow>();
result.rows = rows.ToArray();
result.page = 0;
result.total = 0;
result.records = 0;
response.Write(new JavaScriptSerializer().Serialize(result));
}
Below is the method of converting UTC time to client timezone
public static DateTime ToClientTimeZoneinDateTime(this DateTime dt)
{
try {
if (System.Web.HttpContext.Current.Request.Cookies["timezoneoffset"] != null || System.Web.HttpContext.Current.Request.Cookies["timezonename"] != null)
{
var timezonename = System.Web.HttpContext.Current.Request.Cookies["timezonename"].Value;
timezonename = timezonename.Replace("%2F", "/");
var timezoneLocal1 = FindTimezoneName(timezonename);
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById(timezoneLocal1);
bool isCurrentlyDaylightSavings = tzi.IsDaylightSavingTime(dt);
if (isCurrentlyDaylightSavings == true)
dt.AddHours(1);
var timeOffSet = System.Web.HttpContext.Current.Request.Cookies["timezoneoffset"].Value;
var offset = int.Parse(timeOffSet.ToString());
dt = dt.AddMinutes(-1 * offset);
return dt;
}
return dt.ToLocalTime();
}
catch (Exception)
{
return DateTime.UtcNow;
}
}
No doubt as Timezone handled properly but facing an issue for behind timezone from UTC if end user trying to fetch data after 8 PM EDT. I have attached screenshot as well.
Below img of before conversion -
Above img of after conversion -
How do I need to handle this situation?
The main problem is that you're converting the wrong direction. You are converting from UTC to the user's time zone, but your input is in the user's time zone, so you need to convert the other direction - from the user's time zone to UTC. Then your query will show better results.
A few other things:
Don't convert time zones by trying to add/subtract minutes or hours manually. Use the conversion functions offered on TimeZoneInfo, such as ConvertTimeFromUtc, ConvertTimeToUtc, etc. There's no need to test for DST.
The try/catch shouldn't be in your code at all. Throw an exception if you can't perform the operation. Don't mask important errors by swallowing exceptions.
dt.ToLocalTime() shouldn't be in your code either. Never rely on the server's local time zone.
The offset returned by new Date().getTimezoneOffset() is the user's current offset. You cannot assume that it's the correct offset for the dates chosen. You don't that anyway, as you're already getting the time zone name. (You don't need the timezoneoffset cookie at all.)
The time zone name returned by jstz.determine() on the client-side is going to be an IANA tzdb identifier, such as America/Los_Angeles. These aren't going to work on the server-side with TimeZoneInfo.FindSystemTimeZoneById (unless you are running .NET Core on Linux or Mac). Conversion to a Windows time zone is required. I see you have a FindTimeZoneName function, which I assume is performing the conversion. You didn't show the details in your code, but I highly recommend you use my TimeZoneConverter library to implement that, as it's maintained with changes to time zones.
Reading cookies and time zone conversion are separate concerns. Don't bundle them together.
Ultimately, you should have something like this:
public static DateTime FromTimeZoneToUtc(this DateTime dt, string timeZone)
{
var windowsId = TimeZoneConverter.TZConvert.IanaToWindows(timeZone);
var tzi = TimeZoneInfo.FindSystemTimeZoneById(windowsId);
return TimeZoneInfo.ConvertTimeFromUtc(dt, tzi);
}
Or, even better, if you use Noda Time, then you don't need to convert time zones at all.
public static DateTime FromTimeZoneToUtc(this DateTime dt, string timeZone)
{
var tz = DateTimeZoneProviders.Tzdb[timeZone];
var local = LocalDateTime.FromDateTime(dt);
return local.InZoneLeniently(tz).ToDateTimeUtc();
}
Related
I have the requirement to set particular time of the day to Date Object. The Time is in String and is CET, so "16:00" means "15:00" in UTC in Winter time. The following code does the job in node.js on my local machine which is in CET Timezone:
addTimetoDate(new Date(),"16:00");
function addTimetoDate(theDate,theTime){
var dtDate = new Date(theDate)
try{
var strTime = theTime.replace(/ /g,'');
var hourArray = strTime.split(":");
dtDate.setHours(parseInt(hourArray[0]), parseInt(hourArray[1]), 0)
if (dtDate == "Invalid Date"){
dtDate = theDate;
}
} catch (e){
dtDate = theDate;
}
return dtDate
}
However when deployed to remote server it produces Date Object which is offset by one hour the other direction when displayed with toLocaleString it shows "17:00".
How to do it elegant way (as simple deduction of one hour will work only in Winter Time.
---EDIT---
To clarify the question is - Is there anything I can do prior to using .setHours to make it right. Or I should not use setHours but rather manipulate the string for Date Constructor, so 16.00 CET gets properly converted to UTC representation?
toLocaleString will convert the given time into the timezone defined by the locale,... and that's fine if the time is UTC to start with. But if it's already been offset, then it's going to offset it again, which is what you're seeing.
Time is a fiddly creature; when I'm working with time I always store it (eg in a database) as UTC and let the client descide how it gets displayed. That way I can guarantee no server-side silliness.
Ok Found the solution:
function addTimetoDate(theDate,theTime){
var d = new Date();
var hourLocal = d.toLocaleString({timezone:'Europe/Berlin'}).split(" ")[1].split(":")[0]
var hourISO = d.toISOString().split("T")[1].split(":")[0]
var diff = parseInt(hourLocal) - parseInt(hourISO);
var dtDate = new Date(theDate)
try{
var strTime = theTime.replace(/ /g,'');
var hourArray = strTime.split(":");
dtDate.setHours(parseInt(hourArray[0])-diff, parseInt(hourArray[1]), 0)
if (dtDate == "Invalid Date"){
dtDate = theDate;
}
} catch (e){
dtDate = theDate;
}
return dtDate
}
Diff in whatever situation - being on the server, or on the browser captures current status (including daylight saving change) of hourly difference for the given Timezone. Then you can adjust your given hour to UTC to use setHours.
I am trying to convert a UTC date to local time on my node server and finally return the localized time in the format of hh:mm:ss (not using Moment JS). I'm passing in the timezone offset from the client to Node, which is GMT-6.
My original time is: 2017-05-05T00:25:11.378Z
// ISOTimeString = `2017-05-05T00:25:11.378Z`
// offsetInMinutes = 360; (GMT - 6)
function isoDateToLocalDate(ISOTimeString, offsetInMinutes) {
var newTime = new Date(ISOTimeString);
return new Date(newTime.getTime() - (offsetInMinutes * 60000));
}
The localized time is 2017-05-04T18:25:11.378Z, which is correct (2017-05-05T00:25:11 - 6 hours = 2017-05-04T18:25:11).
// localIsoDate: 2017-05-04T18:25:11.378Z Date object
function formatTime(localIsoDate) {
var hh = localIsoDate.getHours();
var mm = localIsoDate.getMinutes();
var ss = localIsoDate.getSeconds();
return [hh, mm, ss].join(':');
}
// formatted: 12:25:11
The problem is, while still on the server, when I try to format into hh:mm:ss, it subtracts another 6 hours, giving me 12:25:11. I don't want to convert again, I simply want to format and display 18:25:11 from the already localized time.
How can I do this?
Note: Keep in mind I do not have the option to convert timezones after it's passed back to the client in my case.
The isoDateToLocalDate seems to be OK, however in the formatTime you need to use UTC methods, otherwise you are getting the host local values, not the adjusted UTC values.
Also, in ISO 8601 terms (and general convention outside computer programming), an offset of 360 represents a timezone of +0600, not -0600. See note below.
// ISOTimeString = 2017-05-05T00:25:11.378Z
// ECMAScript offsetInMinutes = 360; (GMT-0600)
function isoDateToLocalDate(ISOTimeString, offsetInMinutes) {
var newTime = new Date(ISOTimeString);
return new Date(newTime.getTime() - (offsetInMinutes * 60000));
}
// localIsoDate: 2017-05-04T18:25:11.378Z Date object
function formatTime(localIsoDate) {
function z(n){return (n<10?'0':'')+n}
var hh = localIsoDate.getUTCHours();
var mm = localIsoDate.getUTCMinutes();
var ss = localIsoDate.getUTCSeconds();
return z(hh)+':'+z(mm)+':'+z(ss);
}
var timeString = '2017-05-05T00:25:11.378Z';
var offset = 360;
console.log(formatTime(isoDateToLocalDate(timeString, offset)))
ECMAScript timezone signs are the reverse of the usual convention. If the client timezone offset is +0600 then their host will show -360.
It is possible to disable the daylight timezone conversion in moment.js?
http://plnkr.co/edit/MjFelt?p=preview
$scope.obj.date = moment('2016-06-03T04:00:00.000Z');
Basically my application deals with events and dates only, but moment.js converting the daylight savings time is causing issue with dates. Does it have any setting which will disable it across the entire application usage?
If you are saying that you want moment to display your date and time (which is UTC, as indicated by the 'Z'), exactly as is, you should use moment.utc:
moment.utc('2016-06-03T04:00:00.000Z').format()
"2016-06-03T04:00:00Z"
When you use the default moment constructor, as you are now, you are telling moment to convert your UTC time to local time, and this is why you are seeing a time difference. For instance, on my local machine (I am currently UTC-5) I get the following:
moment('2016-06-03T04:00:00.000Z').format()
"2016-06-02T23:00:00-05:00"
This question comes up quite a lot, so I wrote this blog post that explains moment's constructor functions and how it converts ISO8601 dates in detail: https://maggiepint.com/2016/05/14/moment-js-shows-the-wrong-date/
In my case, I had a problem with timezone changing due to 'daylight saving time'.
I had the next period:
{
"from": "2020-10-01 00:00:00 +0200",
"to":"2020-11-01 00:00:00 +0100",
}
And I wanted to get:
{
"from": "2020-10-01 00:00:00 +0200",
"to":"2020-11-01 00:00:00 +0200",
}
My solution is to get current (local) timezone and set it to the both date moment:
const currentTzOffset = moment().utcOffset(); // getting current timezone offset
const startToLocalZone = moment(from, yourDateFormat)
.local() // just checking. not sure if this is necessary
.utcOffset(currentTzOffset) // put your tz to here
.format(yourDateFormat);
const endToLocalZone = moment(to, yourDateFormat)
.local() // just checking. not sure if this is necessary
.utcOffset(currentTzOffset) // put your tz to here
.format(yourDateFormat);
console.log(startToLocalZone); // output: "2020-10-01 00:00:00 +0200"
console.log(endToLocalZone); // output: "2020-11-01 00:00:00 +0200"
And dont forget to set your date format instead 'yourDateFormat'
var tz = 'America/Vancouver'; // or whatever your time zone is
var dt = '2022-03-13T07:00:00.101Z'; // or whatever date/time you're working with
var momentVal = moment.tz(dt,tz)
function isNextDayDST(mObj){
return mObj.clone().add(1, 'days').isDST();
}
function isTodayDST(mObj) {
return mObj.clone().isDST();
}
function getDSTHourCompensation(mObj) {
const todayDST = isTodayDST(mObj.clone());
const tomorrowDST = isNextDayDST(mObj.clone());
if(todayDST == false && tomorrowDST == true) {
return 1
}
if(todayDST == true && tomorrowDST == false) {
return -1
}
return 0
}
function removeDST(mObj){
const hourCompentation = getDSTHourCompensation(mObj);
return mObj.clone().add(hourCompentation, 'hours');
}
console.log(momentVal.format('YYYY-MM-DD HH:mm'))
console.log(removeDST(momentVal).format('YYYY-MM-DD HH:mm'))
Maybe use the moment lib to compensate for the hours when you want to remove DST for the particular case.
I'm doing something that should be really simple: I'm getting a string that represents an expiration date and using JavaScript to determine whether or not the expiration date has come to pass. My approach has been as follows:
var dateStringFromJson = "2015-09-11T11:21:48.113";
var expirationDate = new Date(Date.parse(dateStringFromJson));
if (expirationDate > new Date()) {
// Expiration not up
}
If I executed this function at a time before the expiration, say 10:50am, the comparison would fail and the function would act as if the expiration date was up.
I'm confident this problem has to do with JavaScript's timezone conversion. I'm in UTC-7 but my customers may be in any time zone across the U.S., so timezone specific fixes will not work here. I'd also prefer not to add an external library like moment.js to the project unless absolutely necessary.
Use a function to localize the json string. When you parse a date string without a time zone, it assumes it is in UTC. See my answer to a similar question for an explanation of how the localizeDateStr() function works.
function localizeDateStr(date_to_convert_str) {
var date_to_convert = new Date(date_to_convert_str);
var local_date = new Date();
date_to_convert.setHours(date_to_convert.getHours() + (local_date.getTimezoneOffset() / 60));
return date_to_convert;
}
function checkExpired() {
var dateString = document.getElementById('date').value;
var expirationDate = localizeDateStr(dateString);
if (expirationDate > new Date()) {
alert("Expiration not up.");
} else {
alert("Expired!");
}
}
Expiration Datetime:
<input id="date" type="text" value="2015-09-11T11:21:48.113" />
<button id="btn" onclick="checkExpired()">Is expired?</button>
You can parse the date manually if its format is consistent:
var DATE_STRING = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d{3})$/;
var match = DATE_STRING.exec(dateStringFromJson);
var expirationDate = new Date();
expirationDate.setFullYear(+match[1]);
expirationDate.setMonth(+match[2] - 1);
expirationDate.setDate(+match[3]);
expirationDate.setHours(+match[4]);
expirationDate.setMinutes(+match[5]);
expirationDate.setSeconds(+match[6]);
expirationDate.setMilliseconds(+match[7]);
Consider just putting the timestamp in UTC, though, which is how it’s parsed. (And add a Z to the end to indicate that.)
I'm using ASP.NET MVC, and I wish to store all my DateTime values on the server in UTC format. And I wish for all transmission of DateTime values to be in UTC format. But I want to display DateTimes in the browser in local time. I've been getting confused and having trouble making it work. Below is my process....
In my UI, the user is able to enter a date which I compose into a string and use to create a Date object. It ends up looking like this:
var dt = new Date("3/23/2012 8:00 AM");
So the user intends to create a Date for 8 AM their time. Now I wish to send this to the server in UTC format so I have this method:
Date.prototype.toUTC = function ()
{
var self = this;
return new Date(self.getUTCFullYear(), self.getUTCMonth(), self.getUTCDate(), self.getUTCHours(), self.getUTCMinutes());
};
Which I use like so:
data.startDt = dt.toUTC(); //Data is the object being set to the server
Then I make an Ajax call using jQuery to send the data object to the server. On the server when I debug, and examine the data that comes in I see StartDt (which is mapped to a .NET DateTime object) as being {3/23/2012 12:00:00 PM}.
This is the value I store in my database. I'm not totally certain it is correct though.
Both the client and server are located in the Eastern United States (UTC-05:00).
Now, when I send this date back to the client in JSON format .NET sends this:
"/Date(1332518400000)/"
In JavaScript I parse it this way:
var dt = new Date(parseInt(serverDt.substr(6))); //parseInt ingnores last /
My thinking is that dt is a UTC Date, but that I can display it in in local format by calling toShortTime() as shown below:
Date.prototype.get12Hour = function ()
{
var h = this.getHours();
if (h > 12) { h -= 12; }
if (h == 0) { h = 12; }
return h;
};
Date.prototype.getAMPM = function ()
{
return (this.getHours() < 12) ? "AM" : "PM";
};
Date.prototype.toShortTime = function ()
{
return this.get12Hour() + ":" + this.getMinutes() + " " + this.getAMPM();
};
But that doesn't give me the 8:00 AM back that I want. It gives me the 12:00 PM. Where am I going wrong?
In your code, dt is a UTC time. You need to convert that from UTC to local time.
See Javascript: Convert a UTC Date() object to the local timezone
Are you constructing the .NET DateTime object with the appropriate DateTimeKind value? You're sending a UTC-relative value to the server, which I'm guessing is storing the value as an EDT-relative time instead of a UTC-relative time, hence the incorrect value. As you stated, 1332518400000 is 12PM EDT, not UTC, which points to a transcription problem on the server:
> new Date(1332518400000)
Fri Mar 23 2012 12:00:00 GMT-0400 (Eastern Daylight Time)
This function works beautifully for me.
function ParseDateForSave(dateValue) {
// create a new date object
var newDate = new Date(parseInt(dateValue.substr(6)));
// return the UTC version of the date
return newDate.toISOString();
}