KB928388 Breaking Tests with Windows DST TimeZone Patch and Past Dates
UPDATE: It appears that what we suspected is true, Windows understands one set of DST rules. Whatever the current DST rules are are applied to all dates. This doesn't make sense to me as that would make the 2005 test below fail also, and it doesn't. More as it comes in.
UPDATE#2: Seems that a new "Dynamic DST" section is added for future OS's, like Vista, for handling future DST time changes. This change will affect all Windows OS's equally - that is, you don't need to custom code for one OS versus another. If congress, in their infinite wisdom, decides to change the laws of Time and Space again, Vista and other OS's will require only a registry change, and past time rules will still apply. Thanks to Tim Heuer for helping puzzle through it all.
One of our Architects at Corillian, Paul Gomes, led the team that designed our .NET-based OFX Banking Server. That product happens to have ridiculously good Code Coverage and a metric-crapload of Unit Tests. Recently a build server had the Daylight Savings Time (DST) Windows KB928288 patch applied. Immediately an internal Date-related test failed.
Paul dug into it and boiled it down to this simplified test that takes a date in March of 2006 and converts it to GMT.
1: [TestFixture]
2: public class DSTTest
3: {
4: private const string DATEFORMAT =
@"yyyyMMddHHmmss.fff[0\:G\MT]";
5:
6: public DSTTest(){}
7:
8: [Test]
9: public void TestDateInThePast()
10: {
11: DateTime myDate = DateTime.ParseExact(
"2006/03/17 11:42:33",
"yyyy/MM/dd HH:mm:ss",
CultureInfo.InvariantCulture);
12: string myDateAsString = myDate.ToUniversalTime().ToString(
DATEFORMAT, CultureInfo.InvariantCulture);
13: Assert.AreEqual("20060317194233.000[0:GMT]",
myDateAsString);
14: }
15: }
The output is interesting. Note that we're comparing strings in this case for clarity.
Output from unit test:
TestCase 'DSTTest.DSTTest.TestDateInThePast'
failed:
String lengths are both 25.
Strings differ at index 9.
expected: <"20060317194233.000[0:GMT]">
but was: <"20060317184233.000[0:GMT]">
---------------------^
c:\dsttest\dsttest.cs(19,0): at DSTTest.DSTTest.TestDateInThePast()
I first thought that this was no problem and I said to Paul:
Makes sense…that date in your test is inside the DST boundary, so we switch from -8 to -7. 11:00 becomes 18:00, rather than 19:00. We “sprung forward.”
However, Paul reminded me that we were testing a date within 2006! We're testing the 17th of March, 2006, outside of DST. Note the table below. I'd expect that day to be DST for 2007, but not 2006.
Here's where it gets really weird. Let's try the 12th of March, and try dates in 2005, 2006, and 2007. Note that when the time is 02:59:59am on March 12th, 2006, the test succeeds, but it fails at 3am, one minute later. Again, note that this is using a date in 2006, where DST started in April.
1: [Test]
2: public void TestDateIn2007Succeeds()
3: {
4: DateTime myDate = DateTime.ParseExact(
"2007/03/12 03:00:00",
"yyyy/MM/dd HH:mm:ss",
CultureInfo.InvariantCulture);
5: string myDateAsString = myDate.ToUniversalTime().ToString(
DATEFORMAT, CultureInfo.InvariantCulture);
6: Assert.AreEqual("20070312100000.000[0:GMT]",myDateAsString);
7: }
8:
9: [Test]
10: public void TestDateIn2006Succeeds()
11: {
12: DateTime myDate = DateTime.ParseExact(
"2006/03/12 02:59:59",
"yyyy/MM/dd HH:mm:ss",
CultureInfo.InvariantCulture);
13: string myDateAsString = myDate.ToUniversalTime().ToString(
DATEFORMAT, CultureInfo.InvariantCulture);
14: Assert.AreEqual("20060312105959.000[0:GMT]",myDateAsString);
15: }
16:
17: [Test]
18: public void TestDateIn2006Fails()
19: {
20: DateTime myDate = DateTime.ParseExact(
"2006/03/12 03:00:00",
"yyyy/MM/dd HH:mm:ss",
CultureInfo.InvariantCulture);
21: string myDateAsString = myDate.ToUniversalTime().ToString(
DATEFORMAT, CultureInfo.InvariantCulture);
22: Assert.AreEqual("20060312110000.000[0:GMT]",myDateAsString);
23: }
24:
25: [Test]
26: public void TestDateIn2005Succeeds()
27: {
28: DateTime myDate = DateTime.ParseExact(
"2005/03/12 03:00:00",
"yyyy/MM/dd HH:mm:ss",
CultureInfo.InvariantCulture);
29: string myDateAsString = myDate.ToUniversalTime().ToString(
DATEFORMAT, CultureInfo.InvariantCulture);
30: Assert.AreEqual("20050312110000.000[0:GMT]",myDateAsString);
31: }
32: }
33: }
What are we missing, dear reader? Is there a problem (bug?) with the registry-based Windows DST Patch?
I'm leaning towards assuming it's us, but I wanted to ask you. It seems that the data points towards this patch not working with dates in 2006. Not a huge deal, but non-trivial,IMHO.
As an aside, but very slightly related note, Steve Harman had an interesting bug where his Unit Tests were expecting to see "Tijuana" at the end of his TimeZone's Display Name. Since Mexico isn't following our lead (if it could be called a "lead") and changing their DST, so Tijuana isn't in PST proper anymore.
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
There is a TZI item that holds the information on when to change the clock. There is a place for the month and the day (if you've applied the patch, you will see a 3 and a 2 in there, indicating the 2nd Sunday of March as well as a 1 and an 11, indicating the 1st Sunday in November. I don't know what the rest represent :).
However, I don't believe it has an associated year with the setting. So, when these values are changed, the system believes that the clock has changed and will change on those days every year.
Now, whether or not the framework is trying to do some other calculations on top of this is left up to someone else to investigate.
Mark - That's odd...if that was the case, it'd be failing on ALL past dates.
Mark is right - DST boundaries have no history in versions of Windows before Vista.
http://blogs.msdn.com/oldnewthing/archive/2003/10/24/55413.aspx
So, if this is true, Kevin and Mark, and it looks like it is, then why is 2005 working?
I ran the tests on my Vista machine and I had to set my timezone to Alaskan (I'm Eastern EST) to get your test to pass...
Using 2.0 framework I ran your tests and came up with the following results:
2001=Pass
2002=Pass
2003=Pass
2004=FAIL
2005=FAIL
2006=Pass
2007=Pass
I don't want to chase a rabbit, but: What version of Windows and Framework is this running against?
"On the blog they are testing with March 12. That isn’t the best day to use, because that is right around the period when the new DST begins. In 2006 March 12 is the date while in 2005 it is not in (new) DST rules.
DST is now defined as second Sunday or March, so for 2005 that day is outside of the extended DST period.
They are correct in that there is only one definition of DST (year independent) and it applies to the past and future, so since we changed for 2007 items from the second Sunday of March to first Sunday of April is previous years could be off, because they are in the extended DST period, but that year never had it"
I'm going to have to play with these tests myself and see what I see just to make my head hurt a little more... ;)
When I posted this last comment I noticed that the date is correct, BUT the time shows 9:44:19 PM (Pacific Standard Time, UTC-08:00).
It's 5:45 PM where I'm at. In Colorado. Mountain Standard Time. 1 hour difference?
Just an observation. :p
Packing stick and rocks now.
In thinking about this, instead of using a single entry to designate DST, why not use multiple entries? This of course would require a history of changes to DST. But isn't that readily available via a Google search? A "table" of the current system timezone and all DST data regarding historical changes could easily be implemented and "popped" into place for the OS to check and create its calculations based on the current system date and use for any other dates passed to these functions.
This is obviously a simplistic view and maybe approach, and possibly not even viable for anything below a new OS due to possible dependency issues, but it makes some kind of sense to me.
</soapbox> (hopefully that wasn't interpreted as HTML... can't tell until I click Save.)
I'd bet good money that the lawmakers who decide to do things like change the DST dates have no clue what the actual ramifications will be.
Watch out folks for the other time zones around the world that have had their display names changed by this fix, and the boatload of new timezones that have been introduced to cope with new political realities, especially in Eastern Europe.
All in all, it's not simple ... and this isn't the end. There will be ongoing changes in these definitions in the years ahead. And did anybody note that there is a clause in the US Energy Policy Act that calls for evaluation in a few years, at which time we might revert to the previous definitions :-)
From what I've heard, there will be no official patch for 2000 and before. There are, however a couple of solutions for those platforms.
http://support.microsoft.com/kb/914387
http://www.intelliadmin.com/blog/2007/01/unofficial-windows-2000-daylight.html
I was just trying to analyze the same and found that:
I can see two STRINGS under EST in registry
"HKEY_lOCAL_MACHINE\SOFTWARE\MCROSOFT\WINDOWSNT\CURRENTVERSION\TIMEZONES\EASTERN STANDARD TIME\DYNAMICDST" -- 2006 AND 2007.
As per the comments mentioned in blog, DST is year independent.Can somebody explain be the significance of "2006" and "2007" in registry?I persume that for any dates before 2007 will be taken care by "2006" registry entry and any date from 2007 to future will be taken "2007".
Additionally can somebody explain the area of concerns if DST is not considering YEAR for time calculation.
Comments are closed.
It's apparently a bug by design.