This seems so basic but I've been researching this (here, other sites, Microsoft documentation, etc.) and working on it for months. I'm just not getting the problem.
I have a program that uses DateTime a lot. It worked great before DST last fall. Then everything stopped working well - seems like everything's off by an hour now. I compensated with DateTime.Now.AddHours() but that's only a band aid.
I've tried DateTimeOffset.Now, DateTime.ToLocalTime, DateTime.UTCNow, etc. It just won't use system time or compensate.
I'm in Pacific Standard time and nobody will ever use this site outside of this time zone if that's another way to approach it.
Anyone have any thoughts on why this is happening? The whole program is doing it, but I'll give a little snippit of one of the areas.
<table class="table-bordered" id="table_lunch" style="width:15em;">
#*populate the table with only those breaks that lack a TimeCleared value*#
#if (ViewBag.Lunches != null)
{
foreach (var item in modelOrdered)
{
if (DateTimeOffset.Now.AddHours(-1) >= item.LunchTime.AddMinutes(-1) && DateTimeOffset.Now.AddHours(-1) <= item.LunchTime.AddMinutes(1))
{
if (item.EmpSent == false)
{
<tr class="listTime">
#*Make each name clickable - deletes the lunch*#
<td class="LunchIdNumber">
#if (item.LongerLunch == false && item.Double == false)
{
<input type="hidden" class="hiddenLunchID" value="#item.LunchID" />
#item.Employee.DisplayName
It's really hard to answer your question without a minimal, reproducible example, but I will make an attempt.
In your code, by using DateTimeOffset.Now you are using the system's local time zone as a basis of comparison. Instead, you should get the time in a specific time zone before making your comparison:
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
DateTimeOffset now = TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, tzi);
Then you can use now in your comparison and it will reflect the current time in the Pacific time zone, both when DST is in effect and when it is not.
Note that I inferred a few things from your question, comments, and symptoms you described:
Your item.LunchTime is a DateTime type. If it's actually a DateTimeOffset type, then the comparison would be made correctly regardless of time zone. In other words, DateTime comparison is based on the date and time as presented while DateTimeOffset comparison is based on the equivalent UTC timestamp.
Your server is a Windows server. I believe GoDaddy only uses Plex for it's Windows hosting (from what I could gather from searching their documentation).
Because you said everything worked until last fall, I assume you meant it worked until DST ended. In other words, while Pacific Time was UTC-7 the value you got from DateTimeOffset.Now was what you expected, but when Pacific Time returned to UTC-8, you still got values with a UTC-7 offset as before.
If that last point is correct, then likely your server is set for Arizona rather than for Pacific Time. Since GoDaddy is an Arizona-based company, this is plausible.
It may or may not be possible to change the system's time zone depending on how much of the hosting environment GoDaddy has exposed. I can see from Plesk's docs that they do indeed give a Date and Time settings panel - but I'm not sure whether GoDaddy exposes it to you or not.
Again, my recommendation would be to ignore the system-local time zone and not try to change it. Instead, alter your code to use a specific time zone as shown. Put it in a helper function somewhere in your code if that makes it easier to use throughout your applicaiton.
Related
I have my aplication which uses BST time zone(UTC+1) and another application where i am using the value from the above specified application . On my second server the timezone is UTC.
Because of this when i get value in a datetime property there is a diufference of one hour and because of that the value is going wrong.
I wont be able to adjust the timezone on both the servers . How can i convert the value to a BST time zone in my second application using .net.
public DateTime? EmploymentStartDate { get; set; }
This is my property in my model.
the value from my first server is 29/8/2001 00:00:00 when it comes to the second server where the timezone is UTC it is 28/9/2001 23:00:00 .
It's simple, you must consider all the DateTime always in UTC, so simply call ToUniversalTime() to convert every instance in UTC timezone:
DateTime dt = DateTime.Now;
dt.ToUniversalTime();
I agree with Daniel A. White. You should store everything as UTC and convert the date to local date on the client.
Look this https://msdn.microsoft.com/en-us/en-en/library/system.timezoneinfo.converttimefromutc(v=vs.100).aspx
private DateTime? _employmentStartDate;
public DateTime? EmploymentStartDate
{
get
{
return _employmentStartDate != null ? TimeZoneInfo.ConvertTimeFromUtc(_employmentStartDate.Value, TimeZoneInfo.Local) : new DateTime();
}
set
{
_employmentStartDate = TimeZoneInfo.ConvertTimeToUtc((DateTime)value);
}
}
Also consult this previous StackOverflow post:
Should MySQL have its timezone set to UTC?
This article (and the numerous other articles and web-pages that it links to) discusses MySQL, but the concerns and techniques are similar for any database and programming language.
While your (the OP's) question seems to be about how to convert time information between time zones, the discussion in the comments has grown hot on the subject of choosing a format for storage. First things first – to convert your UTC time to BST you can use TimeZoneInfo.ConvertTimeFromUtc. This method “converts a Coordinated Universal Time (UTC) to the time in a specified time zone.”
Here’s an example:
var bstZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Daylight Time");
bstTime = TimeZoneInfo.ConvertTimeFromUtc(timeUtc, bstZone);
NB 1: If what you really want is local time on the server when executing, obviously you should rather use ToLocalTime(), and that will yield the same result if that local time happens to be BST (and the Kind property of the DateTime variable is not DateTimeKind.Local).
NB 2: To the best of my knowledge, the TimeZoneInfo class referrers to "British Summer Time" as "GMT Daylight Time", and I’m assuming that’s what you refer to as BST. But don’t take my word for it – you need to make sure yourself that you have the right time zone ID.
Now, about storing:
TL;DR
A time signature without time zone information is unreliable information. Given this premise, there is however no true “right or wrong” time zone to use for storing. While most people might argue that UTC is to be considered a best practice, first of all that information (that it's in UTC) should still be stored explicitly alongside the timestamp, and secondly, there may be practical reasons why another format is preferable in a specific scenario.
When performing arithmetic calculations on time values you should use universal time representations to avoid hiccups with regard to daylight saving practices and such.
Be weary of complications that might arise from serializing and de-serializing mechanisms making the wrong assumptions about the information you provide, e.g. System.XML.Serialization or a database engine.
Obviously, when presenting your time information (that is, when creating a string representation suitable for interpretation by humans), you should do so in whatever format the user should be expecting. Regardless of how you persist your values, conversions (with offset) may be necessary to do so.
Check out these links for more information:
Converting Times Between Time Zones
Coding Best Practices Using DateTime in the .NET Framework
It's been asked, but I am battling to grasp the concept of how to handle timezones in a web app. I have a system that tracks the progress of projects. I have a ProjectStartDate DATE in my SQL Server database. (There's a few more fields and tables, but lets focus on one).
The server is located somewhere in the United States. I live in Australia.
Calling SELECT GETDATE() returns "2013-08-11 14:40:50.630"
My system clock shows "2013-08-12 07:40"
In my database, I have 'CreateDateTime' columns on all tables. When I store that, within my c# code, I use CreateDate = DateTime.UtcNow
I use that, as I heard it was better to use UTC.
But, when a user is presented with a calendar control, and they select a Start Date for a project, I store what ever the user selected. No conversion... And as I said, the StartDate is a DATE type in the database.
The problem is, if a project started today - my front end says that the current project is No Started, because the server is still in Yesterday.
I think the dates should be stored as I am storing them. But maybe I need to somehow get the users timezone, and apply that on the UI level?
Problems I see are:
I don't know the users timezone. Add something to allow them to select it?
The status of the project maybe determined in a stored procedure, so when can I apply the conversion? In the proc, it might do a check, and return a VARCHAR stating "Not Started" if the StartDate <= DateTime.Now?
I use EntityFramework and Linq to get data most of the time. I need a strategy for inserting and retrieving data, both from a SQL sense, and a .Net sense.
I have added code to get the user to select their timezone based on this:
public List<TimeZoneDto> GetTimeZones()
{
var zones = TimeZoneInfo.GetSystemTimeZones();
var result = zones.Select(tz => new TimeZoneDto
{
Id = tz.Id,
Description = tz.DisplayName
}).ToList();
return result;
}
That is then persisted in their profile.
All dates are being stored as UTC, as advised in the answers below.
I'm still confused how to handle the dates when they go to and from the database, to the client. Here is an example of how I am storing a record:
public int SaveNonAvailibility(PersonNonAvailibilityDto n)
{
person_non_availibility o;
if (n.Id == 0)
{
o = new person_non_availibility
{
PersonId = n.PersonId,
Reason = n.Reason,
StartDate = n.StartDate,
EndDate = n.EndDate,
NonAvailibilityTypeId = n.NonAvailibilityTypeId,
CreateUser = _userId,
CreateDate = DateTime.UtcNow
};
_context.person_non_availibility.Add(o);
}
else
{
o = (from c in _context.person_non_availibility where c.Id == n.Id select c).FirstOrDefault();
o.StartDate = n.StartDate;
o.EndDate = n.EndDate;
o.Reason = n.Reason;
o.NonAvailibilityTypeId = n.NonAvailibilityTypeId;
o.LastUpdateDate = DateTime.UtcNow;
o.LastUpdateUser = _userId;
o.Deleted = n.Deleted ? DateTime.UtcNow : (DateTime?)null;
}
_context.SaveChanges();
return o.Id;
}
This method basically saves when a person is not available for work. Note the way I store the LastUpdateDate. Also, the 'Start' and 'End' dates. Those dates are more 'Business' dates.
On selection, and then date checking, is where I have issues. In this example, I am getting a persons rate of charge, based on NOW.
public decimal CurrentRate
{
get
{
if (ResourceCosts != null)
{
var i = ResourceCosts.FirstOrDefault(t => DateTime.UtcNow <= (t.EndDate.HasValue ? t.EndDate.Value : DateTime.UtcNow) && DateTime.UtcNow >= t.StartDate);
if (i != null) return i.Cost;
return 0;
}
return 0;
}
}
So, what I am wanting to do here, is, based on the current date, I want to see his rate (As his charge our rate maybe be $100 from the 1st Jan, to the 15th Jan, and then $110 from the 16th until the 31st of Jan. So, I look for the rate applicable today (if any). This doesn't work across time zones, and it's maybe here where I need to do some date manipulation based on the 'DateTime.UTCNow'?
Note, I now know the users timezone based on the code above where I am storing his timezone. I can use that somehow here? Maybe, when the user logs in, grab the date info from his profile (Zimezone info), and then have a global shared function, that returns the users datetime, based on adding or removing hours from the UTC Date... and using that where ever I am doing DateTime.UTCNow?
Hope someone can guide me.
You may find that there is not one single "right" way to handle all of this. There are multiple approaches to the several different problems you describe in your question. I will attempt to clarify a few points.
First, don't ever attempt to think about local time on a server. Your code and data should not have to change based on where you deploy it. You said your server was in the USA, but there are multiple time zones to consider, and many servers will have their time zone set to UTC regardless of their physical location.
You should avoid GETDATE() or SYSDATETIME() in SQL Server. If you need a current timestamp in SQL, use GETUTCDATE() or SYSUTCDATETIME(). If for some reason the server's time zone is important to you, use SYSDATETIMEOFFSET() instead.
Likewise, avoid using DateTime.Now in .Net from any server-side code. Use DateTime.UtcNow or DateTimeOffset.UtcNow for a UTC timestamp, or use DateTimeOffset.Now if for some reason the server's time zone is important to you.
You can read more about this in my blog post: The Case Against DateTime.Now
Next, let's talk about the data type you're using. The date type in SQL Server stores just a date. That's it. No time, no offset, and no time zone. An example would be 2013-08-11. You should use it when you really mean a whole calendar date. There is no worldwide uniform context of "today". Instead, everyone has their own meaning based on their time zone. Also, not every calendar day is 24 hours in length. A day could be 23, 23.5, 24, 24.5 or 25 hours long, depending on how daylight saving time is applied in the particular time zone, and if you are evaluating the day of a DST transition.
In .Net - there is no Date type, so a SQL date is converted to a DateTime with the time set to midnight (00:00:00), and the kind set to Unspecified. But don't fool yourself - the time isn't suddenly midnight, we are just filling in zeros for the time. This can lead to a lot of error and confusion. (If you want to avoid that, you can try Noda Time, which has a LocalDate type for this purpose.)
What you really need to be thinking about, and haven't defined in your question, is this:
What exact moment does a project start?
Right now you are just saying 2013-08-11, which doesn't refer to a specific moment in time. Do you mean the beginning of that day in a particular time zone? Or do you mean the beginning of that day according to the user's time zone? Those might not be the same thing. You can't compare to anyone's "now" (utc, local, or otherwise) unless you know what moment in time you are talking about.
If the project starts at an exact moment in time worldwide, then the easiest thing would be to store a datetime (or datetime2) type that contains that precise time in UTC. So you might say that a project starts at 2013-08-10T14:00:00Z - which would be exactly midnight on August 11th in Sydney, Australia. In .Net, you would use a DateTime type with the .Kind set to Utc.
Another way you could represent this is by storing a datetimeoffset type that has a value of 2013-08-11T00:00:00+10:00 - which is the same moment in time, but uses the offset to give you a value that is pre-converted. (Sydney is at UTC+10 on that date). You would use the DateTimeOffset type to work with this in .Net.
But if the project starts at different times depending on the user, then it's not really an exact moment in time. It's more of a "floating" start. If users from different places around the world are assigned to the same project, then some users could be starting before others. If that's your intention, then you can just use the date type if all projects start at midnight, or you can use a datetime or (datetime2) type if projects might start at different times. In your .Net code, you would use a DateTime type with the .Kind set to Unspecified.
With regard to getting the user's time zone, the best thing you could do would be to ask them. Despite the common misconception - you can't just get it from the browser. All you could tell from the browser is what their current offset is. (Read the "TimeZone != Offset" section of the timezone tag wiki).
When asking the user for their time zone, if you decide to use Windows time zones you can produce a dropdown list from the TimeZoneInfo.GetSystemTimeZones method. The .Id is the key you store in your database, and you show the .DisplayName to the user. Later you can use the TimeZoneInfo.FindSystemTimeZoneById method to get a TimeZoneInfo object that you can use for conversions.
If you wanted to be more precise, you could use IANA time zones instead of the Windows time zones. For that, I recommend using a map-based timezone picker control, such as this one. You might also use jsTimeZoneDetect to guess a default value for your control. On the server you would use Noda Time to perform time zone conversions.
There is an approach that doesn't require time zone conversions. Basically, you do everything in UTC. That includes transmitting the time to the browser in UTC. You can then use JavaScript to get the user's current time in UTC and compare against that.
You can use various functions of the JavaScript Date class to do this if you wish. But you may find it easier to work with a library such as moment.js.
While this approach is viable for many things, security is not one of them. Your user can easily change the clock of their computer to work around this.
Another approach would be to compare server-side against UTC. If you have the exact UTC starting time in your database, then you can just check DateTime.UtcNow in your .Net code and use that to decide what to do. You won't need the user's time zone to make this comparison, but you will need it if you want to show them what that means in their local time.
I hope this clears up the confusion and didn't make it worse! :) If you have additional concerns, please edit your question or ask in comments.
UPDATE
In response to your updated question, I suggest you try the following:
var timeZoneId = "Eastern Standard Time"; // from your user's selection
var timeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
var nowInTimeZone = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, timeZone);
var todayInTimeZone = nowInTimeZone.Date;
var i = ResourceCosts.FirstOrDefault(t => t.StartDate <= todayInTimeZone &&
(!t.EndDate.HasValue || t.EndDate >= todayInTimeZone));
Of course this means that in your StartDate and EndDate fields, you are not storing these as UTC - but rather as the "business dates" that are relevant to the user. These only line up to a specific moment in time when you apply a time zone, so the same UTC timestamp could fall on different dates depending on what the user's time zone is.
Also, you are using fully inclusive ranges, which is usually OK for these kind calendar date ranges. But make sure you realize that there could be overlap. So if you have 2013-01-01 - 2013-02-01 and 2013-02-01 - 2013-03-01, then there is that one day 2013-02-01 that is in both ranges.
A common way around this problem is to use half-open intervals, [start,end). In other words, start <= now && end > now. But this is more common when using a full date and time instead of just a date. You might not need to do this, but you should at least think about it for your particular scenario.
You can get a user's timezone from the browser. This will only get the value that they've set on their computer. So they can manipulate this for their advantage. For example, if you were limiting access until midnight local time, they could change their time to get "early" access. So keep that in mind.
Next what you'll want to do is store all times in UTC in the database. Instead of using GetDate() you can use GetUTCDate().
Next, you can either store their timezone, or their offset from UTC in the database. You get into some danger just storing the UTC offset, because timezones that observe daylights savings will have different offsets at different time of the year.
But with these two pieces of information, you can calculate the "local" time of the user. You can either add the number of hours to your stored date, then compare that to the local server time (also adjusted from UTC.)
Of if you don't really care that much, you can store the time in UTC in the database, then simply compare DateTime.UTCNow on the server to determine if they should have access or not.
I am trying to control Daylight saving in a C# application instead of letting windows do it. (I won't come into the reason here).
So I have removed the checkmark "Automatically adjust clock for daylight saving" in Date and Time settings (Windows7)
I have written this small piece of code in order to demonstrate the problem that I am facing.
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById(TimeZoneInfo.Local.Id);
// "Romance Standard Time"
var rule = tzi.GetAdjustmentRules()[0];
System.Globalization.CultureInfo.CurrentCulture.ClearCachedData();
var timestampToWorkOn = DateTime.Now;
Console.WriteLine("Timezone is: " + tzi.ToString());
Console.WriteLine("Timezone id is: " + tzi.Id);
Console.WriteLine("Timestamp right now: " + timestampToWorkOn.ToString("yyyy-MM-dd HH:mm"));
Console.WriteLine("Rule for change says: " + rule.DaylightTransitionEnd.TimeOfDay.ToString("HH:mm"));
Console.WriteLine("Is it dst: " + tzi.IsDaylightSavingTime(timestampToWorkOn));
Console.WriteLine("Is it ambiguous:" + tzi.IsAmbiguousTime(timestampToWorkOn));
As the transition from dst to normal time is supposed to happen at 3:00 I would suspect that the time from 2:00 to 3:00 would be Ambiguous.
But the result from running the code at 1:54 is:
Timezone is: (UTC+01:00) Brussels, Copenhagen, Madrid, Paris
Timezone id is: Romance Standard Time
Timestamp right now: 2013-10-27 01:54
Rule for change says: 03:00
Is it dst: False
Is it ambiguous:True
I might be missing something.
I would expect dst to be true and ambiguous to be false, but it is the opposite way around.
It's hard to keep an overview but why am I seeing this behavior?
You should read this blog post, which describes in detail how the Windows registry settings are affected by the time zone selection and the "Automatically adjust..." checkbox. It also describes how TimeZoneInfo uses these settings. Specifically, it states:
When daylight saving time is disabled for the local time zone, TimeZoneInfo.Local will return a TimeZoneInfo object with TimeZoneInfo.SupportsDaylightSavingTime set to False. Any TimeZoneInfo.ConvertTime(...) calls using this TimeZoneInfo instance will not take daylight saving time into account.
IMHO, there is never a good reason to clear that checkbox and disable DST. It puts the computer's clock in an artificial reality.
If you are running code on a server, you should probably set the server's time zone to UTC. This will keep Windows from having to update the computer's bios for the transition, and will let local timestamps from other servers all line up.
Regarding your code, realize that the result of DateTime.Now has .Kind == DateTimeKind.Local, and has no bearing on the time zone you used earlier. You happened to be reporting the local time zone, but if you used a different one, your code would be incorrect.
When you are getting the adjustment rules, you are assuming the local time zone will have one. Some (like Arizona) do not have any DST, so they have no adjustment rules and you get an index out of bounds exception (because of the [0]).
Also, just being nitpicky but,
// This line is self redundant.
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById(TimeZoneInfo.Local.Id);
// It can be reduced to:
TimeZoneInfo tzi = TimeZoneInfo.Local;
But the real problem is that because you have the "adjust for DST" option turned off, the DateTime is not being converted to the right UTC moment in order to make the determination if it is ambiguous or not. If you drill in to the .Net source code (decompiled or symbolsource) for DateTime.IsAmbiguousTime(), you will find that it uses TimeZoneInfo.ConvertTime(), which (per the earlier quote) does not take DST into account when the box is unchecked, thus making the result incorrect (essentially 1 hour off).
You should also look at the MSDN notes for TimeZoneInfo.IsAmbiguousTime, which describe how the Kind of the input affects the output result.
I'm working on software that runs reports for GPS devices that are running 24/7/365. Part of the report output requires that we convert our stored database times (kept in Central Standard Time) to user timers (any requested time zone). Twice a year we run into an issue with DST when people run reports that start before and finish after the time change. It fails at one line:
return TimeZoneInfo.ConvertTime(dateToConvert, DatabaseTime, UserTime);
dateToConvert is a DateTime to be converted. DatabaseTime and UserTime are both TimeZoneInfo objects. I'm not doing anything tricky or complicated but DateTimes near the DST time change throw exceptions. Such as 3/10/2013 2:02:11 AM even though it's being "converted" from Central Time to Central Time.
What is the best method for handling DateTimes near DST time changes?
You have garbage in your database, 3/10/2013 2:02:11 AM never existed. The minute after 1:59 AM that morning was 3:00 AM, the clock was moved by an hour. .NET is not going to put up with that junk date.
You will need to find out how that garbage timestamp ended up in your dbase. Clearly a conversion of a time from one timezone to another that disregards daylight savings rules, like active in one but not the other, is a highly likely source of that garbage. If you can't fix your dbase to use UTC then at least do it in your code. First going to UTC in the one timezone and then back to local time in the other. Use the TimeZoneInfo class, ConvertTimeFrom/ToUtc methods.
I ran into this problem. I fixed it by adding a new GMT time column. This allowed the application to work with the original data and any fixes to work with GMT. Then I changed the application so that any code that was having problems with daylight savings would access this new column. Also, as time went on, I re-pointed any code that was used for calculation to this new column leaving the displays to work with the old column. It's not elegant, but it works and it is easy.
Conversion should work properly as the time is not truly junk, as Hans stated, rather it is just non-adjusted (a term I just invented). 3/10/2013 2:02:11 AM CDT == 3/10/2013 8:02:11 AM UTC == 3/10/2013 3:02:11 AM CDT...they are ALL semantically equivalent. If you do not believe me, do the conversion at timeanddate.com and see they all equate (round to nearest 5 minutes for their calculator though). Whether .NET code will allow this semantic equivalence, I have not tried it because I am not in front of my dev box currently.
UPDATE #1:
Run the following code on a computer set to CST time zone:
using System;
namespace TimeZoneSample
{
public static class Program
{
public static void Main()
{
DateTime t = DateTime.Parse("3/10/2013 2:02:11 AM");
Console.WriteLine(t);
Console.WriteLine(t.ToUniversalTime());
Console.WriteLine(t.ToUniversalTime().ToLocalTime());
}
}
}
This yields the following console output:
3/10/2013 2:02:11 AM
3/10/2013 8:02:11 AM
3/10/2013 3:02:11 AM
Proof that my original explanation is correct. quod erat demonstrandum
I would follow one of the other answers if that's at all possible - you want to fix this properly. If your time is incorrect during the fall transition it won't generate an exception, it's just going to randomly be an hour off.
There's a workaround to get you out of your current jam. Since it's only the missing hour during the spring that will cause the exception, you can catch the exception and add an hour into the time before repeating the conversion.
I have read through multiple posts here until my head was ready to explode with little lines on the atlas strewn all over.
Here is what I wanted to do:
I have a calendar full of appointments to display for a business.
The business is in New York (EDT)
My dev machine is in PST
Production Servers are in California (PST)
Calendar data is stored in the DB in UTC
All I want to do is take a time range specified in EDT (say May 15, 9am - 5pm) and show all the appointments on the calendar.
So, my calendar control tells me "I want the appointments from 5/15/12 9am EDT - 5/15/12 5pm EDT"
I say fine, I will call a db proc and pass the date values in UTC, ie (5/15/12 13:00 UTC - 5/15/12 21:00 UTC). Then when I get them, I will convert them back to EDT before handing them to you.
However, Little did I know that .NET will find this simple task to trip me up.
I got the TimeZoneInfo just fine by using:
TimeZoneInfo zoneInfo = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")
But that is as far as things worked.
Here's what I tried next:
DateTime rangeStartUTC = TimeZoneInfo.ConvertTimeToUtc(rangeStart, zoneInfo);
EXCEPTION: Kind is not properly specified.
(WTF is Kind?) I told you the time I want to convert, I told you it is in EDT, I told you I want to convert it to UTC. What more do you want? I could do it by hand. F**ing do it already.
So I tried to set the Kind, but it only has two values! Local or Utc. But my time is neither local, nor Utc. Why in the hell are you asking me for a time zone info then? Cant you just tell what the local time zone is by asking the system clock? Predictably, neither worked. (yeah, according to the docs there is UnSpecified, but again according to the docs, it doesn't really do anything and yes I tried that one too).
Then, I tried:
TimeZoneInfo.ConvertTime(rangeStart, zoneInfo, TimeZoneInfo.Utc)
SAME LAME EXCUSE!
Time to read some more of St. Jon Skeet's passages.
What do you know, there is a new class DateTimeOffset. It will solve all your problems.
God bless for all the nice and merciful .NET 4.0 bounties...
DateTimeOffset offStartTime = new DateTimeOffset(rangeStart, zone.GetUtcOffset(rangeStart));
rangeStartUTC = offStartTime.UtcDateTime;
EXCEPTION: "Offset should be 0 for Utc dates"
Gaaaaah!
How the heck did you conclude that the rangeStart is Utc? Did I ever tell you that?
A bunch of people are quoting
TimeZoneInfo.ConvertTime(rangeStart, zone) as the solution, how is it "Kind" enough to work for them? Unless their source time zone conveniently happens to be the same as their local time zone.
So what is a poor .NET C# developer to do?
Try DateTime.SpecifyKind(rangeStart, DateTimeKind.Unspecified) before DateTimeOffset or TimeZoneInfo. It should not do any conversion of the time itself, just change the Kind.
As I understand it, the Kind on DateTime should be either Unspecified or correspond to the result of private TimeZoneInfo.GetCorrespondingKind() method, which returns Local for
TimeZoneInfo.Local and Utc for TimeZoneInfo.Utc.
UPDATE: Sorry, get the original answer wrong, should be all good now.