I have a search model that sends a date range to a stored procedure. The stored procedure returns back results. This works fine where I am in Central Time.
However we have users in India and they could input valid dates of lets say '08/15/2017 23:00' and in the table for them the date is '08/14/2017' so they don't get any data returned.
If they input 08/14/2017 they get the wrong data. Data from 08/13/2017
How can I address the issue of time zone in c# before sending it to the stored procedure?
I have tried this with no luck
public DateTime AdjustForTimezone(DateTime date)
{
TimeZoneInfo timeInfo = TimeZoneInfo.FindSystemTimeZoneById(TimeZone.CurrentTimeZone.StandardName);
var newDate = TimeZoneInfo.ConvertTimeToUtc(date);
return newDate;
}
SqlCommand cmd = new SqlCommand("getTableData", connection);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
SqlCommandBuilder.DeriveParameters(cmd);
cmd.Parameters[1].Value = AdjustForTimezone(dateModel.FromDate);
cmd.Parameters[2].Value = AdjustForTimezone(dateModel.toDate);
Client to Server
The clients should send date time instances to the server with a UTC value or with the current offset from UTC.
The database should persist the DateTime (or DateTimeOffset) as DateTime/2 (or use DateTimeOffset).
Server to Client
The server should send the date time information to the client with the UTC value (or offset from UTC). It is the client's responsibility to apply the client's desired offset when the date time is presented (ie. do it in the presentation layer as late as possible).
Serialization
If you have to serialize your date times use ISO8601 notation.
By applying the above rules you now have stored your date time values in a way that they are comparable and unambiguous. They can now be filtered, sorted, etc.
If you are filtering be sure to take the date time filter parameters from the client and apply the same rules. Searching in eastern USA time zone for records that fall on the date 2017-02-06 should be translated into records with a date range between 2017-02-06T05:00:00 and 2017-02-07T05:00:00 as that time zone is UTC−05:00 at that point in time.
Side Notes
That there are exceptions to the rule like storing a birth date.
Personally I prefer storing DateTime/2 as UTC over DateTimeOffset both in code and in the data store.
Related
I'm running a LINQ-to-entities query on a table that contains a nullable DateTime with this Model:
public Nullable<DateTime> TheCloseDate { get; set; }
I has a variable DateTime and I want to filter one record with the correct a Datetime.
For example:
var datefilter = = new DateTime(2022, 8, 18, 16, 32, 10, DateTimeKind.Local);
var xx= dt.Students
.where( x => x.TheCloseDate.Hasvalue && x.TheCloseDate==datefilter)
.FirstOrDefault()
DateTime comparisons in database systems can be tricky. Coupling that to comparing to values provided by an application server ultimately linked to a client that could be in a different time zone than either is doubly and triply so.
The database can be storing date and time values relative to a given time zone or UTC depending on the DB engine and how they are configured. Then you have an application server that is running .Net localized to a given region. Something as simple as having a time zone mismatch between the application server and database server will cause problems where you query data showing there is data for 2022/08/18 16:32:10 but passing that results in a local time for a different time offset. Also consider that date times in the DB vs. .Net can have different resolutions in terms of going down to the ms or 100ns, etc.
The general recommendations when working with date + time values:
If possible, standardize on storing UTC times. Convert local times in the application server to UTC. Convert UTC back to local time for the applicable client location. For web applications you might have a Web App Servers hosted across data centres across US-East and US-West, but have users accessing the application for data stored across any range of time zones. If the time zone is relevant then it should be recorded with the DateTime using DateTimeOffset rather than relying on Local Kind. Local Kind will base the time offset on the application server's time zone. If you don't care whether the time reflects a client's time zone and you just want to ensure that times are standardized, standardize on UTC.
Consider always using ranges where you want to compare times.
For example, to find records down to the second based on a client's local time:
var clientTimezone = "Mountain Standard Time"; // Example, would come from configuration for that client.
var timeZone = TimeZoneInfo.FindSystemTimeZoneById(clientTimezone);
DateTime startUtcDateTime = TimeZoneInfo.ConvertTimeToUtc(new DateTime(2022, 8, 18, 16, 32, 10, timeZone);
datetime endUtcDateTime = startUtcDateTime.AddSeconds(1);
var students = dt.Students
.Where( x => x.TheCloseDateUTC >= startUtcDateTime && x.TheCloseDateUTC < endUtcDateTime)
.ToList();
Where a database is set to store UTC then it is advisable to suffix the DateTime field with "UTC". If storing local then suffix with "Local" so it is clear to developers what to expect to read or write, especially if the DB is not capable of storing offset information with the value. (Column set to datetime rather than datetimeoffset)
This would find all records where the close date might have fallen within that exact second. If you want the First or Last match (two users submitting rows at the same time could result in multiple records in the same second) then add an OrderBy clause on The CloseDate and take the FirstOrDefault;
var student = dt.Students
.Where( x => x.TheCloseDateUTC >= startUtcDateTime && x.TheCloseDateUTC < endUtcDateTime)
.OrderBy(x => x.TheCloseDateUTC) //To get first...
// or .OrderByDescending(x => x.TheCloseDateUTC) // To get last....
.FirstOrDefault();
If you want to store local times in the database then you should be working with DateTimeOffset rather than relying solely on DateTime and DateTimeKind, then consider what that means when you want to find a record for a particular client time (2022/08/19T16:32:10) When that might actually be (2022/08/19T16:32:10.000000-7:00) [MST-7:00] vs. what that means if someone in [EST-5:00] wants to query that time? Should that row be returned if they enter 2022/08/19T16:32:10 or 2022/08/19T18:32:10?
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)
Suppose the server is located in USA (MST) and an user from Chennai (IST) is making a payment. Both the server and user are in different timezones. The MST (Mountain Standard Time) is 7 hours behind GMT (Greenwich Meridian Time) and the IST (Indian Standard Time) is 5.30 hours ahead of GMT, that is both the server and user have a time difference of 12:30 hours. If the user is making the payment on 9/20/2011 9:00 AM then the time at the server is 8/20/2011 8:30 PM. So which time you have to record in the database? If the application stores the user’s time in the database then if someone from US is seeing the transaction will likely see a wrong time i.e. 8/20/2011 8:30 PM. The other option is storing the server’s time in the database (that what we normally do), then the user will be unhappy seeing a wrong time.
Can you please help me. Much appriciated.
I always use the UTC time to record everything, in any computer/server around the word.
DateTime.UtcNow
Then when I show the date and time to some user, I just change it base on their local time zone.
On saving any dates convert it into UTC and save into database, fore eg;
var now = DateTime.Now;
var Date = now.ToUniversalTime();
OR
var Date = DateTime.UtcNow;
and on displaying dates get the current Timezone of user and use it to convert the UTC date ,
var _Timezone = // store timezone id here
var southPole = TimeZoneInfo.FindSystemTimeZoneById(_Timezone);
DateTime userTime = TimeZoneInfo.ConvertTimeFromUtc(Date , southPole);
You can store the Timezone of different users in a database or get the current Timezone of the user logged in;
var _Timezone = new Date().getTimezoneOffset();
This method is not applicable in server side,
NOTE
You can get list of timezones from this code,
var timeZones = System.TimeZoneInfo.GetSystemTimeZones();
foreach ( var timeZone in timeZones )
{
Console.WriteLine( "{0} - {1}", timeZone.Id, timeZone.DisplayName );
}
I have a C# MVC Web application hosted in a remote server. I don't know the exact location of that.
The users of that application are all from (UTC + 06.00) Dhaka. When a user inserts a new record, I want the inserted datetime from his local time e.i (UTC + 06.00) Dhaka.
How can I do it?
The following code solves my problem-
DateTime utcTime = DateTime.UtcNow;
TimeZoneInfo BdZone = TimeZoneInfo.FindSystemTimeZoneById("Bangladesh Standard Time");
DateTime localDateTime = TimeZoneInfo.ConvertTimeFromUtc(utcTime, BdZone);
You can follow below approach:
Step 1:
You can store UTC time in database. If you are using SQL then you can use GETUTCDATE() to get UTC date.
Step 2
Please use Javascript to set a cookie, storing the browser timezone. (You can use scripts like jsTimeZoneDetect to get timezone name)
Step 3
Backend C# code:
Pull timezone from cookie.
Get the inserted utcTime from database and store in local variable(utcTime is the local variable name i used).
Use below mentioned code to convert UTC time to local time.
TimeZoneInfo tzoneinfo = TimeZoneInfo.FindSystemTimeZoneById("browser timezone name");
DateTime localTime = TimeZoneInfo.ConvertTimeFromUtc(utcTime, tzoneinfo);
Finally localtime is the end result. :)
Hope this will help you
Thank you
You should use global time zone of your ASP.NET application. Check this. This will work unless you use only C# datatime function. Once you have some complex code in SQL Server which will use GETTIME function. You will also rely on SQL Server timezone.
You should handle the internal datetime in UTC and convert the timezone only in UI.
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.