[Python-checkins] CVS: python/nondist/sandbox/datetime datetime.py,1.21,1.22 test_datetime.py,1.12,1.13

Tim Peters tim_one@users.sourceforge.net
Sat, 02 Mar 2002 22:11:56 -0800


Update of /cvsroot/python/python/nondist/sandbox/datetime
In directory usw-pr-cvs1:/tmp/cvs-serv5735

Modified Files:
	datetime.py test_datetime.py 
Log Message:
Get a start on a struct tm kinda-workalike that does principled, x-platform
normalization; doesn't have year 1970-2038 restrictions; and can construct
Unixish timestamps (floating seconds since 1970) for years before 1970 too.


Index: datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.py,v
retrieving revision 1.21
retrieving revision 1.22
diff -C2 -d -r1.21 -r1.22
*** datetime.py	3 Mar 2002 04:03:22 -0000	1.21
--- datetime.py	3 Mar 2002 06:11:54 -0000	1.22
***************
*** 92,95 ****
--- 92,151 ----
      return year, month, n-dbm
  
+ # This is a start at a struct tm workalike.  Goals:
+ #
+ # + Works the same way across platforms.
+ # + Handles all the fields datetime needs handled, without 1970-2038 glitches.
+ #
+ # Note:  I suspect it's best if this flavor of tm does *not* try to
+ # second-guess timezones or DST.  Instead fold whatever adjustments you want
+ # into the minutes argument (and the constructor will normalize).
+ 
+ _ORD1970 = _ymd2ord(1970, 1, 1) # base ordinal for UNIX epoch
+ 
+ class tmxxx:
+ 
+     ordinal = None
+ 
+     def __init__(self, year, month, day, hour=0, minute=0, second=0,
+                  microsecond=0):
+         # Normalize all the inputs, and store the normalized values.
+         carry, microsecond = divmod(microsecond, 1000000)
+         second += carry
+         carry, second = divmod(second, 60)
+         minute += carry
+         carry, minute = divmod(minute, 60)
+         hour += carry
+         carry, hour = divmod(hour, 24)
+         day += carry
+         # That was easy.  Now it gets muddy:  the proper range for day
+         # can't be determined without knowing the correct month and year,
+         # but if day is, e.g., plus or minus a million, the current month
+         # and year values make no sense (and may also be out of bounds
+         # themselves).
+         # Saying 12 months == 1 year should be non-controversial.
+         carry, month = divmod(month-1, 12)
+         year += carry
+         month += 1
+         assert 1 <= month <= 12
+         # Now only day can be out of bounds.  If it is, what to do is arguable,
+         # but at least the method here is principled and explainable.
+         if not 1 <= day <= _days_in_month(month, year):
+             # Note that the "if" test is for efficiency, not correctness:
+             # there's simply no need to do this dance if day is already in
+             # range, and it's an expensive dance.
+             self.ordinal = _ymd2ord(year, month, 1) + (day - 1)
+             year, month, day = _ord2ymd(self.ordinal)
+         self.year, self.month, self.day = year, month, day
+         self.hour, self.minute, self.second = hour, minute, second
+         self.microsecond = microsecond
+ 
+     def time(self):
+         "Return Unixish timestamp, as a float."
+         if self.ordinal is None:
+             self.ordinal = _ymd2ord(self.year, self.month, self.day)
+         days = self.ordinal - _ORD1970   # convert to UNIX epoch
+         seconds = ((days * 24. + self.hour)*60. + self.minute)*60.
+         return seconds + self.second + self.microsecond / 1e6
+ 
  class basetime(object):
      """Abstract date/time type.

Index: test_datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/test_datetime.py,v
retrieving revision 1.12
retrieving revision 1.13
diff -C2 -d -r1.12 -r1.13
*** test_datetime.py	3 Mar 2002 02:50:52 -0000	1.12
--- test_datetime.py	3 Mar 2002 06:11:54 -0000	1.13
***************
*** 222,247 ****
          # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
          ISO_LONG_YEARS_TABLE = """
!               4   32   60   88 
!               9   37   65   93 
!              15   43   71   99 
!              20   48   76 
               26   54   82
  
!             105  133  161  189 
!             111  139  167  195 
!             116  144  172 
!             122  150  178 
              128  156  184
  
!             201  229  257  285 
!             207  235  263  291 
!             212  240  268  296 
!             218  246  274 
              224  252  280
  
!             303  331  359  387 
!             308  336  364  392 
!             314  342  370  398 
!             320  348  376 
              325  353  381
          """
--- 222,247 ----
          # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm
          ISO_LONG_YEARS_TABLE = """
!               4   32   60   88
!               9   37   65   93
!              15   43   71   99
!              20   48   76
               26   54   82
  
!             105  133  161  189
!             111  139  167  195
!             116  144  172
!             122  150  178
              128  156  184
  
!             201  229  257  285
!             207  235  263  291
!             212  240  268  296
!             218  246  274
              224  252  280
  
!             303  331  359  387
!             308  336  364  392
!             314  342  370  398
!             320  348  376
              325  353  381
          """
***************
*** 257,260 ****
--- 257,273 ----
          self.assertEqual(L, iso_long_years)
  
+     def test_tmxxx(self):
+         from datetime import tmxxx
+         for timestamp in 123456789.0, 987654321.0:
+             dt = datetime.utcfromtimestamp(timestamp)
+             # Mange the fields, but in such a way that normalization should
+             # restore them to dt's values.
+             tm = tmxxx(dt.year - 1, dt.month + 12, dt.day + 100,
+                        dt.hour - 24*100, dt.minute - 3, dt.second + 12,
+                        (3*60 - 12) * 1000000)
+             dt2 = datetime.new(tm.year, tm.month, tm.day, tm.hour, tm.minute,
+                                tm.second, tm.microsecond, tzoffset=0)
+             self.assertEqual(dt, dt2)
+             self.assertEqual(timestamp, tm.time())
  
  def test_suite():