Why are timezone aware and naive datetimes not distinct classes?

R

Roy Smith

To make a long (and painful) story short, I've got a (large) list of
datetimes, and was getting some bizarre errors working with it. One of
the things I tried while debugging the problem was verifying that all
the elements of the list were indeed datetimes:

In [59]: set(type(foo) for foo in x)
Out[59]: set([datetime.datetime])

Well, it turns out, one of them was a timezone-aware datetime, and all
the others were naive! I finally figured it out when I tried

max(x)

and got

TypeError: can't compare offset-naive and offset-aware datetimes

So, the question is, WHY aren't aware and naive datetimes separate
classes? They share many attributes and methods, but not all. It seems
like they should both be subclasses of some common BaseDatetime. Was
not splitting them into two classes a deliberate design decision?
 
N

Nobody

So, the question is, WHY aren't aware and naive datetimes separate
classes? They share many attributes and methods, but not all.

They share all attributes and methods.

You could just as well ask why positive and negative floats aren't
separate classes (based upon the fact that e.g. math.sqrt() and math.log()
raise domain errors for negative floats).

Just because certain operations aren't defined for all values (or all
combinations of values), that doesn't make the values where those
operations aren't defined into a distinct type.
 
C

Chris Angelico

They share all attributes and methods.

You could just as well ask why positive and negative floats aren't
separate classes (based upon the fact that e.g. math.sqrt() and math.log()
raise domain errors for negative floats).

Just because certain operations aren't defined for all values (or all
combinations of values), that doesn't make the values where those
operations aren't defined into a distinct type.

They represent different things, though. A naive datetime is kinda
like an integer - an abstract thing that has meaning only as the
programmer gives it meaning; an aware datetime unambiguously
represents an instant in time. SQL has completely different data types
for "TIMESTAMP WITHOUT TIME ZONE" and "TIMESTAMP WITH TIME ZONE".

As I see it, a naive datetime simply does not have a timezone. The
fact that it may have an attribute that's set to None doesn't mean
that it "shares all attributes" with aware datetimes; if they were
implemented completely separately (without even a common base class),
the most obvious difference would be the complete absence of timezone
attribute on the naive type.

Conceptually, Roy's notion of a common base class makes good sense.
But I can't speak for the Python datetime module as I've hardly used
it; there may be practical considerations that mean the current model
works better. All I can say is, there's definitely more difference
between aware and naive datetimes than there is between positive and
negative floats.

ChrisA
 
N

Nobody

As I see it, a naive datetime simply does not have a timezone.

The distinction between aware and naive isn't whether the .tzinfo member
is None, but whether self.utcoffset() returns None (which can occur either
if self.tzinfo is None or if self.tzinfo.utcoffset(self) returns None).

IOW, an "aware" time/datetime can be converted to a UTC time/datetime, a
"naive" one can't, although it can still have a timezone which isn't
referenced to UTC.

The distinction may actually matter for times in the far future, as you
can't reliably predict how or when timezone defintions will change.
Converting a future datetime to UTC based upon the current timezone rules
is at best an estimate. For this reason, "appointments" should always be
kept in local time, so that you don't get errors if the timezone rules
(or the timezone) change between the time an appointment is made and the
time it occurs.
 
R

Roy Smith

Nobody said:
The distinction may actually matter for times in the far future, as you
can't reliably predict how or when timezone defintions will change.

Sadly, this is true.
For this reason, "appointments" should always be
kept in local time, so that you don't get errors if the timezone rules
(or the timezone) change between the time an appointment is made and the
time it occurs.

Well, sort of. Future scheduled activities (which I assume is what you
mean by "appointments") should be kept in whatever timezone makes sense
for that activity. For example, I'm on NASA's mailing list to receive
alerts when the ISS is going to be visible to the naked eye at my
location. The last one said:
Time: Wed Feb 27 6:17 PM, Visible: 4 min, Max Height: 54 degrees,
Appears: W, Disappears: SE

If I wanted to put that on my calendar, I really should convert it to
UTC and record it that way. If the folks who mess with these things
were to suddenly decide to move when DST starts, the space station isn't
going to adjust its orbit to accommodate that.

Likewise, let's say I make an appointment to have a phone call with
somebody in Caliornia (I'm in New York) at some future date. I put on
my calendar "Call with Joe, 5 PM". Joe puts on his, "Call with Roy, 2
PM". The idiots in Albany and/or Sacramento decide to mess with the
timezones and one state changes the rules. We're going to miss our
phone call. Had we both recorded the arranged time in UTC, we'd get to
talk to each other.
 
C

Chris Angelico

The distinction between aware and naive isn't whether the .tzinfo member
is None, but whether self.utcoffset() returns None (which can occur either
if self.tzinfo is None or if self.tzinfo.utcoffset(self) returns None).

IOW, an "aware" time/datetime can be converted to a UTC time/datetime, a
"naive" one can't, although it can still have a timezone which isn't
referenced to UTC.

I stand corrected. Though the distinction I made in my first paragraph
is still valid: an aware datetime represents an instant in time, a
naive one is a tagged piece of arbitrary data.

Future scheduled activities (which I assume is what you
mean by "appointments") should be kept in whatever timezone makes sense
for that activity.

Agreed; and in some specific circumstances, it's even possible to
fight the whole mess of timezones. My online Dungeons and Dragons
group schedules everything on UTC - for example, the server quotes the
current time as Sat 22:26, and I have a session at Sun 02:00, so
anyone can figure out how long until session without worrying about
timezones or Daylight Robbery Time. Of course, that _does_ come at a
cost; mainly it's the USA-based players who get confused (which
surprises me somewhat, tbh).

ChrisA
 
N

Nobody

Future scheduled activities (which I assume is what you
mean by "appointments") should be kept in whatever timezone makes sense
for that activity. For example, I'm on NASA's mailing list to receive
alerts when the ISS is going to be visible to the naked eye at my
location. The last one said:


If I wanted to put that on my calendar, I really should convert it to
UTC and record it that way.

Indeed; that is an appropriate use of UTC.
Likewise, let's say I make an appointment to have a phone call with
somebody in Caliornia (I'm in New York) at some future date. I put on
my calendar "Call with Joe, 5 PM". Joe puts on his, "Call with Roy, 2
PM". The idiots in Albany and/or Sacramento decide to mess with the
timezones and one state changes the rules. We're going to miss our
phone call. Had we both recorded the arranged time in UTC, we'd get to
talk to each other.

Maybe. But if the time was chosen so as not to conflict with other
activites, changes to timezone(s) will often result in corresponding
changes to schedules, meaning that one or both parties will no longer be
available at the original UTC time (or even at any time, if both have busy
schedules).

Which is why "future scheduled activities" which require multiple people
to physically meet at a specific place and time are normally scheduled
in terms of "the /de jure/ timezone of that location, as applicable on
that date", rather than in UTC or even a named timezone.

Similarly, the policies of a corporation which operates in multiple
timezones within a single country may dictate times which are common (in
HH:MM terms) to all locations but which are interpreted according to each
location's /de jure/ timezone (e.g. fast food chains' "breakfast" menus).
 
A

Adam Tauno Williams

To make a long (and painful) story short, I've got a (large) list of
datetimes, and was getting some bizarre errors working with it. One of
the things I tried while debugging the problem was verifying that all
the elements of the list were indeed datetimes:
In [59]: set(type(foo) for foo in x)
Out[59]: set([datetime.datetime]

Because date/time management in Python is *@*&@R&*(R *@&Y terrible!
Period, full-stop, awful, crappy, lousy, and aggravating. The design is
haphazard and error inducing.

Pass through the list once and convert them all the UTC [or I suppose,
make them all naive].

BTW, this page is a life and sanity saver:

<http://taaviburns.ca/what_you_need_to_know_about_datetimes/datetime_transforms.html>

And one of my own tricks is posted here:

Well, it turns out, one of them was a timezone-aware datetime, and all
the others were naive! I finally figured it out when I tried

Welcome!
 
D

djc

Because date/time management in Python is *@*&@R&*(R *@&Y terrible!
Period, full-stop, awful, crappy, lousy, and aggravating. The design is
haphazard and error inducing.

+1
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top