classification
Title: datetime.time(0,0,0) evaluates to False despite being a valid time
Type: behavior Stage:
Components: Library (Lib) Versions: Python 2.7, Python 2.6
process
Status: closed Resolution: invalid
Dependencies: Superseder:
Assigned To: Nosy List: Lakin.Wecker, belopolsky, eric.araujo, georg.brandl, gwrtheyrn, lemburg, r.david.murray, tim_one
Priority: normal Keywords:

Created on 2012-02-03 20:43 by Lakin.Wecker, last changed 2012-11-27 14:10 by gwrtheyrn. This issue is now closed.

Messages (12)
msg152556 - (view) Author: Lakin Wecker (Lakin.Wecker) Date: 2012-02-03 20:43
midnight is represented by datetime.time(0,0,0).  However, this time (unlike all other valid times, including datetime.time(0,0,1)) evalutes to false in if conditions:

import datetime
if datetime.time(0,0,0):
    print "datetime.time(0,0,0) is not a bug!"
else:
    print "datetime.time(0,0,0) is a bug!"

if datetime.time(0,0,1):
    print "datetime.time(0,0,1) is not a bug!"
else:
    print "datetime.time(0,0,1) is a bug!"
msg152557 - (view) Author: Lakin Wecker (Lakin.Wecker) Date: 2012-02-03 20:43
I'm updating the versions to the ones that I've actually tried it on - this is not an exhaustive search at this point.
msg152558 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2012-02-03 20:52
I don't think I would have ever thought of testing a datetime for its truth value.  But the behavior you observe is consistent with the rest of Python: 0 is false.

I wonder if this is by design or by accident.
msg152559 - (view) Author: Lakin Wecker (Lakin.Wecker) Date: 2012-02-03 20:55
Right. I've updated my code to be more correct:

instead of:

if not start_time:
    start_time = default_time

it now reads:

if start_time is None:
    start_time = default_time

which operates correctly and works fine for my case, I just thought it was odd that one time out of all of them evaluates to False.  I too wonder if it's by design or not.

It's definitely not documented if it is by design.
msg152560 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2012-02-03 21:04
It must be by design -- someone has implemented a __bool__ (formerly __nonzero__) method; otherwise all objects would be true.
msg152561 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2012-02-03 21:05
BTW, "being a valid time" is not a good argument: 0, "" or False are all valid instances of their types.
msg152562 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2012-02-03 21:09
This is by design.  I don't have a reference, but I do remember this being discussed.  Suggestions for improving the documentation are welcome.
msg152563 - (view) Author: Tim Peters (tim_one) * (Python committer) Date: 2012-02-03 21:19
From the docs, at:

http://docs.python.org/library/datetime.html#time-objects

"""
in Boolean contexts, a time object is considered to be true if and only if, after converting it to minutes and subtracting utcoffset() (or 0 if that’s None), the result is non-zero.
"""
msg152564 - (view) Author: Lakin Wecker (Lakin.Wecker) Date: 2012-02-03 21:23
Although I find it odd, you are all correct.

It is documented.  (I don't know how I missed that when I read those docs looking for that exact documentation).

It is consistent with the rest of python.

Do I close this? Do you guys? I'm fine with closing it.  Thanks and sorry for the noise.
msg152565 - (view) Author: Tim Peters (tim_one) * (Python committer) Date: 2012-02-03 21:28
It is odd, but really no odder than "zero values" of other types evaluating to false in Boolean contexts ;-)  Closing as "invalid".
msg152567 - (view) Author: Lakin Wecker (Lakin.Wecker) Date: 2012-02-03 21:30
Yeah - good point, and I agree.
msg176475 - (view) Author: Danilo Bargen (gwrtheyrn) Date: 2012-11-27 14:10
I disagree, I think this bug should be reopened and fixed.

A use case that I just ran into: I'm using a Django form with time fields that aren't required, but that are only valid in combination (if there's a open time there has to be a close time).

    if not (self.closed or (self.open_time and self.close_time )):                  
        raise ValidationError("Invalid times")

This leads to a Validation Error when using the times 12:00 and 00:00.

Of course, this case is debatable and can be worked around by using `self.open is not None and self.close is not None`, but there are even more problems when using the times inside the template.

I'm using the following widespread pattern inside my templates:

    <p>Close time: {{ close_time|default:"-" }}</p>

The "default" filter used in this case displays the string "-" if the value on the left side of the | symbol evaluates to False. That makes sense in almost all of the cases.

In the case of the `datetime.time(0, 0)` object, the default value is displayed, even though `datetime.time(0, 0).__str__()` results in a valid string (in this case '00:00:00').

(By the way, through these experiments I also found a bug in Django's date formatting template function caused by this inconsistency, which I will report separately.)

I agree that casting time objects to a boolean value doesn't make much sense. But especially because of that, inconsistencies in the resulting boolean value should NOT exist. Yet another argument for this is that in many regions midnight isn't considered 00:00, but 24:00, which would obviously not evaluate to False.

Please fix this. Otherwise it will lead to a whole lot of weird bugs in software using the datetime library.
History
Date User Action Args
2012-11-27 14:10:11gwrtheyrnsetnosy: + gwrtheyrn
messages: + msg176475
2012-05-07 18:29:13eric.araujosetnosy: + eric.araujo
2012-02-03 21:30:47Lakin.Weckersetmessages: + msg152567
2012-02-03 21:28:18tim_onesetstatus: open -> closed
resolution: invalid
messages: + msg152565
2012-02-03 21:23:40Lakin.Weckersetmessages: + msg152564
2012-02-03 21:19:49tim_onesetnosy: + tim_one
messages: + msg152563
2012-02-03 21:09:22belopolskysetmessages: + msg152562
2012-02-03 21:05:37georg.brandlsetmessages: + msg152561
2012-02-03 21:04:48georg.brandlsetnosy: + georg.brandl, lemburg
messages: + msg152560
2012-02-03 20:55:10Lakin.Weckersetmessages: + msg152559
2012-02-03 20:52:17r.david.murraysetnosy: + belopolsky
2012-02-03 20:52:01r.david.murraysetnosy: + r.david.murray
messages: + msg152558
2012-02-03 20:44:23Lakin.Weckersettype: behavior
components: + Library (Lib)
2012-02-03 20:43:43Lakin.Weckersetmessages: + msg152557
versions: + Python 2.6, Python 2.7
2012-02-03 20:43:16Lakin.Weckercreate