# HG changeset patch # User Paul Boddie # Date 1332713662 -7200 # Node ID b8f1a2cc8866491f6cac60bc660d747551af14ec # Parent fe647b8e49e4dabaa63aaafa104cb3ccc211e5e1 Fixed the Olson zone offset calculation to produce correct offsets for zones west of the prime meridian in a form consistent with those generated using explicit offsets (where the sign of the hours and minutes is always the same). Changed the subtraction of dates, months and non-time objects to produce the actual difference between those objects. Added explicit subtraction support for times. Added some tests. diff -r fe647b8e49e4 -r b8f1a2cc8866 DateSupport.py --- a/DateSupport.py Sun Mar 25 21:19:17 2012 +0200 +++ b/DateSupport.py Mon Mar 26 00:14:22 2012 +0200 @@ -29,7 +29,7 @@ month_regexp_str = ur'(?P[0-9]{4})-(?P[0-9]{2})' date_regexp_str = ur'(?P[0-9]{4})-(?P[0-9]{2})-(?P[0-9]{2})' time_regexp_str = ur'(?P[0-2][0-9]):(?P[0-5][0-9])(?::(?P[0-6][0-9]))?' -timezone_offset_str = ur'(?P(UTC)?(?:(?P[-+])(?P[0-9]{2})(?::?(?P[0-9]{2}))?))' +timezone_offset_str = ur'(?P(UTC)?(?:(?P[-+])?(?P[0-9]{2})(?::?(?P[0-9]{2}))?))' timezone_olson_str = ur'(?P[a-zA-Z]+(?:/[-_a-zA-Z]+){1,2})' timezone_utc_str = ur'UTC' timezone_regexp_str = ur'(?P' + timezone_offset_str + '|' + timezone_olson_str + '|' + timezone_utc_str + ')' @@ -56,6 +56,12 @@ # Utility functions. +def sign(x): + if x < 0: + return -1 + else: + return 1 + def int_or_none(x): if x is None: return x @@ -164,10 +170,12 @@ else: data = self.as_tuple() other_data = other.as_tuple() + direction = self < other and 1 or -1 + if len(data) < len(other_data): - return len(self.until(other)) + return (len(self.until(other)) - 1) * direction else: - return len(other.until(self)) + return (len(other.until(self)) - 1) * -direction def _until(self, start, end, nextfn, prevfn): @@ -442,6 +450,25 @@ return Date.__cmp__(this, other) + def __sub__(self, other): + + """ + Return the difference between this object and the 'other' object at the + highest common accuracy of both objects. + """ + + if not isinstance(other, Temporal): + return NotImplemented + elif not other.has_time(): + return self.as_date() - other + else: + utc = self.to_utc() + other = other.to_utc() + days = utc.as_date() - other.as_date() + h1, m1, s1 = utc.as_tuple()[3:6] + h2, m2, s2 = other.as_tuple()[3:6] + return days * 24 * 3600 + (h1 - h2) * 3600 + (m1 - m2) * 60 + s1 - s2 + def has_time(self): """ @@ -546,12 +573,12 @@ match = timezone_offset_regexp.match(zone) if match: if match.group("sign") == "-": - sign = -1 + offset_sign = -1 else: - sign = 1 + offset_sign = 1 - hours = int(match.group("hours")) * sign - minutes = int(match.group("minutes") or 0) * sign + hours = int(match.group("hours")) * offset_sign + minutes = int(match.group("minutes") or 0) * offset_sign return hours, minutes # Attempt to handle Olson time zone identifiers. @@ -559,9 +586,9 @@ dt = self.as_olson_datetime() if dt: seconds = dt.utcoffset().seconds + dt.utcoffset().days * 24 * 3600 - hours = seconds / 3600 - minutes = (seconds % 3600) / 60 - return hours, minutes + hours = abs(seconds) / 3600 + minutes = (abs(seconds) % 3600) / 60 + return sign(seconds) * hours, sign(seconds) * minutes # Otherwise return None. diff -r fe647b8e49e4 -r b8f1a2cc8866 tests/test_dates.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_dates.py Mon Mar 26 00:14:22 2012 +0200 @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +from DateSupport import * + +d1 = DateTime((2012, 3, 25, 20, 45, 30, "Europe/Oslo")) +d2 = DateTime((2012, 3, 25, 21, 05, 25, "Europe/Oslo")) +ts1 = Timespan(d1, d1) +ts2 = Timespan(d1, d2) + +# expected result operands +print "%r : %r <- %r < %r" % (d1 < d2, d1 < d2, d1, d2) +print "%r : %r <- %r == %r" % (not d1 == d2, d1 == d2, d1, d2) +print "%r : %r <- %r < %r" % (ts1 < ts2, ts1 < ts2, ts1, ts2) +print "%r : %r <- %r == %r" % (not ts1 == ts2, ts1 == ts2, ts1, ts2) + +d2_offset = d2.utc_offset() + +# expected result operands +print "%r : %r <- %r.utc_offset()" % (d2_offset == (2, 0), d2_offset, d2) + +d3 = DateTime((2012, 3, 25, 21, 05, 25, "America/Montreal")) +d4 = DateTime((2012, 3, 25, 21, 05, 25, "America/Anchorage")) +d5 = DateTime((2012, 3, 25, 21, 05, 25, "America/St_Johns")) +d6 = DateTime((2012, 3, 25, 21, 05, 25, "America/Vancouver")) +d7 = DateTime((2012, 3, 25, 21, 05, 25, "-05:45")) +d8 = DateTime((2012, 3, 25, 21, 05, 25, "05:45")) + +# expected result operands +print "%r : %r <- %r < %r" % (d2 < d3, d2 < d3, d2, d3) +print "%r : %r <- %r < %r" % (d3 < d4, d3 < d4, d3, d4) +print "%r : %r <- %r > %r" % (d4 > d5, d4 > d5, d4, d5) +print "%r : %r <- %r > %r" % (not d5 > d6, d5 > d6, d5, d6) +print "%r : %r <- %r > %r" % (d6 > d7, d6 > d7, d6, d7) +print "%r : %r <- %r > %r" % (d7 > d8, d7 > d8, d7, d8) + +d3_offset = d3.utc_offset() +d4_offset = d4.utc_offset() +d5_offset = d5.utc_offset() +d6_offset = d6.utc_offset() +d7_offset = d7.utc_offset() +d8_offset = d8.utc_offset() + +# expected result operands +print "%r : %r <- %r.utc_offset()" % (d3_offset == (-4, 0), d3_offset, d3) +print "%r : %r <- %r.utc_offset()" % (d4_offset == (-8, 0), d4_offset, d4) +print "%r : %r <- %r.utc_offset()" % (d5_offset == (-2, -30), d5_offset, d5) +print "%r : %r <- %r.utc_offset()" % (d6_offset == (-7, 0), d6_offset, d6) +print "%r : %r <- %r.utc_offset()" % (d7_offset == (-5, -45), d7_offset, d7) +print "%r : %r <- %r.utc_offset()" % (d8_offset == (5, 45), d8_offset, d8) + +# expected result operands +print "%r : %r <- %r - %r" % (d2 - d2 == 0, d2 - d2, d2, d2) +print "%r : %r <- %r - %r" % (d3 - d2 == 6 * 3600, d3 - d2, d3, d2) +print "%r : %r <- %r - %r" % (d4 - d2 == 10 * 3600, d4 - d2, d4, d2) +print "%r : %r <- %r - %r" % (d5 - d2 == 4.5 * 3600, d5 - d2, d5, d2) +print "%r : %r <- %r - %r" % (d6 - d2 == 9 * 3600, d6 - d2, d6, d2) +print "%r : %r <- %r - %r" % (d7 - d2 == 7.75 * 3600, d7 - d2, d7, d2) +print "%r : %r <- %r - %r" % (d8 - d2 == -3.75 * 3600, d8 - d2, d8, d2) + +# vim: tabstop=4 expandtab shiftwidth=4