[Zope3-dev] Can we remove ZopeLegacy for now?

Guido van Rossum guido@python.org
Mon, 18 Mar 2002 14:01:04 -0500


> [Tim, copies Indiana rules]
> 
> [Guido]
> > I don't see the mess, unless you make the mistake of thinking of
> > Indiana as one timezone.  Of course it isn't!  There are three
> > different timezones (using the definition of timezone as "a group of
> > clocks that show the same time, by agreement"): US/Eastern,
> > US/Central, and EST.

[Tim]
> As before, I prefer to think of it as two timezones with 3 sets of
> DST rules, but I'll play along.

Sure, in many people's thinking, Arizona may be in the US/Mountain
timezone (especially if they don't notice the little footnote saying
"Arizona does not have DST" :-).  I'm curious how Arizonans think
about this, BTW.  Any Arizonans here still listening?

But I prefer to defer everything needed to calculate the UTC offset in
the "timezone info", and that includes the DST rules (heck, if it
weren't for DST rules, the tzinfo wouldn't be worth sticking in a
separate object).  And thanks for playing along.

> Note that I was specifically responding to
> your:
> 
>    even the combination of tm_zone and tm_isdst isn't enough to
>    unambiguously determine the DST transition points (unless you
>                                                       ^^^^^^^^^^
>    are in the US).
>    ^^^^^^^^^^^^^
> 
> I didn't (and don't) see any simplification here for people who are
> in the US.

I was referring to the fact that within the US, the rules that
determine the transition dates are fixed (at least for any given
year), and that the info (tz base offset, does location honor dst) is
enough to compute whether a particular given time is DST or not and
how to translate it to UTC and back (except for that one hour in
October that occurs both with and without DST).  But if you don't know
whether you're in the US, a location with the same base offset may use
a different DST ruleset.

Of course, C's struct tm doesn't record whether DST rules are in
effect at the location, just whether DST is in effect at the given
time; and since not all US locales honor DST, I agree that you still
don't know enough.  (It's a bit like some logic puzzles by Raymond
Smullyan though, in that if the DST bit is set, you know more than if
it's not set.)

> > ...
> > Let me clarify what I meant by "you don't have enough information".
> > The struct tmx stores a "time zone" which is really an offset from
> > UTC, and a "DST offset".
> >
> > Now suppose I present you with a time, say March 16, 2002, 12:21:00,
> > and I tell you the UTC offset (-5 hours) and the DST offset (zero,
> > meaning DST is not in effect on this date).  Now let's try to do some
> > date/time arithmetic.  What UTC offset and DST are in effect exactly
> > two months later?  Or exactly two months earlier?
> 
> I can't tell, and note that the answer is the same even if I'm told
> this all takes place in the US (picture two people in Indiana living
> in the same time zone (by my meaning) but under different DST rules:
> they can have the same UTC + DST offsets at one point in time, but
> not two months later).  I wasn't confused by your claim that there's
> not enough information, I was confused by your claim that the US was
> somehow exempt from ambiguity.

OK, I've withdrawn that claim, see above.

> OTOH, if you can write a tzinfo object that captures the rules for
> someone in Texas or for a specific person in Indiana, I'm sure you
> could also write a customized set of functions for them based on a
> tmx too.  The C standard doesn't supply sufficient routines on its
> own; but neither does a datetime object on its own.

Yes, but (apart from the tmx extension, which I believe is under the
library's control, not under the user's control) the struct tmx
doesn't have a place to hold a pointer to the code and state needed by
that set of functions.  You'd have to keep a separate pointer outside
the struct tmx to the data for your county in Indiana or Texas (if you
want to be able to compare dates recorded in Indiana and Texas).

The datetimetz object allows all of that state to be in the tzinfo
object, so as a user you don't need to keep track of the tzinfo object
separately.  That's all I meant.  Not very deep.

> Also note that if the offsets aren't all mushed together, I *can*
> tell that the timezone (in my sense of the word) hasn't changed (it
> never changes, barring political shenanigans).  The only thing
> that's uncertain is the DST component of the aggregate offset two
> months later.

Sure.  If DST didn't exist, we wouldn't be having this discussion,
because all we needed was a UTC offset (and there probably would even
be standardized names for all 25 UTC offsets in use, and that would be
the end of that).

> I don't know about that yet -- see earlier msg reraising your
> "chemical plant" example.

Have you seen my reply?  It's also in the TimeZoleInfo Wiki page.

> > it has a defined interface (methods tzoffset(), tzname(), and
> > tzdst(), each taking a datetime instance).  How does that work?
> > There are distinct tzinfo instances (or classes) that implement
> > the rules for Chile, US/Eastern, Equador, and so on.
> 
> Yet we can find two people living in US/Eastern who follow different
> DST rules.  It's easiest to find them by looking in Indiana <wink>.
> "US/Eastern" is simply ill-defined if it's meant to encompass *all*
> localtime information needed by people who swear they live in
> US/Eastern.

Yes.  Under that definition, "US/Eastern" is of no interest to me;
before I could give you a tzinfo object suitable for your notion of
timezone you'd have to tell me also whether or not you obey DST in
summer.

> > There's no single tzinfo object for US/Indiana; times in Indiana
> > must somehow choose the tzinfo object that applies to the county
> > where the time is taken.
> 
> Agreed.
> 
> >> The base code
> 
> > Exactly what base code?  You mean what we had before we dropped
> > tzoffset from the prototype implementation?
> 
> I mean whatever code ends up being the datetime class (as
> distiguished from the datetimetz class) -- the "base class", if you
> will.

Ah.

> >>               can't get in trouble because of this simply because
> >> our spec declines to say anything about what this number means,
> >> beyond that it's "seconds east of UTC".  I'm not sure that's
> >> enforcable-- or even meaningful --in the absence of other defined
> >> semantics.  It reads more like a hint about intent.
> 
> > I don't understand what you're trying to say here.  But that may
> > be no more than fair: you probably don't understand what I had in
> > mind for the tzinfo object, because I haven't finished writing it
> > up (still haven't).
> 
> I expect that's true.  At the time I wrote this, the Wiki said very
> little concrete about tzinfo objects.

I've lost too much context -- my base class doesn't deal in seconds
east of UTC at all, it deals in naive time, which explicitly denies
knowledge of a UTC offset.  So I still don't understand what you were
trying to say, but it probably isn't important.  See if the new Wiki
is still ambiguous, and if so, if it's a matter of clarifying language
or whether there's a real semantic trap hidden somewhere still.

> > Yeah, you learn a lot of nonsense in grade school.  As became
> > clear in other messages in this thread, the word "timezone" means
> > many things to many people.
> 
> That's why I find it clarifying to break it into distinct
> components.  Then it becomes possible to speak with precision, or at
> least as much precision as politicians and religious authorities can
> bear ...

I'd be happy to look for a term that's less loaded than "timezone".
I'm very clear on the *concept* I want a tzinfo object to capture: the
base timezone and the full set of DST rules for a particular set of
clocks whose owners have agreed they'll keep them synchronized.

> For most of the rest, I'll do us both a favor and wait for the Wiki
> to catch up with your thoughts.  Heck, if I can channel them, I'll
> even help <wink>.

Have another look.

> > ...
> > And I suppose if there's a use case for asking a datetimetz object
> > whether DST is in effect, that question, too, is passed on to the
> > tzinfo object.
> 
> My own use case for that is discoverability: if a tzinfo object
> claims to incorporate knowledge of DST rules, I'd like a direct way
> to query it about what it believes at any given time (preferably not
> just "yes" or "no", but a signed duration, as in a tmx and as in
> ECMAScript).  Since DST is such a mess, discoverability is a
> debugging tool for when things go wrong.

Yes.  But note that *conceivably* (e.g. in the "zigzag" proposal,
which I mistakenly called sawtooth but which really is triangle
shaped) the UTC offset is different each day of the year, and then it
may be hard to distinguish a base offset and a DST offset except
arbitrarily.  IIRC, Saudi Arabia used to be on "solar time", which I
believe means their clocks always show 12 noon exactly when the sun is
at its highest point of the day.  Easy enough to compute the UTC
offset (given the exact latitude and longitude).  Hard to give DST
offset a meaning.

> >>> Sample tzinfo classes:
> >>>
> >>>        class UTC:
> >>>            "UTC"
> >>>           def tzoffset(self, dt):
> >>>                return 0
> >>>           def tzname(self, dt):
> >>>                return "UTC"
> 
> >> I assume it's just as valid to define this as, e.g.,
> >>
> >>         class UTC:
> >>             "UTC"
> >>            def tzoffset(self, dt):
> >>                 return -42137    # ONLY CHANGE IS HERE
> >>            def tzname(self, dt):
> >>                 return "UTC"
> >>
> >> If it's not, scream at me, because then there's some hidden
> >> assumption about the relationship between naive time and UTC.  Or is
> >> the rest of the spec going to change to say that naive time *is* UTC
> >> (but stripped of leap secconds)?  That assumption seems to underlie
> >> all the sample classes: is it a required assumption, or just a
> >> convenient assumption for purposes of illustration?
> 
> > SCREAM!!!  Returning -42137 is *not* the same as returning zero!!!
> 
> Of course it isn't, and that's the point.  Up until now, you've said:
> 
>     [from <http://www.zope.org/Members/fdrake/DateTimeWiki/NaiveTime>]
>     Whether a datetime object represents UTC, local time, or time in
>     some other timezone is purely up to the program; just like it's up
>     to the program whether a particular number represents meters, miles,
>     or mass.
> 
> If you're now telling me that I'm not allowed to pretend a datetime
> object is 42137 seconds removed from UTC, then the nature of "naive
> time" has changed.  [Oops: I see the defn of tzoffset() has changed
> to minutes now -- whatever.]

I've also changed the Wiki to clarify that tz.utcoffset(dt) [new name
too] requires that dt.tzinfo() is the same as tz.  As a special case,
a naive datetime or a datetimetz whose tzinfo() is None are also
allowed, and then the tz object imposes an interpretation -- this
should only be used when you know that a particular naive time is
really meant to be interpreted using that tz.  E.g. when you store UTC
times as naive times, which is a perfectly legal use of naive times,
you may want to pass a UTC instance along to some other routine that
needs to be told the tzinfo explicitly.

> Or does "dt" in the example not refer to an arbitrary datetime
> object, but only to a (subclass of) datetimetz?  Even then, I should
> be able to make up any silly rules I want <wink>.

I thought my intention was perfectly clear, but apparently not.

A datetimetz object with tz=None is the same as a datetime object --
it represents naive time, and has no defined UTC offset.

A datetimetz object with tz!=None should always be understood as
representing local time according to the tz object.

> > I'm not sure where the misunderstanding is.  Naive datetime
> > doesn't have an explicit timezone, but it could have an implied
> > timezone that the application knows.
> 
> Presumably my application wants to believe a naive datetime is 42137
> sec^H^H^H minutes off from UTC.  In that case I believe my app's
> version of a UTC tzinfo object must return +-42137 when a naive
> datetime is passed to its tzoffset() method.

I think you've got it backwards.  You would indeed use a tz object
that returns 42137, but you shouldn't call that tz object UTC, since
your naive time is not recorded in UTC.

I'm not sure where the confusion lies.  It seems that you thought that
the different tz objects can be used to convert any given datetime
object to any given timezone.  That's not the intention: the tz
objects exist to label datetimetz objects as meaning local time in a
specific "timezone".  I hope I've clarified it by now rather than
confusing you even more.

> > The datetimetz class is intended to make the timezone explicit.
> 
> Sure.  And if I, e.g., live in a DST-free part of the world, and
> choose to believe that a naive datetime *is* my local time, then my
> version of a UTC tzinfo object is going to have to take my belief
> into account.

Ah, indeed, there's the same confusion again.  You wouldn't change the
UTC tzinfo object.  You'd have a very simple tzinfo object that labels
your datetimetz objects as your local time (probably an instance of
the FixedOffset class from the Wiki).  To calculate UTC, you'd call
your tzinfo object's utcoffset() method, which would yield the offset
between UTC and your timezone.

> An alternative (and probably saner <wink>) is to stop saying that
> "naive time" makes no assumptions, but that when combined with a
> tzinfo object it specifically assumes UTC stripped of leap seconds,
> and users have no choice about that.  Then your example classes are
> compelling.

No, not at all.

Ah, I just realize what you must've thought all along.  You must've
thought that datetimetz stores the broken-out date and time in UTC,
and that the tzinfo object is needed to compute local time.  One other
reader read it this way, at least.  But my plan is to continue to
store local time, and use the tzinfo object to calculate UTC from
that.

Does it make more sense now?  I realize that storing local time allows
ambiguities -- around the transition out of DST, there are two local
times mapping to different UTC times.  I don't care enough to change;
as I say at the very end of the Wiki, """I really don't want to store
the time in UTC. Here's why: Field extraction (dt.year, dt.hour, etc.)
usually wants the local time fields, and if we stored the broken-out
time in UTC we'd end up doing the full UTC offset calculation on each
field extraction, thereby defeating the whole point of storing the
broken-out fields."""

> > When I create a datetimetz object, I pass in a tzinfo object that
> > represents the timezone semantics I want to use for this
> > datetimetz object.  So if I am going to record times in UTC, I
> > pass an instance (or maybe *the* instance :-) of the UTC class.
> 
> If your app chooses to believe that "naive" datetimes are at an
> offset of 0 from UTC, or the spec changes to *require* that belief
> when using tzinfo objects, fine by me.  The sample UTC class doesn't
> make sense unless that's what you do choose (or are forced) to
> believe.

Again, confusion caused by reversal of the assumptions, see above.

> ...
> >> BTW, I'm delighted to punt the nasty issues into the court of a
> >> vague object.
> 
> > I object to calling the tzoffset object vague.  It's got a precise
> > interface.
> 
> Vague wasn't meant as pejorative.  Indeed, the strength of a tzinfo
> relies on what it *doesn't* specify.  I meant "vague" only in the
> sense of "unspecified".  Freedom of speech is also vague, and I'm
> not opposed to it either <wink>.

So let's call it a Strategy pattern.

--Guido van Rossum (home page: http://www.python.org/~guido/)