.NET Save DateTime and completely ignore timezone - c#

Maybe the answer is so obvious, I'm not seeing it, but I have a question that I'll risk asking anyway.
I want to allow users of a .NET Web application to enter a date/time, store it in an Oracle database, and no matter which timezone they are in, it always displays as the raw, original version as typed in by the user. So if one user in California enters 2PM and another in Maryland enters 2PM, they would both show as 2PM to a user in Japan. Two types of clients are possible, a web user and a windows client user (connected via web service).
Think of it like I want to completely break all timezone smarts that most applications worry about.
I don't want to save as a string though. I want a datetime that I can add/remove hours and minutes from.
Edit:
This was basically the exact problem I was having.

You should always store DateTime in UTC format (universal). When you display it you can choose which ever timezone you wish, in your case this can be fixed for all users, rather than based on location.
// when recording date time
DateTime utcDateTime = DateTime.UtcNow;
// parse DateTime back out from string
DateTime utcDateTime = DateTime.SpecifyKind(DateTime.Parse(dateStr),
DateTimeKind.Utc);
// localized DateTime
DateTime localDate = utcDateTime.ToLocalTime();
// fixed DateTime based on timezone
string timeZoneKey = "New Zealand Standard Time";
TimeZoneInfo timeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneKey);
DateTime nzlocalDate = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, timeZone);
This takes into account things like day-light savings which can trip you up if start saving localized dates.

If you just store the DateTime as entered/picked by the user then it will be stored as just that. There will be time zone informations stored in it, but you are in control of what you do with it.
For example, when you want to output it to the screen/file etc. you will need to format it to a string. If you use this ToString overload with CultureInfo.InvariantCulture then this should ignore the current culture and output the date as is:
DateTime date = DateTime.Now;
string output = date.ToString("d", CultureInfo.InvariantCulture);
Other operations will require different handling, but you will need to specify what happens in each case.

I had a time sensitive fix and the SpecifyKind did not allow me to make proper TimeSpan comparisons. In my situation, the quickest solution was to simply remove the Hours, Minutes, and Seconds from the time, as the DateTime I was comparing with was at midnight (00:00:00),
DateTime eventDateTime = (DateTime)row["event_date"];
eventDateTime = eventDateTime.AddHours(-1 * eventDateTime.Hour).AddMinutes(-1 * eventDateTime.Minute).AddSeconds(-1 * eventDateTime.Second);

If you really want discard time zone:
public static DateTime WithoutTimeZone(this DateTime dateTime)
{
if (dateTime.Kind != DateTimeKind.Unspecified)
return new DateTime(dateTime.Ticks, DateTimeKind.Unspecified);
return dateTime;
}

Related

Is there functionality to convert from UTC time to local time using country name? [duplicate]

We are developing a C# application for a web-service client. This will run on Windows XP PC's.
One of the fields returned by the web service is a DateTime field. The server returns a field in GMT format i.e. with a "Z" at the end.
However, we found that .NET seems to do some kind of implicit conversion and the time was always 12 hours out.
The following code sample resolves this to some extent in that the 12 hour difference has gone but it makes no allowance for NZ daylight saving.
CultureInfo ci = new CultureInfo("en-NZ");
string date = "Web service date".ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);
As per this date site:
UTC/GMT Offset
Standard time zone: UTC/GMT +12 hours
Daylight saving time: +1 hour
Current time zone offset: UTC/GMT +13 hours
How do we adjust for the extra hour? Can this be done programmatically or is this some kind of setting on the PC's?
For strings such as 2012-09-19 01:27:30.000, DateTime.Parse cannot tell what time zone the date and time are from.
DateTime has a Kind property, which can have one of three time zone options:
Unspecified
Local
Utc
NOTE If you are wishing to represent a date/time other than UTC or your local time zone, then you should use DateTimeOffset.
So for the code in your question:
DateTime convertedDate = DateTime.Parse(dateStr);
var kind = convertedDate.Kind; // will equal DateTimeKind.Unspecified
You say you know what kind it is, so tell it.
DateTime convertedDate = DateTime.SpecifyKind(
DateTime.Parse(dateStr),
DateTimeKind.Utc);
var kind = convertedDate.Kind; // will equal DateTimeKind.Utc
Now, once the system knows its in UTC time, you can just call ToLocalTime:
DateTime dt = convertedDate.ToLocalTime();
This will give you the result you require.
I'd look into using the System.TimeZoneInfo class if you are in .NET 3.5. See http://msdn.microsoft.com/en-us/library/system.timezoneinfo.aspx. This should take into account the daylight savings changes correctly.
// Coordinated Universal Time string from
// DateTime.Now.ToUniversalTime().ToString("u");
string date = "2009-02-25 16:13:00Z";
// Local .NET timeZone.
DateTime localDateTime = DateTime.Parse(date);
DateTime utcDateTime = localDateTime.ToUniversalTime();
// ID from:
// "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Time Zone"
// See http://msdn.microsoft.com/en-us/library/system.timezoneinfo.id.aspx
string nzTimeZoneKey = "New Zealand Standard Time";
TimeZoneInfo nzTimeZone = TimeZoneInfo.FindSystemTimeZoneById(nzTimeZoneKey);
DateTime nzDateTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, nzTimeZone);
TimeZone.CurrentTimeZone.ToLocalTime(date);
DateTime objects have the Kind of Unspecified by default, which for the purposes of ToLocalTime is assumed to be UTC.
To get the local time of an Unspecified DateTime object, you therefore just need to do this:
convertedDate.ToLocalTime();
The step of changing the Kind of the DateTime from Unspecified to UTC is unnecessary. Unspecified is assumed to be UTC for the purposes of ToLocalTime: http://msdn.microsoft.com/en-us/library/system.datetime.tolocaltime.aspx
I know this is an older question, but I ran into a similar situation, and I wanted to share what I had found for future searchers, possibly including myself :).
DateTime.Parse() can be tricky -- see here for example.
If the DateTime is coming from a Web service or some other source with a known format, you might want to consider something like
DateTime.ParseExact(dateString,
"MM/dd/yyyy HH:mm:ss",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal)
or, even better,
DateTime.TryParseExact(...)
The AssumeUniversal flag tells the parser that the date/time is already UTC; the combination of AssumeUniversal and AdjustToUniversal tells it not to convert the result to "local" time, which it will try to do by default. (I personally try to deal exclusively with UTC in the business / application / service layer(s) anyway. But bypassing the conversion to local time also speeds things up -- by 50% or more in my tests, see below.)
Here's what we were doing before:
DateTime.Parse(dateString, new CultureInfo("en-US"))
We had profiled the app and found that the DateTime.Parse represented a significant percentage of CPU usage. (Incidentally, the CultureInfo constructor was not a significant contributor to CPU usage.)
So I set up a console app to parse a date/time string 10000 times in a variety of ways. Bottom line:
Parse() 10 sec
ParseExact() (converting to local) 20-45 ms
ParseExact() (not converting to local) 10-15 ms
... and yes, the results for Parse() are in seconds, whereas the others are in milliseconds.
I'd just like to add a general note of caution.
If all you are doing is getting the current time from the computer's internal clock to put a date/time on the display or a report, then all is well. But if you are saving the date/time information for later reference or are computing date/times, beware!
Let's say you determine that a cruise ship arrived in Honolulu on 20 Dec 2007 at 15:00 UTC. And you want to know what local time that was.
1. There are probably at least three 'locals' involved. Local may mean Honolulu, or it may mean where your computer is located, or it may mean the location where your customer is located.
2. If you use the built-in functions to do the conversion, it will probably be wrong. This is because daylight savings time is (probably) currently in effect on your computer, but was NOT in effect in December. But Windows does not know this... all it has is one flag to determine if daylight savings time is currently in effect. And if it is currently in effect, then it will happily add an hour even to a date in December.
3. Daylight savings time is implemented differently (or not at all) in various political subdivisions. Don't think that just because your country changes on a specific date, that other countries will too.
#TimeZoneInfo.ConvertTimeFromUtc(timeUtc, TimeZoneInfo.Local)
Don't forget if you already have a DateTime object and are not sure if it's UTC or Local, it's easy enough to use the methods on the object directly:
DateTime convertedDate = DateTime.Parse(date);
DateTime localDate = convertedDate.ToLocalTime();
How do we adjust for the extra hour?
Unless specified .net will use the local pc settings. I'd have a read of: http://msdn.microsoft.com/en-us/library/system.globalization.daylighttime.aspx
By the looks the code might look something like:
DaylightTime daylight = TimeZone.CurrentTimeZone.GetDaylightChanges( year );
And as mentioned above double check what timezone setting your server is on. There are articles on the net for how to safely affect the changes in IIS.
In answer to Dana's suggestion:
The code sample now looks like:
string date = "Web service date"..ToString("R", ci);
DateTime convertedDate = DateTime.Parse(date);
DateTime dt = TimeZone.CurrentTimeZone.ToLocalTime(convertedDate);
The original date was 20/08/08; the kind was UTC.
Both "convertedDate" and "dt" are the same:
21/08/08 10:00:26; the kind was local
I had the problem with it being in a data set being pushed across the wire (webservice to client) that it would automatically change because the DataColumn's DateType field was set to local. Make sure you check what the DateType is if your pushing DataSets across.
If you don't want it to change, set it to Unspecified
I came across this question as I was having a problem with the UTC dates you get back through the twitter API (created_at field on a status); I need to convert them to DateTime. None of the answers/ code samples in the answers on this page were sufficient to stop me getting a "String was not recognized as a valid DateTime" error (but it's the closest I have got to finding the correct answer on SO)
Posting this link here in case this helps someone else - the answer I needed was found on this blog post: http://www.wduffy.co.uk/blog/parsing-dates-when-aspnets-datetimeparse-doesnt-work/ - basically use DateTime.ParseExact with a format string instead of DateTime.Parse
This code block uses universal time to convert current DateTime object then converts it back to local DateTime. Works perfect for me I hope it helps!
CreatedDate.ToUniversalTime().ToLocalTime();

Correct way to convert datetime timezone for display

We are developing a website and currently, the timezone of the website and database is in German time zone (European standard time zone). But the application is being accessed from the US also. There is a screen in the application which contains a DateTime field called ValidFrom and the time we are storing is UTC time. currently, users are not selecting the time so we are using .NET built-in DateTime.UTCNow to store DateTime value in the database. But the problem is while displaying, we need to display it according to User timezone. So after googling for many hours, we found two solutions one using moment and another approach is using DateTime.SpecifyKind. We tried using moment.js but it converted the date time to local time once again. So we ended up using DateTime.SpecifyKind as below.
[DataMember]
private DateTime _validFrom;
public DateTime ValidFrom
{
get { return _validFrom; }
set { _validFrom = DateTime.SpecifyKind(value, DateTimeKind.Utc); }
}
And now the values are displayed according to timezone. But my doubt is, is it the correct approach to handle timezone display or any other better solution exist?
I'd use DateTimeOffset instead, something like:
var utc = DateTimeOffset.UtcNow;
var tz = TimeZoneInfo.FindSystemTimeZoneById("Your Specific Time Zone Id");
var zonedDateTime = TimeZoneInfo.ConvertTime(utc, tz);
Save the UTC and user's time zone in the database, and convert UTC to specific time zone any time you want to show it to your users. I also suggest you take a look at NodaTime if you want to do anything serious with date and time. The built-in DateTime in .Net is misleading.
I wrote an extension method for this:
public static DateTime ConvertFromUTC(this DateTime date, TimeZoneInfo destZone)
{
var utcZone = TimeZoneInfo.FindSystemTimeZoneById("UTC");
return TimeZoneInfo.ConvertTime(date, utcZone, destZone);
}
However, if you plan to use something like this you need to be aware of daylight saving time. The result may be off if the conversion crosses a DST change in either timezone. So it isn't really suitable if you need absolute precision, which depends on the website, e.g.: is it a blog or a stock trading app?

Convert current local time to any other timezone

I want to test some time-zone related code by comparing local time to UTC. However this test relies on local time being different to UTC and I'm in the UK so 6 months of the year, local time is UTC according to DateTime comparison tests (due to summer time).
I could hard-code my test to convert UTCNow to a certain timezone like EST but on the off-chance my code was used in an American system, now I have the same issue.
So is there a way I can easily convert DateTime.UtcNow to a timezone that's definitely different to my local timezone, without hard-coding it or making assumptions what timezone I'm in?
Ok, as I mentoined in comments, if you want to get timezones which differs from yours, you can do it in that way:
var zone = TimeZoneInfo.GetSystemTimeZones()
.Where(x=>x.BaseUtcOffset != TimeZoneInfo.Local.BaseUtcOffset)
.First();
To convert UTC DateTime to another timezone, you have to use TimeZoneInfo.ConvertTimeFromUtc, sample:
var datetime = // datetime in UTC, for example, DateTime.UtcNow
var zone = // target zone
return TimeZoneInfo.ConvertTimeFromUtc(datetime, zone);
You can check samples in my pet project here
Time zones in .net are a very confusing matter. DateTime only supports local and UTC timezones really, it has no concept of different zones as it only gets the current offset from the machine and applies it to a tick count value which is in UTC.
So at a first there's no way to change to another timezone, but you can simulate something. If you want per example simulate a timezone GMT+2, you must first retrieve the current zone offset and add the difference of this offset and the desired offset to the local date, something like this:
TimeSpan targetOffset = new TimeSpan(2, 0, 0) - TimeZoneInfo.Local.BaseUtcOffset; //target to GMT+2
DateTime targetNow = DateTime.Now + targetOffset;
In this way the Date will have the values like if it were on that timezone, but for calculations using the datetime object and not just the year/month/day/hour/minute/second all of them will be wrong as the object will be marked as local and thus will have the wrong values.
To solve this you have two options, reverse your logic (convert non-local time to local time) or just work with UTC dates.
The first approach is very easy:
DateTime nonLocal = new DateTime(2016, 10, 21, 13, 33, 0); //We suppose we want 2016-10-21 13:33:00 at GMT+2
DateTime local = nonLocal + (TimeZoneInfo.Local.BaseUtcOffset - new TimeSpan(2, 0, 0));
This will yield to correct DateTime operations, but, here we go again with problems, if you use the year/month/day/hour/minute they will be in your local zone, not in the supposed zone and code using these properties will fail.
Finally, the best approach is the second one, you just forget about timezones and whenever you get a DateTime you convert it to UTC, all will work flawlesly and when you need to represent the data just convert it to local and all is done, no need to worry about DateTime arithmetic or properties differing just because they're on another timezone.
public static class DateTimeExtensions
{
public static DateTime As(this DateTime source, string timeZoneName)
{
DateTime utcTime = DateTime.SpecifyKind(source, DateTimeKind.Unspecified);
TimeZoneInfo newTimeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneName);
return TimeZoneInfo.ConvertTimeFromUtc(utcTime, newTimeZone);
}
}
Usage:
DateTime date1 = DateTime.UtcNow;
DateTime date2 = date1.As("Eastern Standard Time");

getting hour part and time-zone part from datetime

I had a string in config file, defining date time with time zone.
I am not able to get this value, while reading values from config file.
In config file:
Setting name="abcdefgh" value="2012-08-10T22:00:00-08:00"
In C#, I am reading this as follows:
DateTime StartDate;
StartDate = DateTime.ParseExact(RoleEnvironment.GetConfigurationSettingValue("abcdefgh"), "yyyy-MM-dd HH:mm:ss", null);
Configuration.Instance.abcdefgh= StartDate;
In start date, i am getting 11 Aug, 2012 11:30:00, with no time zone.
I want to read it as it is. also tell, if my format of writing datetime in config file is correct
MSDN link to DateTimeOffset.
Use DateTimeOffset whenever you are referring to an exact point in
time. For example, use it to calculate "now", transaction times, file
change times, logging event times, etc. If the time zone is not
known, use it with UTC. These uses are much more common than the
scenarios where DateTime is preferred, so this should be considered
the default.
var date = DateTimeOffset.Parse("2012-08-10T22:00:00-08:00");
date.Offset // -08:00:00, offset from Coordinated Universal Time (UTC)
date.DateTime // 10/08/2012 22:00:00,
DateTime doesn't keep information about timezone. To parse the string and keep information about timezone - you should use DateTimeOffset structure.
Use the DateTimeOffset structure (and DateTimeOffset.ParseExact) if you want to store timezone information.
Your ParseExact format also doesn't quite match the setting value: it should have a zz at the end for the timezone information. You can also use DateTimeOffset.Parse since your setting string is in a standard format.
It's a standard format, so the ParseExact isn't needed, try:
StartDate = DateTime.Parse(RoleEnvironment.GetConfigurationSettingValue("abcdefgh"));
I substituted the hard-coded value you provided and got the correct result for my timezone (GMT-4) as
8/11/2012 2:00 AM
Note: as others mentioned, the timezone is not retained, so you will get the correct localized time corresponding to whatever timezone information was in the string, but you won't be able to find out what timezone that was. The DateTime.Kind property will reflect that it's a local time.

Converting UK times (both BST and GMT) represented as strings to UTC (in C#)

I have to use some dates and times from a legacy database. They are represented as strings. Dates are dd/MM/yy. Times are HH:mm.
I'd like to convert these to UTC as soon as I pull them from the database. I'm working on US systems, so need a common time.
The problem I'm facing is how to convert them to UTC DateTime values. I can do the parsing, etc. The real problem I have concerns the timezone.
I'm trying to use the following approach:
DateTime ukTime = // Parse the strings in a DateTime value.
TimeZoneInfo timeZoneInformation = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
DateTimeOffset utcTime = new DateTimeOffset(ukTime, timeZoneInformation.BaseUtcOffset);
However, this gives incorrect values if the date is in the British Summer Time period.
I can use "GMT Daylight Time" on those dates, but that requires me to know when the switchover is. I'm sure there must be a less laborious way.
As I'm not using a machine with UK time settings I can't rely on local time.
Basically, I need something like:
// Works for both GMT (UTC+0) and BST (UTC+1) regardless of the regional settings of the system it runs on.
DateTime ParseUkTimeAsUtcTime(string date, string time)
{
...
}
I've scoured the posts, but couldn't find anything that addressed this directly. Surely this is also an issue with EST, EDT, etc?
Try using the GetUtcOffset() method on your TimeZoneInfo instance, which takes "adjustment rules" into consideration.
Using this should work basically the same as your original example, but you'll use that method instead of the BaseUtcOffset property.
DateTime ukTime = // Parse the strings in a DateTime value.
TimeZoneInfo timeZoneInformation = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
DateTimeOffset utcTime = new DateTimeOffset(ukTime, timeZoneInformation.GetUtcOffset(ukTime));
How about:
DateTime.Parse(dateTimeString).ToUniversalTime();
Assuming that the database server stores its datetimes in the same timezone as your application server.

Categories

Resources