Date randomly changed in different time zone in asp.net - c#

I am facing a weird scenario that the date of a textbox (which is fetched by sql database) is changed randomly by date (-1)day
My WebApp is deployed in USA on two servers *(the both servers have time difference on 1 hour due to UTC-5, UTC-6) and i am in Timezone UTC+5.
Inside SQL Table there is a date '05/31/2020' but sometimes it shows '05/30/2020' on front end.
The column type in SQL is Date. Then i get it DataSet, and on code behind this line is used Convert.ToDateTime(DataSet.Tables[0].Rows[0]["Date"])).ToShortDateString()
When i debug this code locally the date is correct every time.

I suspect that somewhere you have an issue with local and UTC time.
With you on UTC+5 and your servers on UTC-5 and UTC-6 its possible for them to be a day behind you e.g. your local time is before 10am Friday they will still be on Thursday.
How does your back end handle the date? It should always be in UTC, you then load your SQL DATE value into a DateTime type and use DateTime.ToLocalTime to convert to your UI and DateTime.ToUniversalTime when passing from UI to back end SQL.

Related

Problems with postgresql, c# and windows server 2019

I have a very strange problem, I have an application that connects to a local server with postgresql, the application is programmed with visual studio 2019, c # and npgsql driver, what the application does is ask for the time zone of a column timestamp from the database. So far so good, it gives me time zone values ​​-3 and -4 which is what I expect. Then I take the same application with the same framework, version of the postgresql server and driver, but when I run this on the server that has windows server 2019 it always returns the timezone with value 8 no matter what I do, the time zone of my computer and windows server 2019 are the same and both are located in the same country, the operating system of both is updated to the latest version.
This is the code for the main method of the class.
static void Main(string[] args)
{
PgSqlConnection pgSqlConnection1 = new PgSqlConnection();
pgSqlConnection1.Host = "127.0.0.1";
pgSqlConnection1.Port = 5432;
pgSqlConnection1.UserId = "postgres";
pgSqlConnection1.Password = "230169";
pgSqlConnection1.Database = "biotime";
pgSqlConnection1.Open();
PgSqlCommand command = pgSqlConnection1.CreateCommand();
command.CommandText = "SELECT EXTRACT(TIMEZONE FROM now())/3600.0 as zona;";
PgSqlDataReader reader = command.ExecuteReader();
PgSqlDataAdapter adaptador = new PgSqlDataAdapter(command);
DataTable tabla = new DataTable();
adaptador.Fill(tabla);
Console.WriteLine(tabla.Rows[0][0].ToString());
Console.ReadKey();
}
I'm using DEVART connector form postgresql but same issues with NPGSQL
I think there's some baseline misunderstanding of PG time storage here. Timestamps in Postgres do not "store a timezone" so you can't get PG to "give you a stored timezone".
With respect to datatypes:
timestamp stores whatever time you give it, or whatever time it is in whatever timezone the db is set to if you use now() as the value. If you give it a time of '15:00' it stores 15:00. If you give it a time with an offset such as 15:00-0500, it discards the offset and stores the 15:00. If it's 3pm in Chicago right now and you told the db it lives in Chicago, and you insert now() then 15:00 is stored.
When you ask for it back the time you get is the time as stored, even if you changed the timezone of the db since
the column is a timestamp
the db is in -8 (in a location that doesn't observe DST, to make things simple) and you store the time 15:00 (either by inserting a value '15:00' or by using the now() function when it's exactly 3pm in a -8 zone)
querying the db reports the time as 15:00
you change the db to a -5 (non DST) zone
querying the table still gives the time as 15:00
timestamptz takes whatever timestamp with offset you give it and converts it to UTC. If you get the time from now() then the time is the current time in the declared zone, converted to its UTC equivalent. The UTC time is stored. The offset you provided (or the offset of the timezone active at the point you used the now() function) is NOT stored. When you ask for it back the db converts the stored time according to the currently configured zone and presents it as the time in that zone
the column is a timestamptz
the db is in -8 (non DST) and you store the time 15:00 (either by inserting a value '15:00' or by inserting a value '15:00-0800' or by inserting a value '16:00-0700' or by inserting a value '17:00-0600' or by ... or by using the now() function when it's exactly 3pm in a -8 zone)
querying the table gives the time as '15:00-0800' (even if you inserted '16:00-0700' it is stored as '23:00+0000' and converted to '15:00-0800' upon query because the db is in -8
you change the db to a -5 (non DST) zone
querying the table now gives the time as '18:00-0500'; same moment in time, same stored value of '23:00+0000', different presentation
So, EXTRACT TIMEZONE FROM somestoredtimestamp is really just a synonym for "what is the applicable offset of this stored moment, in the timezone you're set to?", it's not extracting a timezone from anything - it's giving an offset from UTC for that time in the current known timezone. If the db is set to Chicago (observes DST) then you'll get -5 or -6 out of it depending on what time of year the date is. If the db was set to a location that didn't observe DST then you'd get a consistent offset out of it regardless the date. Offsets are not time zones so "extract timezone" is a misnomer.
The time zone PG uses can be set in a variety of places - the PostgreSQL config file, the TZ environment variable, the connection string, per session by SET TIMEZONE TO ..., or it might even use the windows control panel region settings if it wasn't set anywhere else
You're getting +8 from your EXTRACT TIMEZONE command because PG believes it lives in a zone that is +8 and has no DST (like Singapore)

DateTime.Now vs DateTime.UtcNow on Azure server hosting my site and db

I've been struggling with utc and local time for a while. I have an app that creates events (classes) and when the host creates an event (class), they set the event date and time. So, on the server side I save both the local date time for the event and the UTC date time for the event.
But now is where I keep confusing myself.
Example: If a user visits the hosts page to see all the hosts events (classes), how would I look up the host's events if I'm using a date time in the query and the user visiting the page is a different time zone from the host.
Currently I'm doing something like this:
var events = host.Events.Where(j => j.EventDateTime >= DateTime.Now
&& j.EventStatus == EventStatus.Active).ToList()
But now I'm frustrated over the DateTime.Now, because it's the date time of the Azure server and this query compares the local event date time to a UTC date time.
So my question is,
Should I try to figure out what the UTC date time is and pass that in before adding the offset to the DateTime.Now?
Or should I run the query using the UTC date times? Which would be something like this:
var events = host.Events.Where(j => j.UtcEventDateTime >= DateTime.UtcNow
&& j.EventStatus == EventStatus.Active).ToList()
Note that the DateTime.Now on the server means the server now datetime not the user Now, which might get you confused especially on the cloud when the application is distributed on multiple services, or when you change the location of your service.
It is always recommended to save dates in UTC format and you can add to this the offset based on each user zone for display, this will make sure that your date data are consistent and easy to compare and filter.
Many people had same questions like yours such as this good one:
DateTime.Now vs. DateTime.UtcNow
Also you might find Datetimeoffset a different way to save your date, just make sure you understand it very well before adding it in action:
DateTime vs DateTimeOffset

Saving user selected date as utc and query against db with utc fields

I am a little bit confused what is the correct approach.
My app will be a global app meaning places (owners of events) with different timezones will be interacting with it and I want to keep things simple.
My app allows places(owner of events) to create events on a calendar (by obviously picking datetime) and then save to db. (StartDateTime, EndDateTime)
Then users (event attendees) will view events happening near them.
What I have done so far is create 2 fields called StartDateTimeUTC, EndDateTimeUTC in my table and I have for each user allowed them to have a default timezoneid set.
When creating new events, i read their timezone, convert the datetime they have selected to UTC
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"); //gotten from db
DateTime startDateTimeUserSelected = new DateTime(2016, 4, 14, 2, 15, 0); //user picked from a datetimepicker
DateTime endDateTimeUserSelected = new DateTime(2016, 4, 14, 4, 15, 0); //user picked from a datetimepicker
var starDateTimeUTC = TimeZoneInfo.ConvertTimeToUtc(startDateTimeUserSelected, tz);
var endDateTimeUTC = TimeZoneInfo.ConvertTimeToUtc(endDateTimeUserSelected, tz);
Then I save those to to my db fields, great now i got my user selected date as utc and saved to db.
Now I want to query the db for all events that happen today but I need to get the LOCAL TIME for the current user (based on their timezone)
var currentTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tz); //tz being read from db for particular user
And then I can use the currentTime to find and query my db for all events happening today.
Just of the app
Place creates events for their users (place can be anywhere in the world) using the backend, users using app on their mobile will view what events are happening at different places around them (same timezone)
Question
Is this the correct way to deal with timezones without having to worry about daylight savings? or do I have to do more for an app that shows events to user based on their local time, and keeping everything in sync.
or should i just store everything as none UTC and determine user's current datetime from browser for places (owner of events) to query against db, and edit/add events. And determine users (attendees)'s time from the mobile device.
If I go with UTC i have keep track of timezone both for the Places and attendees and make sure all comparisons are done via UTC.
If i just enter everything as NONE utc, i dont have to worry about any conversion other than the fact that I need to read users CURRENT DATETIME from either browser/mobile device.
Have i missed anything?
When I was working with Azure Table Storage (NOSQL) I encountered this same roadblock since ATS always store all date time fields as UTC.
On the database table I had 3 fields:
TerminalUtcTime DATETIME
TerminalTimezone CHAR(6)
TerminalLocalTime CHAR(12)
The goal is to NEVER compute the terminal's local time using UTC and timezone. Because the effects of daylight
savings time (summer time), etc. is not reflected on the timezone as
well as UTC time.
On my experience, we had to forward the terminal's local time as is to some bank web service which is very strict. We had to send data as accurate as possible.
On the client:
DateTime terminalUtcTime = DateTime.Now.ToUniversalTime();
string terminalTimeZone = // Get from TimeZoneInfo.Local and format to ±hh:mm like +08:45 or -10:00 (format varies on how you may want it to be)
// Getting the timezone can be tricky because the hh and mm separator can be changed in the control panel so I had to use substrings instead
DateTime terminalLocalTime = DateTime.Now.ToLocalTime();
On the server:
You just need to save the terminalUtcTime as TerminalUtcTime,
terminalTimezone as TerminalTimezone and the terminalLocalTime as string.
terminalLocalTime.ToString("yyyyMMddHHmmssfff"); // Saves up to the milliseconds
Now, if needed, you can retrieve the DateTime instance of the terminal's local time. No need to compute, etc. I use DateTime.ParseExact(var, "yyyyMMddHHmmssfff");
Yes, you have to add a new field on the table but it saves you all the worries. This is the approach on a database that only allows UTC time, but if you are using MySql, MSSQL, etc. that allows saving of non-UTC time then that may work for you as well and saves up all the trouble.

Format of DateTime that SQL Server uses

I am debugging a query that is getting built in C# maybe with EntityFrameWork - not sure - but it doesn't return any records although it should.
The query has some DateTime fields and they are like this:
personStartDate {4/3/2013 12:00:00 AM}
The value for Date is getting from user interface date picker but I guess it also defaults the time part to 12:00:00 AM somehow.
Then I go to SQL Server and view the table rows and values on their datatime field data looks like this example: '2013-04-23 09:20:38.897'
Do you see any obvious problem right there?
Also I am trying to take the generated SQL from my breakpoint in C# and post it to SQL Server to see what does it return so for a value like {4/3/2013 12:00:00 AM} I am replacing it with 2013-04-03 12:00:00.000 Is that even correct?
Formatting is irrelevant. Internally it won't be in a text format at all, and I'd hope that the query doesn't end up sending the query value to the database as text either.
If you're only interested in the date part, you need to say that in the query:
where foo.Start.Date == personStartDate
(for example - we don't know what your query looks like).
If your field in the database is logically just a Date but is currently a DateTime, you should consider changing your schema to match your logical data model. It'll make things simpler.
If you create a DateTime using DateTime.Today then the time part will default to midnight. That is what the date picker is doing.
Your database contains a time portion too. If that is incorrect you can convert it in sql: Best approach to remove time part of datetime in SQL Server
2013-04-03 12:00:00.000 is noon is 12:00:00 PM. You want 12:00:00 AM which is midnight. You should use 2013-04-03 00:00 in your test or 2013-04-03 or '3 April 2013'. Again, the time portion will default to 00:00.
To get the query to work in c# with the "bad" data in the database, make the query less precise by doing "less than tomorrow more than or equal to today" rather than "equals". Or make the database more precise by dropping the time part - then you can use "equals". If you are using Entity Framework and Linq-to-Entities you may need to use EF's DbFunctions
Ideally you should track down the inserts and updates that are setting the time in the database and stop that happening, then fix the existing data. Then you could change the data type in the database from DateTime to Date.
A Sql Server DateTime value is a pair of 32-bit integers. The first is the count of days from the SQL Server calendar's epoch (1 Jan 1900 00:00:00.000); the second is the count of milliseconds since start of day.
The string representation of that is dependent on (A) the default language setting for your SQL Server instance, (B) the current language setting for the session, (C) the current set dateformat setting, and probably a few other options I've forgotten.
If you care about the string representation, explicitly convert it to a string using convert(varchar(X),your-datetime-value-here,style) using the style of your choice.
Note that SQL Server Date and DateTime values are converted to/from System.DateTime values by the runtime.

Modifying dates in ASP.NET MVC C# based on stored time zone

My question is two-fold.
1) I am coding a forum and I'm having trouble figuring out how to store time zones for the users of the forum. They will be able to set their time zone and have all dates on the forum modified accordingly. Do I have to create a DB table with timezone names and the number to adjust the server time by? Does .NET have time zone support built in somewhere?
2) Once I've figured out how to store the user's time zone and then modify a DateTime object to the right time, I then need an easy way to pass this modified date to the view in MVC. For instance, I have the following code:
List<Topic> topics = board.Topics.OrderByDescending(x => x.Replies.Any()
? x.Replies.OrderBy(y => y.PostedDate).Last().PostedDate
: x.PostedDate).ToList();
This topics object is passed to the view as part of a view model object. The view loops through Model.Topics and displays the list of topics. The problem is that I don't want to do the time zone modifications in the view because I think that is too much responsibility for the view. Is there a way to modify the topic date within the LINQ query?
Thanks in advance!
You can get a list of timezones System.TimeZoneInfo.
var timeZones = System.TimeZoneInfo.GetSystemTimeZones();
foreach ( var timeZone in timeZones )
{
Console.WriteLine( "{0} - {1}", timeZone.Id, timeZone.DisplayName );
}
You can use that list to populate a dropdown list on the users profile page. The selected value should be stored with each user's profile data.
You can then use TimeZoneInfo.ConvertTime to convert any date time to the users time zone. Assuming you know which time zone it was created it.
var now = DateTime.Now;
Console.WriteLine( now );
Console.WriteLine( System.TimeZoneInfo.ConvertTime( now, TimeZoneInfo.Local, TimeZoneInfo.FindSystemTimeZoneById( "China Standard Time" ) ) );
As far as where to do this conversion goes you can do it in your controller rather then the view. Your best bet would be to create a view model on top of of Topic where you do the conversion.
Other wise create a helper function to do the conversion which is accessible from your views and use it appropriately. Personally I wouldn't be afraid of doing this in the view.
Be weary of attempting to do the conversion in the database, you'll severely limit the ability to perform object caching on the data returned from the database.
Also, Consider converting all dates to UTC time before inserting them into the database. This will make sorting correct (in regards to daylight savings) and also limit any issues that may arise if the hosting environment was moved time zones or hosted across time zones.
I understand this is an answered question. I am adding my few cents also for future reference. This is also another approach.
My Complete Solution as follows. All the time is stored as UTC in database. You can choose another timezone as default. But then calculation get complicated. And if you decided to move your host to another country, it would be issue.
Database
First, Need to store user selected timezone in some table that holds user information. Basically we are going to store TimeZoneInfo.Id (String value). This is easy to create TimeZoneInfo class again.
Next, Create a User defined function and set return as datetime. This will be used in whole DB where ever you need server time. Code as shown,
Create FUNCTION [dbo].[fnGetDateTime] ()
RETURNS datetime
AS
BEGIN
RETURN GETUTCDATE()
END
This will return UTC time. We can use GETUTCDATE() method straightaway everywhere in the db. But using a UDF gives flexibility on maintenance.
Code
we can use method extendability in Date-time Class. Since this solution is based on asp.net, it is tricky to calculate time. Problem is, there are 3 parties involved. DB Server, Web Server and User. Each can be in different time zone. But we need to solely relay on User Selected time zone and UTC time.
I have exetened DateTime Strucute with 2 addtional methods.
Namespace Extensions
Public Module ModDateTimeExtensions
<System.Runtime.CompilerServices.Extension()> _
Public Function GetUserTimeFromUTC(ByVal dtUtcTime As DateTime, ByVal id As String) As DateTime
Return TimeZoneInfo.ConvertTimeFromUtc(dtUtcTime, TimeZoneInfo.FindSystemTimeZoneById(id))
End Function
<System.Runtime.CompilerServices.Extension()> _
Public Function SetUserTimeToUTC(ByVal dtUserTime As DateTime, ByVal id As String) As DateTime
Return TimeZoneInfo.ConvertTime(dtUserTime, TimeZoneInfo.FindSystemTimeZoneById(id), TimeZoneInfo.Utc)
End Function
End Module
End Namespace
Here important thing you shouldn't relay on web-server time zone for calculation.
Then basically you can do the conversion as below. Assuming user time zone set to "Pacific Standard Time". This needs to be pulled from DB during user login.
Dim dt as DateTime = FunctionToGetUTCTimeFromDB()
dt = dt.GetUserTimeFromUTC("Pacific Standard Time")
If you want to save user time to UTC then call,
Dim dt as DateTime = GetUserSelectedTimeFromUI()
dt = dt.SetUserTimeToUTC("Pacific Standard Time")
This gives Following flexibility,
Less Coding and Less Change if it needs to be implemented in existing system.
Easy maintenance.
In future if you want to implement User selectable date time format, can follow same way with small change.

Categories