Re: Trellis and datetime.datetime
"Sergey Schetinin" <[email protected]> Sat, 3 Jan 2009 02:01:10 +0200
| Newsgroups | gmane.comp.python.peak |
|---|---|
| Message-ID | <[email protected]> |
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-6zGkXsw2EZWGJGYlWa3Ukdi2O/[email protected]> 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 > [email protected] > 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