Offset for UTC to Daylight Savings Time incorrect - c#

I am trying to set a time to EST, and then find what it UCT time is. (We have our reasons).. I have read that "Eastern Standard Time" should take into account the Daylight savings time. But when we check the date, and we know it falls within Daylight Savings time, it still tries to convert for 5 hours instead of 4. Is there any method I am missing? Or do we have to do some manipulation.
DateTimeOffset convertDateTime = new DateTimeOffset(
subbmission.EntryDate,
TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time").BaseUtcOffset);
I added the following bu the boss is not a fan of using this..so any ideas would be greatly appreciated.
if (zone.IsDaylightSavingTime(convertDateTime.DateTime))
{
currentDateTime = convertDateTime.DateTime.AddHours(-1);
}
else
{
currentDateTime = convertDateTime.DateTime;
}

Fundamentally, you're using the wrong approach - if you want to convert to UTC, use TimeZoneInfo.ConvertTimeToUtc(DateTime, TimeZoneInfo). You'll want to be careful around times that are either invalid (because they were skipped) or ambiguous (because the clock went back, and the same local time happened twice).
If you actually want a DateTimeOffset so that you can get both the local and UTC times, you could use call ConvertTimeToUtc then compute the difference between them. It's a shame that TimeZoneInfo doesn't appear to have a "construct a DateTimeOffset based on this DateTime in this time zone" but I can't see it...
As an alternative, you could use my Noda Time project which makes all of this a lot clearer, of course :)

To get the correct offset for a date in the timezone (with or without daylight saing time), i use something like
DateTimeOffset utcOffset = new DateTimeOffset(subbmission.EntryDate.ToUniversalTime(), TimeSpan.Zero);
var zone TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
var correctOffset = zone.GetUtcOffset(utcOffset);

Related

Confusion with the naming of 'LocalDateTime' in NodaTime

I'm confused with the naming of LocalDateTime in NodaTime.
Reading the documentation, it appears that it says:
A LocalDateTime value does not represent an instant on the global time line, because it has no associated time zone
Therefore, I would interpret this as:
"If I create a LocalDateTime in daylight savings, it doesn't matter, because this LocalDateTime has no knowledge of TimeZone and therefore no known offset from UTC."
Therefore, if I want to convert that LocalDateTime to UTC, I must do this:
// We can't convert directly to UTC because it doesn't know its own offset from UTC.
var dt = LocalDateTime.FromDateTime(DateTime.SpecifyKind(myDt, DateTimeKind.Unspecified));
// We specify the timezone as Melbourne Time,
// because we know this time refers to Melbourne time.
// This will attach a known offset and create a ZonedDateTime.
// Remember that if the system time variable (dt) is in daylight savings, it doesn't
// matter, because LocalDateTime does not contain such information.
// Therefore 8:00 PM in Daylight Savings and 8:00 PM outside of daylight savings
// are both 8:00 PM when we created the ZonedDateTime as below.
var melbCreatedTime = createdTime.InZoneLeniently(DateTimeZoneProviders.Tzdb["Australia/Melbourne"]);
// Now we can get to UTC, because we have a ZonedDateTime and therefore
// have a known offset from UTC:
sharedB.CreatedUTC = melbCreatedTime.ToDateTimeUtc();
Is this correct? DateTime logic is by far my weakest area, and I just want to make sure I understand what's happening here.
Without the comments:
var dateCreatedLocal = LocalDateTime.FromDateTime(DateTime.SpecifyKind(result.DateCreated.Value, DateTimeKind.Unspecified));
var dateCreatedUtc = dateCreatedLocal.InZoneLeniently(DateTimeZoneProviders.Tzdb["Australia/Melbourne"]).ToDateTimeUtc();
sharedB.CreatedUTC = dateCreatedUtc;
Thanks.
There's no concept of a "LocalDateTime in daylight savings". It's just a date and time, with no reference to any time zone that might or might not be observing DST.
However, if your aim is just to go from a DateTime with a Kind of Unspecified to a DateTime with a Kind of Utc using a particular time zone, I'd normally suggest using TimeZoneInfo - unless you specifically need IANA time zone ID support.
In general, I'd suggest using the Noda Time types everywhere - or if you're going to use the BCL types everywhere, don't use Noda Time at all. There are some cases where it makes sense to use Noda Time just for an isolated piece of code, and if you really need to do that here, then I believe your code should be fine - but be aware of the implications of InZoneLeniently when it comes to ambiguous or skipped local times around daylight saving changes (or other offset changes).

How to use Standard Time Only in TimezoneInfo.ConvertTime C#

I am having a Issue with the C# function of TimeZoneInfo.ConvertTime().
What i need to get is the Standard Time not the DST but i'm only getting DST is there a way to Tell the Function to only get the Result in Standard Time.
My local Timezone is UTC -6:00 Central America Standard Time, so if my time is 12:00 PM the Conversion i'm getting is throwing it at 2 PM Eastern Time but i need it to tell me it's 1:00 PM.
public static DateTime TimetoEst( DateTime timenow)
{
var currentTimeZone = TimeZone.CurrentTimeZone.GetUtcOffset(timenow).ToString();
var estzone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
var conver = TimeZoneInfo.ConvertTime(timenow, estzone);
return conver;
}
Thanks
A couple of things:
The ID "Eastern Standard Time" represents both EST and EDT, with the appropriate transitions in place. It would more appropriately be called "Eastern Time", but alas this is the identifier and the convention used by Windows time zones.
The Eastern Time zone really does transition in and out of daylight saving time. If you were to always adjust to just Eastern Standard Time (UTC-5), you would be ignoring the reality of timekeeping in that region - that it is in Eastern Daylight Time (UTC-4) for dates in the summer months.
Your code will indeed return EST, but only for date values that are outside of the DST period.
Now, if you still think this is what you want to do - then you can do it without the TimeZoneInfo object at all. Simply adjust the result from the offset that applies to your source value, to the UTC-5 offset that applies to EST.
public static DateTime TimetoEst(DateTime timenow)
{
var dto = new DateTimeOffset(timenow); // will use .Kind to decide the offset
var converted = dto.ToOffset(TimeSpan.FromHours(-5));
return converted.DateTime;
}
This is what you asked, but for the record - I don't think this is the best plan. Besides the issues above - assuming that the input value should be interpreted based on the local time zone is not necessarily a good idea - and unless the input time has a .Kind of DateTimeKind.Utc, it will indeed use the computer's local time zone.
You did label the variable as timenow. If you are just after a DateTime which represents the current time in a fixed offset, then it's much safer just to get it like so:
DateTime dt = DateTimeOffset.UtcNow.ToOffset(TimeSpan.FromHours(-5)).DateTime;
And if you really want the current time in the Eastern Time zone, taking DST into account when appropriate, then:
DateTime dt = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.UtcNow,
"Eastern Standard Time");

Creating daylight savings-aware DateTime when server set to UTC

My application is hosted on Windows Azure, which has all servers set to UTC.
I need to know when any given DateTime is subject to daylight savings time. For simplicity, we can assume that my users are all in the UK (so using Greenwich Mean Time).
The code I am using to convert my DateTime objects is
public static DateTime UtcToUserTimeZone(DateTime dateTime)
{
dateTime = DateTime.SpecifyKind(dateTime, DateTimeKind.Utc);
var timeZone = TimeZoneInfo.FindSystemTimeZoneById("Greenwich Standard Time");
var userDateTime = TimeZoneInfo.ConvertTime(dateTime, timeZone);
return DateTime.SpecifyKind(userDateTime, DateTimeKind.Local);
}
However, the following test fails at the last line; the converted DateTime does not know that it should be in daylight savings time.
[Test]
public void UtcToUserTimeZoneShouldWork()
{
var utcDateTime = new DateTime(2014, 6, 1, 12, 0, 0, DateTimeKind.Utc);
Assert.IsFalse(utcDateTime.IsDaylightSavingTime());
var dateTime = Utils.UtcToUserTimeZone(utcDateTime);
Assert.IsTrue(dateTime.IsDaylightSavingTime());
}
Note that this only fails when my Windows time zone is set to (UTC) Co-ordinated Universal Time. When it is set to (UTC) Dublin, Edinburgh, Lisbon, London (or any other northern-hemisphere time zone that observes daylight savings), the test passes. If you change this setting in Windows a restart of Visual Studio is required in order for the change to fully take effect.
What am I missing?
Your last line specifies that the value is in the local time zone of the system it's running in - but it's not really... it's converted using a different time zone.
Basically DateTime is somewhat broken, in my view - which makes it hard to use correctly. There's no way of saying "This DateTime value is in time zone x" - you can perform a conversion to find a specific local time, but that doesn't remember the time zone.
For example, from the docs of DateTime.IsDaylightSavingTime (emphasis mine):
This method determines whether the current DateTime value falls within the daylight saving time range of the local time zone, which is returned by the TimeZoneInfo.Local property.
Obviously I'm biased, but I'd recommend using my Noda Time project for this instead - quite possibly using a ZonedDateTime. You can call GetZoneInterval to obtain the ZoneInterval for that ZonedDateTime, and then use ZoneInterval.Savings to check whether the current offset contains a daylight savings component:
bool daylight = zonedDateTime.GetZoneInterval().Savings != Offset.Zero;
This is slightly long-winded... I've added a feature request to consider adding an IsDaylightSavingTime() method to ZonedDateTime as well...
The main thing you are missing is that "Greenwich Standard Time" is not the TimeZoneInfo id for London. That one actually belongs to "(UTC) Monrovia, Reykjavik".
You want "GMT Standard Time", which maps to "(UTC) Dublin, Edinburgh, Lisbon, London"
Yes, Windows time zones are weird. (At least you don't live in France, which gets strangely labeled as "Romance Standard Time"!)
Also, you should not be doing this part:
return DateTime.SpecifyKind(userDateTime, DateTimeKind.Local);
That will make various other code think it came from the local machine's time zone. Leave it with the "Unspecified" kind. (Or even better, use a DateTimeOffset instead of a DateTime)
Then you also need to use the .IsDaylightSavingsTime method on the TimeZoneInfo object, rather than the one on the .DateTime object. (There are two different methods, with the same name, on different objects, with differing behavior. Ouch!)
But I wholeheartedly agree that this is way too complicated and error prone. Just use Noda Time. You'll be glad you did!

Is there a generic TimeZoneInfo For Central Europe?

Is there a generic TimeZoneInfo for Central Europe that takes into consideration both CET and CEST into one?
I have an app that is doing the following:
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time");
DateTimeOffset dto = new DateTimeOffset(someDate, tzi.BaseUtcOffset);
var utcDate = dto.ToUniversalTime().DateTime;
The problem is that this is returning the wrong utcDate because the BaseUtcOffset is +1 instead of +2. It appears that CET has DST as well and depending on the time of the year it is +1 or +2.
Firstly, I'd like to applaud mgnoonan's answer of using Noda Time :) But if you're feeling less adventurous...
You're already using the right time zone - but you shouldn't be using BaseUtcOffset which is documented not to be about DST:
Gets the time difference between the current time zone's standard time and Coordinated Universal Time (UTC).
It can't possibly take DST into consideration when you're not providing it a DateTime to fetch the offset for :)
Assuming someDate is a DateTime, you could use:
DateTimeOffset dto = new DateTimeOffset(someDate, tzi.GetUtcOffset(someDate));
Or just ConvertTimeToUtc:
var utcDate = TimeZoneInfo.ConvertTimeToUtc(someDate, tzi);
Note that you should work out what you want to do if your local time occurs twice due to a DST transition, or doesn't occur at all.
Maybe Noda Time can help you out?

Having problems with converting my DateTime to UTC

I am storing all my dates in UTC format in my database. I ask the user for their timezone and I want to use their time zone plus what I am guessing is the server time to figure out the UTC for them.
Once I have that I want to do a search to see what the range is in the database using their newly converted UTC date.
But I always get this exception.
System.ArgumentException was unhandled by user code
Message="The conversion could not be completed because the
supplied DateTime did not have the Kind property set correctly.
For example, when the Kind property is DateTimeKind.Local,
the source time zone must be TimeZoneInfo.Local.
Parameter name: sourceTimeZone"
I don't know why I am getting this.
I tried 2 ways
TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById(id);
// I also tried DateTime.UtcNow
DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local);
var utc = TimeZoneInfo.ConvertTimeToUtc(now , zone );
This failed so I tried
DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local);
var utc = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(now,
ZoneId, TimeZoneInfo.Utc.Id);
This also failed with the same error. What am I doing wrong?
Edit Would this work?
DateTime localServerTime = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Local);
TimeZoneInfo info = TimeZoneInfo.FindSystemTimeZoneById(id);
var usersTime = TimeZoneInfo.ConvertTime(localServerTime, info);
var utc = TimeZoneInfo.ConvertTimeToUtc(usersTime, userInfo);
Edit 2 # Jon Skeet
Yes, I was just thinking about that I might not even need to do all this. Time stuff confuses me right now so thats why the post may not be as clear as it should be. I never know what the heck DateTime.Now is getting (I tried to change my Timezone to another timezone and it kept getting my local time).
This is what I wanted to achieve: User comes to the site, adds some alert and it gets saved as utc (prior it was DateTime.Now, then someone suggested to store everything UTC).
So before a user would come to my site and depending where my hosting server was it could be like on the next day. So if the alert was said to be shown on August 30th (their time) but with the time difference of the server they could come on August 29th and the alert would be shown.
So I wanted to deal with that. So now I am not sure should I just store their local time then use this offset stuff? Or just store UTC time. With just storing UTC time it still might be wrong since the user still probably would be thinking in local time and I am not sure how UTC really works. It still could end up in a difference of time.
Edit3
var info = TimeZoneInfo.FindSystemTimeZoneById(id)
DateTimeOffset usersTime = TimeZoneInfo.ConvertTime(DataBaseUTCDate,
TimeZoneInfo.Utc, info);
You need to set the Kind to Unspecified, like this:
DateTime now = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Unspecified);
var utc = TimeZoneInfo.ConvertTimeToUtc(now , zone);
DateTimeKind.Local means the in local time zone, and not any other time zone. That's why you were getting the error.
The DateTime structure supports only two timezones:
The local timezone the machine is running in.
and UTC.
Have a look at the DateTimeOffset structure.
var info = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");
DateTimeOffset localServerTime = DateTimeOffset.Now;
DateTimeOffset usersTime = TimeZoneInfo.ConvertTime(localServerTime, info);
DateTimeOffset utc = localServerTime.ToUniversalTime();
Console.WriteLine("Local Time: {0}", localServerTime);
Console.WriteLine("User's Time: {0}", usersTime);
Console.WriteLine("UTC: {0}", utc);
Output:
Local Time: 30.08.2009 20:48:17 +02:00
User's Time: 31.08.2009 03:48:17 +09:00
UTC: 30.08.2009 18:48:17 +00:00
Everyone else's answer seems overly complex. I had a specific requirement and this worked fine for me:
void Main()
{
var startDate = DateTime.Today;
var StartDateUtc = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.SpecifyKind(startDate.Date, DateTimeKind.Unspecified), "Eastern Standard Time", "UTC");
startDate.Dump();
StartDateUtc.Dump();
}
Which outputs (from linqpad) what I expected:
12/20/2013 12:00:00 AM
12/20/2013 5:00:00 AM
Props to Slaks for the Unspecified kind tip. That's what I was missing. But all the talk about there being only two kinds of dates (local and UTC) just muddled the issue for me.
FYI -- the machine I ran this on was in Central Time Zone and DST was not in effect.
As dtb says, you should use DateTimeOffset if you want to store a date/time with a specific time zone.
However, it's not at all clear from your post that you really need to. You only give examples using DateTime.Now and you say you're guessing that you're using the server time. What time do you actually want? If you just want the current time in UTC, use DateTime.UtcNow or DateTimeOffset.UtcNow. You don't need to know the time zone to know the current UTC time, precisely because it's universal.
If you're getting a date/time from the user in some other way, please give more information - that way we'll be able to work out what you need to do. Otherwise we're just guessing.
UTC is just a time zone that everyone agreed on as the standard time zone. Specifically, it's a time zone that contains London, England. EDIT: Note that it's not the exact same time zone; for example, UTC has no DST. (Thanks, Jon Skeet)
The only special thing about UTC is that it's much easier to use in .Net than any other time zone (DateTime.UtcNow, DateTime.ToUniversalTime, and other members).
Therefore, as others have mentioned, the best thing for you to do is store all dates in UTC within your database, then convert to the user's local time (by writing TimeZoneInfo.ConvertTime(time, usersTimeZone) before displaying.
If you want to be fancier, you can geolocate your users' IP addresses to automatically guess their time zones.

Categories