[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