[Python-checkins] CVS: python/nondist/sandbox/datetime datetime.py,1.38,1.39

Tim Peters tim_one@users.sourceforge.net
Mon, 04 Mar 2002 02:17:43 -0800


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

Modified Files:
	datetime.py 
Log Message:
_ord2ymd():  Speedups, especially with an eye toward C reimplementation.
This no longer makes any external calls.  The computation of the year is
now a mirror of that in "Calendrical Calculations", but the latter's
computation of month and day is woefully inefficient.  Curiously, they
didn't seem to realize that leap-year determination comes almost for
free from the intermediates left behind from computing the year number.

Also added dummy None initial entries to the arrays indexed by month
ordinal, to avoid the "-1" adjustment when subscripting.


Index: datetime.py
===================================================================
RCS file: /cvsroot/python/python/nondist/sandbox/datetime/datetime.py,v
retrieving revision 1.38
retrieving revision 1.39
diff -C2 -d -r1.38 -r1.39
*** datetime.py	4 Mar 2002 06:10:15 -0000	1.38
--- datetime.py	4 Mar 2002 10:17:41 -0000	1.39
***************
*** 21,31 ****
  # proleptic Gregorian ordinals and many other calendar systems.
  
! _DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  
! _DAYS_BEFORE_MONTH = []
  dbm = 0
! for dim in _DAYS_IN_MONTH:
      _DAYS_BEFORE_MONTH.append(dbm)
!     dbm = dbm + dim
  del dbm, dim
  
--- 21,31 ----
  # proleptic Gregorian ordinals and many other calendar systems.
  
! _DAYS_IN_MONTH = [None, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  
! _DAYS_BEFORE_MONTH = [None]
  dbm = 0
! for dim in _DAYS_IN_MONTH[1:]:
      _DAYS_BEFORE_MONTH.append(dbm)
!     dbm += dim
  del dbm, dim
  
***************
*** 48,52 ****
      if month == 2 and _is_leap(year):
          return 29
!     return _DAYS_IN_MONTH[month-1]
  
  def _days_before_month(month, year):
--- 48,52 ----
      if month == 2 and _is_leap(year):
          return 29
!     return _DAYS_IN_MONTH[month]
  
  def _days_before_month(month, year):
***************
*** 54,58 ****
      if not 1 <= month <= 12:
          raise ValueError('month must be in 1..12', month)
!     return _DAYS_BEFORE_MONTH[month-1] + (month > 2 and _is_leap(year))
  
  def _ymd2ord(year, month, day):
--- 54,58 ----
      if not 1 <= month <= 12:
          raise ValueError('month must be in 1..12', month)
!     return _DAYS_BEFORE_MONTH[month] + (month > 2 and _is_leap(year))
  
  def _ymd2ord(year, month, day):
***************
*** 68,71 ****
--- 68,85 ----
  
  _DI400Y = _days_before_year(401)    # number of days in 400 years
+ _DI100Y = _days_before_year(101)    #    "    "   "   " 100   "
+ _DI4Y   = _days_before_year(5)      #    "    "   "   "   4   "
+ 
+ # A 4-year cycle has an extra leap day over what we'd get from pasting
+ # together 4 single years.
+ assert _DI4Y == 4 * 365 + 1
+ 
+ # Similarly, a 400-year cycle has an extra leap day over what we'd get from
+ # pasting together 4 100-year cycles.
+ assert _DI400Y == 4 * _DI100Y + 1
+ 
+ # OTOH, a 100-year cycle has one fewer leap day than we'd get from
+ # pasting together 25 4-year cycles.
+ assert _DI100Y == 25 * _DI4Y - 1
  
  def _ord2ymd(n):
***************
*** 97,123 ****
  
      # Now n is the (non-negative) offset, in days, from January 1 of year, to
!     # the desired date.  First compute how many whole years precede the
!     # desired date.  Dividing by 365 gives an upper bound quickly, and it's
!     # easy to show that it's either exact or one too large (note that this
!     # relies on that n < _DI400Y; if we did this before factoring out a close
!     # 400-year boundary, there's no bound on how far off n//365 could be).
!     whole_years_preceding = n // 365
!     exact_days = _days_before_year(whole_years_preceding + 1)
!     if exact_days > n:  # estimate is too large
!         exact_days -= _days_in_year(whole_years_preceding)
!         whole_years_preceding -= 1
!     assert 0 <= exact_days <= n
!     year += whole_years_preceding
!     n -= exact_days
  
      # Now the year is correct, and n is the offset from January 1.  We find
!     # the month via another estimate that's either exact or one too large.
      month = min(n//29 + 1, 12)
!     exact_days = _days_before_month(month, year)
!     if exact_days > n:  # estimate is too large
          month -= 1
!         exact_days -= _days_in_month(month, year)
!     assert 0 <= exact_days <= n
!     n -= exact_days
  
      # Now the year and month are correct, and n is the offset from the
--- 111,143 ----
  
      # Now n is the (non-negative) offset, in days, from January 1 of year, to
!     # the desired date.  Now compute how many 100-year cycles precede n.
!     # Note that it's possible for n100 to equal 4!  In that case 4 full
!     # 100-year cycles precede the desired day, which implies the desired
!     # day is December 31 at the end of a 400-year cycle.
!     n100, n = divmod(n, _DI100Y)
! 
!     # Now compute how many 4-year cycles precede it.
!     n4, n = divmod(n, _DI4Y)
! 
!     # And now how many single years.  Again n1 can be 4, and again meaning
!     # that the desired day is December 31 at the end of the 4-year cycle.
!     n1, n = divmod(n, 365)
! 
!     year += n100 * 100 + n4 * 4 + n1
!     if n1 == 4 or n100 == 4:
!         assert n == 0
!         return year-1, 12, 31
  
      # Now the year is correct, and n is the offset from January 1.  We find
!     # the month via an estimate that's either exact or one too large.
!     leapyear = n1 == 3 and (n4 != 24 or n100 == 3)
!     assert leapyear == _is_leap(year)
      month = min(n//29 + 1, 12)
!     preceding = _DAYS_BEFORE_MONTH[month] + (month > 2 and leapyear)
!     if preceding > n:  # estimate is too large
          month -= 1
!         preceding -= _DAYS_IN_MONTH[month] + (month == 2 and leapyear)
!     assert 0 <= preceding <= n
!     n -= preceding
  
      # Now the year and month are correct, and n is the offset from the