[PEAK] Trellis and datetime.datetime
Sergey Schetinin
maluke at gmail.com
Fri Jan 2 19:01:10 EST 2009
Hi,
I think if you really want to depend on computed datetime's timezone,
a much cleaner solution would be to use @property in such cases, i.e.
... @property
... def dt(self):
... return self.base_dt.astimezone(self.tzinfo)
On Sat, Jan 3, 2009 at 00:58, Jeffrey Harris <jeffrey at osafoundation.org> wrote:
> Happy New Year PEAKfolk!
>
> In playing with Trellis and datetimes, I've come across an issue. One
> could argue my issue is actually with datetime.datetime's __eq__ method,
> but I'd rather not have Yet Another DateTime Implementation, so...
>
> Basically, the problem is that two datetimes which represent the same
> instant in time compare as equal, even if their timezones are different.
>
> This presents a problem if you actually care about a datetime's timezone
> and you're using the Trellis. Rules that depend on datetimes won't
> always get updated if a timezone is changed.
>
> One way to work around this would be to just never have datetime valued
> Trellis cells, and instead store two cells, a timestamp and a timezone.
>
>
> Alternately, making Trellis datetime aware isn't hard (sample patch
> below). If there was an entry_point to plug-in additional equality
> tests for determining whether or not to propagate value changes, it
> wouldn't need to be so datetime specific...
>
> Here's a sample doctest to demonstrate the problem, and the patch.
>
>>>> from peak.events import trellis
>>>> from datetime import timedelta, datetime, tzinfo
>>>> class FixedOffset(tzinfo):
> ... def __init__(self, offset, name):
> ... self.__offset = timedelta(minutes = offset)
> ... self.__name = name
> ...
> ... def __repr__(self):
> ... return "<%s>" % self.__name
> ...
> ... def utcoffset(self, dt):
> ... return self.__offset
> ...
> ... def tzname(self, dt):
> ... return self.__name
> ...
> ... def dst(self, dt):
> ... return timedelta(0)
>
>>>> pacific = FixedOffset(-480, "US/Pacific")
>>>> eastern = FixedOffset(-300, "US/Eastern")
>
> Test with datetime attribute dt, and tzinfo rule computed from dt:
>
>>>> class Dated(trellis.Component):
> ... dt = trellis.attr(None)
> ...
> ... @trellis.compute
> ... def later(self):
> ... return self.dt + timedelta(hours=1)
> ...
> ... @trellis.compute
> ... def tzinfo(self):
> ... return self.dt.tzinfo
> ...
> ... @trellis.maintain
> ... def compound(self):
> ... return "OK: %s" % self.tzinfo
>
>>>> d = Dated(dt=datetime(2008,12,30,3, tzinfo=pacific))
>>>> d.tzinfo
> <US/Pacific>
>>>> d.compound
> 'OK: <US/Pacific>'
>>>> d.later
> datetime.datetime(2008, 12, 30, 4, 0, tzinfo=<US/Pacific>)
>>>> d.dt = d.dt.astimezone(eastern)
>>>> d.compound # fails
> 'OK: <US/Eastern>'
>>>> d.dt
> datetime.datetime(2008, 12, 30, 6, 0, tzinfo=<US/Eastern>)
>>>> d.later
> datetime.datetime(2008, 12, 30, 7, 0, tzinfo=<US/Eastern>)
>>>> d.dt = d.dt.astimezone(pacific)
>>>> d.tzinfo
> <US/Pacific>
>>>> d.compound
> 'OK: <US/Pacific>'
>
> Separating timezone and point-in-time into different cells:
>
>>>> class Dated2(Dated):
> ... base_dt = trellis.attr(None)
> ... tzinfo = trellis.attr(None)
> ...
> ... @trellis.compute
> ... def dt(self):
> ... return self.base_dt.astimezone(self.tzinfo)
> ...
> ... @trellis.maintain
> ... def compound(self):
> ... return "OK: %s" % self.dt
>
>>>> d2 = Dated2(base_dt=d.dt, tzinfo=pacific)
>>>> d2.dt
> datetime.datetime(2008, 12, 30, 3, 0, tzinfo=<US/Pacific>)
>>>> d2.tzinfo = eastern
>>>> d2.dt #fails
> datetime.datetime(2008, 12, 30, 6, 0, tzinfo=<US/Eastern>)
>
> -------------------------------
>
> Patch that gets the tests passing:
>
> Index: peak/events/trellis.py
> ===================================================================
> --- peak/events/trellis.py (revision 2595)
> +++ peak/events/trellis.py (working copy)
> @@ -118,6 +118,13 @@
> return who is not _sentinel and who is not self
>
>
> +def strong_eq_test(value, other):
> + if value != other: return False
> + import datetime
> + if isinstance(value, datetime.datetime):
> + if value.tzinfo != other.tzinfo:
> + return False
> + return True
>
>
>
> @@ -138,7 +145,7 @@
> if value is self._value:
> return # no change, no foul...
>
> - if value!=self._value:
> + if not strong_eq_test(value, self._value):
> if self._set_by not in (ctrl.current_listener, self):
> # already set by someone else
> raise InputConflict(self._value, value) #self._set_by)
> #, value, ctrl.current_listener) # XXX
> @@ -195,7 +202,7 @@
> on_commit(self._finish)
> else:
> value = self.rule()
> - if value!=self._value:
> + if not strong_eq_test(value, self._value):
> if self._set_by is _sentinel:
> change_attr(self, '_set_by', self)
> on_commit(self._finish)
> _______________________________________________
> PEAK mailing list
> PEAK at eby-sarna.com
> http://www.eby-sarna.com/mailman/listinfo/peak
>
--
Best Regards,
Sergey Schetinin
http://s3bk.com/ -- S3 Backup
http://word-to-html.com/ -- Word to HTML Converter
More information about the PEAK
mailing list