Date time time Convertion From UTC to Local Twice - c#

I want to convert my time to universal time when I store date time in database and convert back to system time when I retrieve from the database.
Both of them I have done it success fully by creating functions ConvertToLocalTime()
and ConvertToUniversalTime().
The code for convert to local time is given below:
public void ConvertToLocalTime()
{
string szTimezoneinfoid = TimeZoneInfo.Local.Id.ToString();
TimeZoneInfo tzInfo = TimeZoneInfo.FindSystemTimeZoneById(szTimezoneinfoid);
this.m_dtCreatedDate = TimeZoneInfo.ConvertTimeFromUtc(m_dtCreatedDate, tzInfo);
}
When I call a function Shipment.ConvertToLocalTime() it changes the shipment created date to local time till this it works fine.
Now if I call the Shipment.ConvertToLocalTime() again then it consider the local time again as UTC and convert it to local time.
Is there any way to block the conversion to local time if it is already in local time?
Does there any way that we can get the time zone of the datetime?

The result of TimeZoneInfo.ConvertTimeFromUtc is a DateTime with a Kind of Unspecified, unless the time zone itself is UTC.
When you pass that result into TimeZoneInfo.ConvertTimeFromUtc, it will assume you understand it to be UTC DateTime - otherwise you wouldn't be calling ConvertTimeFromUtc...
Basically, DateTime.Kind is a hack to try to work around the problem that DateTime tries to represent too many different things. Your method starts with potentially ambiguous data, and ends up with ambiguous data... which doesn't bode well.
Either you could just make sure you always know what you're doing, and avoid calling your method with its own result, or you could consider using Noda Time, my alternative .NET date/time API which avoids this sort of thing by having different data types to represent different kinds of data.

I'm not sure exactly what's going wrong in your example, but if you simply want to get a date/time you know to be UTC from the database and convert it to local time, you can do the following:
var dbTime = //whatever, from database.
var utcTime = DateTime.SpecifyKind(dbTime, DateTimeKind.Utc);
var localTime = utcTime.ToLocalTime();
In my own project, I check dbTime.Kind != DateTimeKind.Local first, jut in case my data reader has decided to start performing the conversion for me.

Related

C# Parse String into DateTime with Specific TimeZone

I've been struggling with this for a bit so I'll punt to SO.
I have an app where I need to parse strings into datetimes. The strings look like this:
"201503131557"
(they will always be this format "yyyyMMddHHmm") and always be in Central Standard Time (or Central Daylight Time depending on the date)even though they don't specify it.
When I parse them I understand that it parses them into local time. I'm running on Azure PaaS and don't want to change it to run in CST just to support this operation.
How do i write code that works both locally and in Azure PaaS that will correctly parse these dates into DateTimes setting their timezone to CST?
Here is a quick unit test I wrote to prove Matt Johnson's answer.
[TestMethod]
public void DateTests()
{
var unSpecDt = DateTime.ParseExact("201503131557", "yyyyMMddHHmm", CultureInfo.InvariantCulture);
var tz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
var utcDt = TimeZoneInfo.ConvertTimeToUtc(unSpecDt, tz);
var offset = tz.GetUtcOffset(unSpecDt);
var dto =new DateTimeOffset(unSpecDt, offset);
var cstDt = dto.DateTime;
Assert.IsTrue(cstDt.Hour - utcDt.Hour == offset.Hours);
}
To parse the string, since you have only the date and time, and you know the specific format the strings will be in, do this:
DateTime dt = DateTime.ParseExact("201503131557", "yyyyMMddHHmm", CultureInfo.InvariantCulture);
The resulting value will have its Kind property set to DateTimeKind.Unspecified (not local time, as you thought). That is to be expected, as you provided no information regarding how this timestamp is related to UTC or local time.
You said the value represents time in Central Standard Time. You'll need a TimeZoneInfo object that understands that time zone.
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
Note that this identifier represents the Central Time observed in the USA, including both CST or CDT depending on which is in effect for the given value (despite the word "Standard" in its name). Also note that it valid on Windows operating systems only. If you wanted to use .NET Core on some other OS, then you'd need to pass the IANA identifier "America/Chicago" instead (or use my TimeZoneConverter library to use either identifier on any platform).
The next step is to figure out what you want to do with this value. You might do a few different things with it:
If you want to convert it to the equivalent UTC value, represented as a DateTime, then you can do this:
DateTime utc = TimeZoneInfo.ConvertTimeToUtc(dt, tz);
If you want a DateTimeOffset representation which holds the input you gave and the offset from UTC as it related to US Central Time, then you can do this:
TimeSpan offset = tz.GetUtcOffset(dt);
DateTimeOffset dto = new DateTimeOffset(dt, offset);
Keep in mind that it's possible for the input time is invalid or ambiguous if it falls near a DST transition. The GetUtcOffset method will return the standard offset in such cases. If you want different behavior, you have more code to write (out of scope for this post).
There are other things you might do, all of which are provided by the TimeZoneInfo class.
Note that "Azure PaaS" might refer to a few different things, and while there is a setting called WEBSITE_TIME_ZONE in Azure App Service - I don't recommend you lean on it. Consider it a last resort to be used only when you can't control the code. In most cases, it is better off writing your code to never depend on the time zone setting of the system it runs on. That means never calling DateTime.Now, or TimeZoneInfo.Local, DateTime.ToLocalTime, or even DateTime.ToUniversalTime (since it converts from the local time zone), etc. Instead, rely upon methods that work explicitly with either UTC or a specific time zone or offset. Then you will never need to care about where your app is hosted.
Lastly, understand that neither the DateTime or DateTimeOffset types have any capability of understanding that a value is tied to a specific time zone. For that, you'd need to either write your own class, or look to the Noda Time library whose ZonedDateTime class provides such functionality.
You can use:
DateTime.ParseExact(...)
public static DateTime ParseExact (string s, string format, IFormatProvider provider);
This datetime do not have any metadata about timezone. You can convert to UTC and then you will be sure that you are able to convert to all timezones.
Use the DateTime.TryParseExact(...) method with DateTimeStyles.RoundtripKind to avoid conversion.
DateTime dt;
DateTime.TryParseExact("201503131557", "yyyyMMddHHmm", null, System.Globalization.DateTimeStyles.RoundtripKind, out dt);
If you need to convert you will need to go to UTC then use the conversion methods of TimeZoneInfo with TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time").

UTC date.ToLocalTime() still produces date in UTC time

I'm working with an ASP.Net backend. I'm saving all my dates from the client side to the database in UTC time.
I have a function in the backend that exports some records and I would like to convert the dates extracted, from UTC time to the user's local time before displaying them.
I have tried tons of solutions proposed here on StackOverflow, but none of them seem to convert the date to local time, even though this works when displaying some of these dates on the client.I suspect the server already thinks the date is in local time, but I'm not sure how else to solve it.
Below are the different solutions I have tried:
//1.
var alertTime = record.TimeRecorded.GetValueOrDefault().ToLocalTime().ToString("hh:mm tt");
// 2.
var alertTime = record.TimeRecorded;
if (alertTime.HasValue)
{
var timeInUtc = TimeZoneInfo.ConvertTimeToUtc(alertTime.Value);
string alertTimeToLocalTime = timeInUtc.ToLocalTime().ToString("hh:mm tt");
}
//alertTimeToLocalTime is still in UTC time here
// 3.
if (alertTime.HasValue)
{
var localTime = TimeZoneInfo.ConvertTimeFromUtc(timeInUtc, TimeZoneInfo.Local);
string alertTimeToLocalTime = localTime.ToString("hh:mm tt");
}
None of these have managed to convert the alertTime to local time.
Am I missing something?
EDIT
//4. Another approach I had already tried which didn't work as well
var alertTime = DateTime.SpecifyKind(record.TimeRecorded.GetValueOrDefault(), DateTimeKind.Utc);
alertTimeToLocalTime = alertTime.ToLocalTime().ToString("hh:mm tt");
You said:
... from UTC time to the user's local time ...
Nothing in ASP.Net will tell you the user's local time zone. Calling ToLocalTime will convert from UTC to the server's time zone (unless the .Kind is already DateTimeKind.Local).
In many cases, the best practice of setting the server's time zone to UTC will mean that you will see no change with ToLocalTime or ToUniversalTime, other than the kind. And since the server's time zone is irrelevant in most cases, this is not the correct approach.
Instead, you either need to know the user's time zone through some other mechanism (such as them selecting it in your application) so you can use the TimeZoneInfo.ConvertTime (or Noda Time) to convert server-side, or you need to send UTC time down to the client and do the utc-to-local in JavaScript (since the browser is running in the user's time zone).
In general, any use of "local time" in a server application (such as ASP.NET) should be avoided. This includes ToLocalTime, ToUniversalTime, DateTimeKind.Local, TimeZoneInfo.Local, DateTime.Now, and a few other miscellaneous things.
It's because your DateTime's Kind is either Local or Unspecified. See the documentation for ToLocalTime():
Starting with the .NET Framework version 2.0, the value returned by the ToLocalTime method is determined by the Kind property of the current DateTime object. The following table describes the possible results.
Utc - This instance of DateTime is converted to local time.
Local - No conversion is performed.
Unspecified - This instance of DateTime is assumed to be a UTC time, and the conversion is performed as if Kind were Utc.
You can use the SpecifyKind method to set the Kind prior to doing the conversion.

Change server time to local time that is sent by Facebook

I am using Facebook C# SDK to get some data from my Facebook page.
When I get objects of data, at that time I get one field:
created_time : 2014-05-23T11:55:00+0000
As it is not same as my current time how do I convert it to my current time or to standard UTC time?
For me the code:
DateTime.Parse("2014-05-23T11:55:00+0000").ToString()
returns
2014-05-23 12:55:00
Because 'DateTime.Parse()` understands the timezone in this string, and by default produces a local time. My timezone being Irish Summer Time, one hour ahead of UTC, that means 2014-05-23T12:55:00 for me, for you it'll be a different time depending on your timezone.
For the converse, to get the time parsed in UTC (more useful for behind-the-scenes web stuff or storage, rather than user interface), use DateTime.Parse("2014-05-23T11:55:00+0000").ToUniversalTime()
If you need to deal with timezones other than local to the machine the code is running on, or UTC, then you will need to use DateTimeOffset, with the exception that DateTimeParse will handle other timezones in the string, but from that point on only has the concepts of "local time", "universal time" and "unknown timezone".
I would use DateTimeOffset.ParseExact, specifying the format string an the invariant culture:
DateTimeOffset value = DateTimeOffset.ParseExact(text, "yyyy-MM-dd'T'HH:mm:ssK",
CultureInfo.InvariantCulture);
I strongly recommend this over using DateTime.Parse for the following reasons:
This always uses the invariant culture. It's explicitly stating that you don't want to use the local culture, which might have a different default calendar system etc
This specifies the format exactly. If the data becomes "10/06/2014 11:55:00" you will get an exception, which is better than silently guessing whether this is dd/MM/yyyy or MM/dd/yyyy
It accurately represents the data in the string: a date/time and an offset. You can then do whatever you want with that data, but separating the two steps is clearer
The first two of these can be fixed by using DateTime.ParseExact and specifying the culture as well, of course.
You can then convert that to your local time zone, or to any other time zone you want, including your system local time zone. For example:
DateTimeOffset localTime = value.ToLocalTime(); // Applies the local time zone
or if you want a DateTime:
DateTimeOffset localTime = value.ToLocalTime().DateTime;
It's just one extra line of code, but it makes it clearer (IMO) what you're trying to do. It's also easier to compare DateTimeOffset values without worrying about what "kind" they are, etc.
In fact, I wouldn't personally use DateTimeOffset or DateTime - I'd use OffsetDateTime from my Noda Time project, but that's a different matter.

Correct insert DateTime from c# to mongodb

I try to insert local time in MongoDB
var time = DateTime.Now; // 03.05.2014 18:30:30
var query = new QueryDocument
{
{ "time", nowTime}
};
collection3.Insert(query);
But in database I see ISODate("2014-05-03T15:30:30.170Z"),
that must be ISODate("2014-05-03T18:30:30.300Z").
Please help me!
I think you're getting confused by time zones. The Z at the end of the string indicates that it's in UTC. When you posted this question, it was just after 15:30 UTC.
I strongly suspect that the correct instant in time is being recorded - but it's being recorded as an instant in time without reference to a particular time zone. You can then convert that to whatever time zone you want later on, but recording the UTC time is almost always the correct approach.
As an aside, you can make this clearer by using UtcNow to start with. That way it's more obvious that you're not trying to obtain a "local" time.
Looking at the MongoDB documentation, it seems that the internal representation is simply a number of milliseconds since the Unix epoch - so again, that has no indication of time zone or an offset between UTC and local time. If you want to store a value which can be converted back to the local time you saw when it was recorded (even if you're now in a different time zone) you should store a time zone ID and/or the UTC offset as a separate value. That's not needed terribly often, but it's an option.
The MongoDB driver is converting your DateTime to UTC.
If you don't want to bother with time zones, you could change the kind to UTC:
time = DateTime.SpecifyKind(time, DateTimeKind.Utc);
I would not recommed this because the database will store a "wrong" Date, but it will show you ISODate("2014-05-03T18:30:30.170Z").
Just define the "Kind" of the DateTime by the "BsonDateTimeOptions" attribute and set it to local:
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
public DateTime SomeDateProperty {get;set;}
It could work with:
DateTime t = DateTime.Now;
var update = Builders<BsonDocument>.Update
.Set("key1", t.ToUniversalTime())
.CurrentDate("key_updatetime");

Convert UTC/GMT time to local time

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();

Categories