[Python-checkins] r62107 - sandbox/trunk/datetime/US.py

david.goodger python-checkins at python.org
Wed Apr 2 22:14:56 CEST 2008


Author: david.goodger
Date: Wed Apr  2 22:14:56 2008
New Revision: 62107

Modified:
   sandbox/trunk/datetime/US.py
Log:
implement the new DST rules (2007 forward)

Modified: sandbox/trunk/datetime/US.py
==============================================================================
--- sandbox/trunk/datetime/US.py	(original)
+++ sandbox/trunk/datetime/US.py	Wed Apr  2 22:14:56 2008
@@ -2,15 +2,55 @@
 from datetime import tzinfo
 from datetime import timedelta
 
-from dateutil import SUNDAY, APRIL, OCTOBER, weekday_of_month
+from dateutil import SUNDAY, MARCH, APRIL, OCTOBER, NOVEMBER, weekday_of_month
 
 __all__ = ['USTimeZone', 'Eastern', 'Central', 'Mountain', 'Pacific']
 
+
+class DST_Rules(object):
+
+    """Encapsulate the rules for a DST change date."""
+
+    # Assumes that DST changes happen on Sunday mornings.
+
+    def __init__(self, month, sunday_index):
+        """
+        Parameters:
+
+        * `month`: Month of DST change (1=January, 12=December).
+        * `sunday_index`: Base-0 index of Sunday (0=first, 1=second, -1=last).
+        """
+        self.month = month
+        self.sunday_index = sunday_index
+        self.dummy_date = datetime(year=1, month=month, day=1, hour=self.hour)
+
+    def date(self, year):
+        """Return the date of the change in the given `year`."""
+        date = weekday_of_month(
+            SUNDAY, self.dummy_date.replace(year=year), self.sunday_index)
+        assert date.month == self.dummy_date.month
+        return date
+
+
+class DST_Start(DST_Rules):
+
+    # DST starts at 2am (standard time):
+    hour = 2
+
+
+class DST_End(DST_Rules):
+
+    # DST ends at 1am (standard time; 2am DST time):
+    hour = 1
+
+
 class USTimeZone(tzinfo):
-    "A class capturing the current (2002) rules for United States time zones."
-    # XXX Note that in 2007 the rules are changing:
-    # XXX 2nd Sunday in March, 1st Sunday in November.
-    # XXX Anybody want to volunteer a fix for this code?
+    """
+    A class capturing the pre- and post-2007 daylight saving time (DST)
+    rules for United States time zones.
+    """
+    # Note that in 2007 the rules changed.  The comment below refers to
+    # the old rules.  See USTimeZone.dst_rules for specific dates.
 
     # A seemingly intractable problem:  when DST ends, there's a one-hour
     # slice that repeats in "naive time".  That is, when the naive clock
@@ -49,10 +89,18 @@
 
     dstoff = timedelta(hours=1)
     zero = timedelta(0)
-    # DST starts at 2am (standard time) on the first Sunday in April.
-    start = datetime(1, APRIL, 1, 2)
-    # and ends at 2am (DST time; 1am standard time) on the last Sunday of Oct.
-    end = datetime(1, OCTOBER, 1, 1)
+
+    # [(first year, start rule, end rule), ...]
+    dst_rules = [
+        (2007,
+         DST_Start(month=MARCH, sunday_index=1),
+         DST_End(month=NOVEMBER, sunday_index=0)),
+        (1987,
+         DST_Start(month=APRIL, sunday_index=0),
+         DST_End(month=OCTOBER, sunday_index=-1)),
+        ]
+    # Ensure that the rules are in reverse chronological order:
+    dst_rules.sort(reverse=True)
 
     def __init__(self, stdhours, stdname, dstname):
         self.stdoff = timedelta(hours=stdhours)
@@ -76,13 +124,27 @@
 
         assert dt.tzinfo is self
 
-        # Find first Sunday in April.
-        start = weekday_of_month(SUNDAY, self.start.replace(year=dt.year), 0)
-        assert start.weekday() == 6 and start.month == 4 and start.day <= 7
-
-        # Find last Sunday in October.
-        end = weekday_of_month(SUNDAY, self.end.replace(year=dt.year), -1)
-        assert end.weekday() == 6 and end.month == 10 and end.day >= 25
+        for (first_year, dst_start, dst_end) in self.dst_rules:
+            if dt.year >= first_year:
+                break
+        else:
+            # As above, an exception instead may be sensible here.
+            return self.zero
+
+        start = dst_start.date(dt.year)
+        assert start.weekday() == 6
+        if dt.year >= 2007:
+            #import pdb ; pdb.set_trace()
+            assert 8 <= start.day <= 14
+        else:
+            assert start.day <= 7
+
+        end = dst_end.date(dt.year)
+        assert end.weekday() == 6
+        if dt.year >= 2007:
+            assert end.day <= 7
+        else:
+            assert end.day >= 25
 
         # Can't compare naive to aware objects, so strip the timezone from
         # dt first.
@@ -91,6 +153,7 @@
         else:
             return self.zero
 
+
 Eastern  = USTimeZone(-5, "EST", "EDT")
 Central  = USTimeZone(-6, "CST", "CDT")
 Mountain = USTimeZone(-7, "MST", "MDT")
@@ -233,6 +296,24 @@
 EST
 (2002, 10, 27, 1, 0, 0, 6, 300, 0)
 Sun Oct 27 01:00:00 2002
+
+Post-2007 rules:
+
+Right before DST starts.
+>>> before = datetime(2007, 3, 11, 1, 59, 59, tzinfo=Eastern)
+>>> printstuff(before)
+2007-03-11 01:59:59-05:00
+EST
+(2007, 3, 11, 1, 59, 59, 6, 70, 0)
+Sun Mar 11 01:59:59 2007
+
+Right when DST starts.
+>>> after = before + timedelta(seconds=1)
+>>> printstuff(after)
+2007-03-11 02:00:00-04:00
+EDT
+(2007, 3, 11, 2, 0, 0, 6, 70, 1)
+Sun Mar 11 02:00:00 2007
  """
 
 __test__ = {'brainbuster': brainbuster_test}


More information about the Python-checkins mailing list