
Hello, Currently, `bool(datetime.time(0, 0)) is False`. Can we change that to `True`? There is nothing False-y about midnight. Ram.

Ram Rachum wrote:
Of course there is -- it is the witching hour, and witches are known to be deceivers whose words and actions are false. *wink* I fear that backwards compatibility will prevent any change, but I don't see any good reasons for treating any date or time as a false value. By the way, the "To:" address on your post is set to python-ideas@googlegroups.com, which does not exist. -- Steven

I agree for the date, time and datetime classes. Having timedelta(0) be False makes sense to me, though. But see: http://bugs.python.org/issue13936 Mark

On Mon, May 7, 2012 at 11:32 AM, Antoine Pitrou <solipsis@pitrou.net> wrote:
Well, less occasional puzzlement is an improvement in itself. Unintuitive behaviour is always a risk for software quality.
I don't find the current behavior unintuitive. It is common to represent time of day as an integer (number of minutes or seconds since midnight) or as a float (fraction of the 24-hour day). In these cases one gets bool(midnight) -> False as an artifact of the representation. For someone who wants to switch from typeless time variables to datetime module types, bool(midnight) -> True may present an extra hurdle. One can improve the quality of his software by avoiding constructs that he finds unintuitive. For example, I claim that in most cases a test for bool(t) is really a lazy version of the more appropriate test for t is None. Note that if we make bool(midnight) -> True, it will not be trivial to faithfully reproduce the old behavior. I want the proponents of the change to try it before I explain why it is not easy.

On Mon, 7 May 2012 11:57:34 -0400 Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote:
I'm not sure it's common. I don't remember seeing it myself. When I use an integer or a float as you say, it's to represent a *duration*, not an absolute time.
That's part of why the integer or float representation is worse than a higher-level structure.
From a purity standpoint, you are right, but people still do it intuitively, and it works for well-behaved types. Either we try to lecture people into "the one way of writing Python code using time objects", or we make it so that common uses are not broken (i.e. a piece of code that gets wrongly executed in the rare case they encounter a midnight time object).
Note that if we make bool(midnight) -> True, it will not be trivial to faithfully reproduce the old behavior.
Why do you want to reproduce it? Does midnight warrant any special shortcut for testing? Especially one that is confusing to many readers. Regards Antoine.

On Mon, May 7, 2012 at 12:06 PM, Antoine Pitrou <solipsis@pitrou.net> wrote:
Why do you want to reproduce it?
If I am porting my software to the hypothetical Python 3.4 and see that the time.__bool__ changed, I would prefer to simply replace every occurrence of time tested for truth with something equivalent. In a porting scenario, I don't want to second guess the intent of the original programmer or "improve" the code.
Does midnight warrant any special shortcut for testing?
I never needed it, but apparently it is common enough for users to notice an complain. That's why I asked my original question: if you've seen a time variable been tested for truth, was it a bug that can be fixed by a change in time.__bool__ or a deliberate test for the midnight value?
Especially one that is confusing to many readers.
I have a feeling that "readers" here are readers of documentation or tutorials rather than readers of actual code. If this is the case, we can discuss how to improve the documentation and not change the behavior.

On Mon, 7 May 2012 12:24:21 -0400 Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote:
How so? Those users complain that midnight is false, not that they have trouble testing for midnight. That's the whole point really: they don't think about midnight as a special value, and they are surprised that it is.
Most likely it's a bug, unless the code is written by an expert in the datetime module. I don't expect many people to remember such oddities (and I don't remember them myselves), let alone willfully rely on them instead of writing more explicit code.
I was talking about readers of code. If I read code where boolean testing of a time object is done, I wouldn't assume the intent is to test for midnight (unless there's a comment indicating so). Regards Antoine.

On Mon, May 7, 2012 at 12:33 PM, Antoine Pitrou <solipsis@pitrou.net> wrote:
I understand your hypothetical, but does such code actually exist in the wild or are we debating the number of angels than can dance at midnight? Yes, if I were to rely on bool(time(0)) exact behavior, I would comment my code. Do we know one way or another whether such code exists? As a matter of coding stile, I recommend avoiding use of datetime.time objects. More often than not, time values are meaningless when detached from the date value. This is particularly true when timezone aware instances are used. Lack of support for time + timedelta makes naked timevalues inconvenient even in reasonable applications that deal with schedules that repeat from day to day. If perceived uncertainly over the truth value will further dissuade anyone from using naked time objects, I am all for it. Note that since date range starts at date(1,1,1) we don't have the same problem with the date or datetime objects.

On Mon, 7 May 2012 12:57:47 -0400 Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote:
Well, people complained about it, so they did try to write such code and got bitten, right? Whether or not such code still exists "in the wild" probably depends on how fast people get bitten and fix it :-) But regardless, it's still an annoyance for people who write new code. On the other hand, nobody chimed in to say that they relied on boolean testing to check for midnight.
I tend to agree.
Note that since date range starts at date(1,1,1) we don't have the same problem with the date or datetime objects.
That's fortunate. Regards Antoine.

On 5/7/2012 12:33 PM, Antoine Pitrou wrote:
It is only special in the representation because 24:00 == 00:00. I have the impression that European train timetables at least in decades past printed midnight arrival times as 24:00 instead of 00:00. I agree that that is an extremely thin reason for the current behavior. Someone printing timetables in that style should explicitly test arrivals for being midnight. There have been cultures that started the day at dawn or noon, and in the US at least, we still restart half-days at both noon and midnight, so both would be special here. Rather unusually, I disagree with Tim here: "It is odd, but really no odder than "zero values" of other types evaluating to false in Boolean contexts ;-)". Numerical 0 and empty collections are special and often need to be treated specially in a way that is untrue of midnight. I think treating it as special was a design mistake. There have been discussions on python-list to the effect that if one wants to branch on something being None or not, one should be explicit -- 'is None' or 'is not None' -- to avoid accidentally picking up other null values. -- Terry Jan Reedy

On 07/05/2012 18:27, Terry Reedy wrote:
My understanding is that 24:00 hours is only really used by the military to avoid misunderstanding over the actual day they're talking about. The night of 5th/6th June 1944 springs to my mind. I'll happily be corrected. -- Cheers. Mark Lawrence.

Mark Lawrence wrote:
It's in common use in Germany, e.g. for describing opening hours. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, May 07 2012)
2012-07-02: EuroPython 2012, Florence, Italy 56 days to go 2012-04-26: Released mxODBC 3.1.2 http://egenix.com/go28 2012-04-25: Released eGenix mx Base 3.2.4 http://egenix.com/go27 ::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/

On Mon, May 7, 2012 at 1:49 PM, M.-A. Lemburg <mal@egenix.com> wrote:
Properly supporting 24:00 timestamps in datetime module is actually a more interesting issue than what bool(time(0)) should be. See http://bugs.python.org/issue10427

Alexander Belopolsky wrote:
Just to clarify: 24:00 is used when describing times, but not in timestamps (those use 00:00 and the next day). E.g. it's common to write: "open 00:00-24:00" or "open 18:00-24:00". I've never seen anything like "2011-12-31 24:00" in Germany, but Google suggests that it's in common use in Asia: https://www.google.de/search?q=%222011-12-31+24:00%22 -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, May 07 2012)
2012-07-02: EuroPython 2012, Florence, Italy 56 days to go 2012-04-26: Released mxODBC 3.1.2 http://egenix.com/go28 2012-04-25: Released eGenix mx Base 3.2.4 http://egenix.com/go27 ::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/

On Tue, May 8, 2012 at 12:57 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
The behavior is broken. Midnight is not False.
Whereas I disagree - I see having zero hour be false as perfectly reasonable behaviour (not necessarily *useful*, but then having all time objects report as True in boolean context isn't particularly useful either). In matters of opinion, the status quo reigns. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Tue, May 08, 2012 at 01:57:13PM +1000, Nick Coghlan wrote:
On the contrary, it can be very useful to have all objects of some classes treated as true. For example, we can write: mo = re.search(a, b) if mo: do_something_with(mo) without having to worry about the case where a valid MatchObject happens to be false. Consider: t = get_job_start_time() # returns a datetime.time object, or None if t: do_something_with(t) Oops, we have a bug. If the job happens to have started at exactly midnight, it will wrongly be treated as false. But wait, it's worse than that. Because it's not actually midnight that gets treated as false, but some arbitrary time of the day which depends on your timezone. It's only midnight if you don't specify a tzinfo, or if you do and happen to be using GMT. Midnight (modulo timezone) is not special enough to treat it as a false value. It's not an empty container or mapping, or the identity element under addition, or the only string that contains no substrings except itself. It's just another hour. (Midnight is only special if you care about the change from one day to another. But if you care about that, you're probably using datetime objects rather than time objects, and then you don't have this problem because "midnight last Tuesday" is not treated as false.) I believe that having time(0,0) be treated as false is at best a misfeature and at worst a bug. It is as unnecessary a special case as it would be to have the string "\0" treated as false. The only good defence for keeping it, in my opinion, would be fear that there is working code that relies on this.
In matters of opinion, the status quo reigns.
That's somewhat of an exaggeration. The mere existence of a single dissenting opinion isn't enough to block all progress/changes. (Not unless it's Guido *wink*.) Consensus doesn't require every single person to agree. -- Steven

On Tue, May 8, 2012 at 1:42 AM, Steven D'Aprano <steve@pearwood.info> wrote:
For what it's worth, I am also against changing the status quo. time(0) is special: it is the smallest possible value. If you deal with low resolution time values, say hourly schedules, it is not unreasonable to test for time(0). For example, when estimating daily averages, midnight samples can be weighted by 1/2 to account for the uncertainty in assigning midnight to a given day.

Alexander Belopolsky wrote:
Testing for midnight does not require midnight to be False. And no, I don't maintain any hope of winning this argument -- that's why I wrote my own class. With it it is possible to create an unspecified moment... and guess what? It evaluates to False; all actual times evaluate as True. (Including midnight. ;) ~Ethan~

On Tue, May 08, 2012 at 02:21:07AM -0400, Alexander Belopolsky wrote:
I think this demonstrates the incidious nature of this design flaw in time objects. Alexander, you caught me in a mistake earlier, when I neglected to take tzinfo into account, and here you are doing the same sort of thing: you can't reliably detect midnight with a simple bool(timevalue) test. -- Steven

On Tue, May 8, 2012 at 3:42 PM, Steven D'Aprano <steve@pearwood.info> wrote:
IMO, you've completely misdiagnosed the source of that bug. Never *ever* rely on boolean evaluation when testing against None. *Always* use the "is not None" trailer. Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Tue, 8 May 2012 17:02:04 +1000 Nick Coghlan <ncoghlan@gmail.com> wrote:
IMO, you've completely misdiagnosed the source of that bug. Never *ever* rely on boolean evaluation when testing against None.
Nick, that's just plain silly. If we didn't want people to rely on boolean evaluation, we wouldn't define __bool__ at all (or we would make it return a random value). Regards Antoine.

The problem is not using boolean evaluation - it's assuming that boolean evaluation is defined as "x is not None". Doing so introduces a completely unnecessary dependency on the type of "x". I'm frankly astonished that so many people seem to think it's a reasonable thing to do. -- Sent from my phone, thus the relative brevity :) On May 8, 2012 8:01 PM, "Antoine Pitrou" <solipsis@pitrou.net> wrote:

Antoine Pitrou <solipsis@pitrou.net> writes:
Yet you mis-represent him, omitting the crucial qualifier “when testing against None” when you quote him as saying “we don't want people to rely on boolean evaluation”. Then you call your straw man silly. -- \ “I took it easy today. I just pretty much layed around in my | `\ underwear all day. … Got kicked out of quite a few places, | _o__) though.” —Bug-Eyed Earl, _Red Meat_ | Ben Finney

Nick Coghlan wrote:
Fully agreed. The above code is just plain wrong and often causes problems in larger applications - besides, it's also slower in most cases, esp. if determining the length of an object or converting it to a numeric value is slow. If you want to test for None return values, you need to use "if is None" or "if is not None". -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, May 08 2012)
2012-07-02: EuroPython 2012, Florence, Italy 55 days to go 2012-04-26: Released mxODBC 3.1.2 http://egenix.com/go28 2012-04-25: Released eGenix mx Base 3.2.4 http://egenix.com/go27 ::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/

On 8 May 2012 11:53, Antoine Pitrou <solipsis@pitrou.net> wrote:
I see no need - if you're testing for "true" return values, bool is correct. But if you're testing for None vs an actual value, it's not. The fact that testing for boolean true values is a lot rarer than people think, or that it's not appropriate in certain situations, doesn't mean it's useless. Paul.

On Tue, 8 May 2012 12:12:05 +0100 Paul Moore <p.f.moore@gmail.com> wrote:
Well, again, if that's the case, then __bool__ should be deprecated for all types where being "true" or "false" doesn't make obvious sense. Which is most types, actually. Of course, this is a completely vacuous discussion. The reality is that a __bool__ exists for all types, we are not deprecating it, and people rely on it even though PEP 8 zealots may recommend otherwise. Again, the question is whether time.__bool__ is sane and, if not, why not make it saner? Lecturing people on style doesn't make Python better. Regards Antoine.

On Tue, 8 May 2012 13:17:22 +0200 Antoine Pitrou <solipsis@pitrou.net> wrote:
Not quite, because "practicality beats purity". It should be "all types where there aren't obviously useful values for 'true' and 'false' in a boolean context." For container types, "not empty" provides obviously useful etc. For numeric types, "nonzero" does that. I think that covers most of the builtins. Arguably, the ability to write "if not <container>" instead of "if empty(<container>)" isn't worth the price of the all-to-common bug of writing "if not <container>" when you should be writing "if <container> is None" passing quietly instead of possibly throwing an exception. But that battle is already lost (and I prefer the current behavior anyway). For the case at hand - datetime.time() - the current behavior isn't obviously useful. If we were doing it from scratch, yeah, maybe it ought to be true all the time. Or maybe we should follow your suggestion here, and make converting a datetime.time() to bool throw an exception. But it doesn't appear to be a problem worth the cost of fixing.
PEP 8 doesn't recommend against using __bool__. It warns about the common python coding error of writing "if not x" instead of "if x is not None" when x may have a value that's both false and not None. This is a code correctness issue. Checking whether converting something to bool yields false when you're trying to see if it's some specific value that happens to convert to false is wrong. It's just as wrong to write "if not x" rather than "if len(x)" to check to see if x is an empty container if x might be None as to write "if not x" rather than "if x is not None" to check to see if x is None. The latter is the far more common bug in Python programs, which is why PEP 8 warns people about it instead of the former. <mike -- Mike Meyer <mwm@mired.org> http://www.mired.org/ Independent Software developer/SCM consultant, email for more information. O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

I think there's nothing to be done. It's clear that making datetime.time() be false was a conscious decision when the datetime module was designed (we thought about *every* aspect of the design quite a lot -- see (*) below). At the same time knowing what I know now about common usage I wouldn't design it this way today. Note that the date and datetime types don't have this problem because a zero date is invalid; and for the timedelta type having zero be false is more useful than harmful. But for time, the use case is marginal and the "trap" is real, even if it is due to poor coding style. (Good API design should consider avoiding traps for poor coders one of its goals.) However, given that it's been a feature for so long I don't think we can change it. Perhaps we could call it out more in the documentation (though it's already quite prominent). (*) I trawled through some history. The original design wiki is only accessible on the wayback machine: http://wayback.archive.org/web/20020801000000*/http://zope.org/Members/fdrak... (has many versions between 2002 and 2006). The wiki has no mention of boolean interpretation for the time type, but the earliest docs for the C implementation mentions Boolean context: http://web.archive.org/web/20030119231337/http://www.python.org/dev/doc/deve... -- --Guido van Rossum (python.org/~guido)

On Tue, May 8, 2012 at 1:11 PM, Guido van Rossum <guido@python.org> wrote:
At the same time knowing what I know now about common usage I wouldn't design it this way today.
I agree with this. Note that the latest addition to the datetime module - the timezone type is designed differently:
bool(timezone.utc) True
In many ways the timezone type is similar to time: it represents a point on a 24-hour circle. Even though the "zero" timezone is even more special than midnight, the potential for a coding mistake testing for truth instead of identity with None is even greater because None is (unfortunately) a very common value for tzinfo. I still don't think we can change bool(time(0)), though.

Antoine Pitrou wrote:
I think I lost you there. What does the above have to do with __bool__ methods ? Whether or not a type implements the notion of a boolean value is really up to the specific implementation and not a question that can be answered in general. It's perfectly fine for time value to mimic a boolean value by following the same paradigm as a float "seconds since midnight" value. As such, reusing the __nonzero__ or __len__ slots for boolean values is fine as well. It may not always make sense in every conceivable way, but as long as there is a valid explanation that can be documented, I don't see that as problem. If you're purist, you'd probably disallow __bool__ methods on non-boolean types, but this is Python, so we pass on control to object and type implementers. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, May 08 2012)
2012-07-02: EuroPython 2012, Florence, Italy 55 days to go 2012-04-26: Released mxODBC 3.1.2 http://egenix.com/go28 2012-04-25: Released eGenix mx Base 3.2.4 http://egenix.com/go27 ::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/

On 5/8/2012 7:36 AM, M.-A. Lemburg wrote:
Ah, I think this is the key to the dispute as to whether midnight should be False or True. Is the implementation of time of day as seconds since midnight essential (then midnight should be False) or accidental (then midnight should be True like all other times)? Different discussants disagree on the premise and hence the conclusion. If one first implements time-of-day as a number representing seconds from midnight, then bool(midmight) is bool(0) is False, like it or not. If one later wraps the number as a Time object, as Python did, then seconds from midnight and the specialness of midnight is essential for the new object to be a completely back-compatible drop-in replacement (with augmentations). Anyway, if 'from midnight' is part of the core concept of the class, the current behavior is correct. If one starts with time-of-day as a concept independent of linear numbers, as smoothly flowing around a circle, then making any particular time of day (or point on the circle) special seems wrong. Indeed, time of day is the same as local rotation angle with respect to the sum. So it is as much geometric as numeric. Abstractly, the second viewpoint seems correct. Pragmatically, however, civilized humans (those with clocks ;-) have standardized on local nominal midnight as the base point for numerically measuring time of day. --- We can also argue the issue both ways from the viewpoint of code compactness. False: Let t be a Time instance and midnight be Time(0). Then False midnight allows 'if t == midnight', which is needed occasionally, to be abbreviated 'if not t'. True: Let t be a Time instance or None, such as might be the return from a function just prior to testing t. Then True midnight allows 'if t is None', which may be needed on such occasions, to be abbreviated 'if not t'. While I am comfortable with and love the abbreviations for 0 and empty (and occasionally None), I would be disinclined, at least at present, to use either abbreviation for Time conditions. Typing the actual conditions above was as fast as thinking about getting the abbreviation to match correctly. --- If the stdlib had an Elevation class, we could have the same argument about whether Elevation(0) should be True, like all others, or False. -- Terry Jan Reedy

Terry Reedy wrote:
If the stdlib had an Elevation class, we could have the same argument about whether Elevation(0) should be True, like all others, or False.
I liked your explanation, Terry. For me, it comes down to the something vs. nothing argument: empty containers, the number 0 (when it represents Nothing), False, etc., are all instances of nothing. I do not see midnight as a representation of nothing. I would probably go with Something for an elevation of zero as well. But not money. ;) positive is money I have, negative is money I owe, and zero is nothing. ~Ethan~

Ethan Furman writes:
But not money. ;) positive is money I have, negative is money I owe, and zero is nothing.
Accountants and economists take exception. Unless you live on a desert island with Friday, an aggregate zero is an accident, just like a Poisson arrival at exactly midnight is an accident. On the other hand, there are zeros that are None in accounting, but they are *always* associated with a real zero (no sales of an item, no production, worker absent, ...). I have to admit I find Terry's circular reasoning[1] compelling as an ex ante argument, but looking at the clock I discover it's ex post. And it's really not that big a deal; if a factory function is documented to return None, the test *should* be "x is not None", not "bool(x)". Footnotes: [1] Sorry, Terry, I couldn't resist!

Terry Reedy wrote:
I think you have to broaden that view a bit :-) The Julian day starts noon and in other date/time concepts, the day starts at sunrise or sunrise, so it depends on the location as well as the day of the year (and various other astronomical corrections). See e.g. http://en.wikipedia.org/wiki/Day The whole date/time topic is full of mysteries, oddities and very human errors and misconceptions. It also demonstrates that there's no single right way to capture date/time. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, May 08 2012)
2012-07-02: EuroPython 2012, Florence, Italy 55 days to go 2012-04-26: Released mxODBC 3.1.2 http://egenix.com/go28 2012-04-25: Released eGenix mx Base 3.2.4 http://egenix.com/go27 ::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/

On Tue, May 08, 2012 at 01:57:32PM -0400, Terry Reedy wrote:
If we implemented times as an Hour-Minute-Second tuple would that imply that midnight was True because the tuple (0, 0, 0) is not an empty tuple? I don't think so.
Just because times can be implemented as (say) floats doesn't mean it is sensible to treat them as floats. "Square root of 3:15pm" isn't a meaningful concept.
Precisely. Especially when that special-time-of-day depends on the timezone. [...]
Even if that is correct, and I think that orthodox Jews may disagree that the day begins at midnight (even those with clocks), the datetime.time class does not fit that model. Local midnight is not necessarily false. -- Steven

Nick Coghlan wrote:
On Tue, May 8, 2012 at 3:42 PM, Steven D'Aprano <steve@pearwood.info> wrote:
and then in a follow-up post:
I am perfectly aware that None is not the only falsey value, and that bool tests are not implemented as comparisons against None. It is unfortunate that this thread has been hijacked into a argument about testing objects in a bool context, because that's not the fundamental problem. In my example code I intentionally assumed that time values don't have a false value, to show how the time values encourage buggy code. At the time I wrote that example I thought that the behaviour of time values in a bool context was undocumented, an easy mistake to make: the only documentation I have found is buried under the entry for time.tzinfo: 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. http://docs.python.org/py3k/library/datetime.html#datetime.time.tzinfo The complicated nature of this bool context should have been a clue that it might have been a bad idea. The false time value is, perhaps "unpredictable" is a little strong, but certainly surprising. If my timezone calculations are correct, the local time which is falsey for me is 10am. Anyone keen to defend having 10am local time treated as false? Also, be careful about dogmatic prohibitions like "*never* ever rely on boolean evaluation when testing against None". The equivalent comparison for re MatchObjects is unproblematic. They are explicitly documented as always being true, with the explicit aim of allowing boolean evaluation to work correctly: "Match objects always have a boolean value of True. This lets you use a simple if-statement to test whether a match was found." http://docs.python.org/py3k/library/re.html#match-objects and indeed, there are at least five instances in the standard library that use the idiom mo = re.match(blah blah) if mo: ... or similar. It is unfortunate that time values don't operate similarly. -- Steven

On Tue, May 8, 2012 at 3:42 PM, Steven D'Aprano <steve@pearwood.info> wrote:
The current behaviour is perfectly consistent and well-defined, so changing it will break any code that relies on the current behaviour. The burden is not on me to prove that there *is* such code in the wild, it's on those proposing a change to prove that there *isn't* such code (which can't be done), or else to provide a sufficiently compelling rationale that the risk of breakage can be justified. "I don't like it" is not a valid argument for a change, nor is "I like using a boolean test when I really mean an 'is not None' test". *If* such a change were to be made, it would require at least one release where a DeprecationWarning was emitted before returning False, and then the return value could change in the next release. Why bother? Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Tue, May 8, 2012 at 8:42 AM, Steven D'Aprano <steve@pearwood.info> wrote: [...]
It's only midnight if you don't specify a tzinfo, or if you do and happen to be using GMT.
Arbitrary and unexpected times evaluating to False is a bug waiting to happen. Personally I'd prefer all datetime.time objects had no boolean value at all. The only good defence for keeping it, in my opinion, would be fear that
there is working code that relies on this.
+1 Yuval

On Mon, May 7, 2012 at 12:06 PM, Antoine Pitrou <solipsis@pitrou.net> wrote:
Why do you think that 0 represents midnight? *Because* it is zero, it will often be used as a default for missing data. Python at least offers alternatives, but that doesn't mean people will use them, and certainly doesn't mean that the data wasn't already corrupted before python ever saw it. And if I know the hour but not the minute or second, I myself would generally use zeros for the missing data even in python. Saying that it represents midnight because it is defined that way is true only in the same sense that evaluating to False is correct because it is defined that way. With a sufficiently powerful time machine, I would use 1-60 to represent the minute/second being traversed and leave 0 for missing data. (And making this the right answer would probably involve going back long before python considered the issue.) Without such a time machine, none of options are good enough to justify breaking backwards compatibility. -jJ

On Tue, 8 May 2012 20:53:58 -0400 Jim Jewett <jimjjewett@gmail.com> wrote:
Well, if you've decided upfront that midnight "is zero", then you may argue it's special. But as others have shown, there's nothing obvious about midnight being "zero", especially with timezones factored in. For example, there are no binary operators where midnight is a "zero" i.e. a neutral element. Besides, we have a special value called None exactly for the purpose of representing missing data. Regards Antoine.

Alexander Belopolsky wrote:
In Python 2.x, the slot used by bool() is called nb_nonzero, which returns 1/0 depending on whether a value is considered zero or not. This makes it quite natural for any special object representing a value akin to zero in its domain to be false. In Python 3.x, nb_nonzero was renamed to nb_bool without really paying attention to the fact that many types implemented the original meaning instead of a notion of boolean value, so I guess we'll just have to live with it, unless we want to introduce yet another subtle difference between Python 2.x and 3.x. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, May 07 2012)
2012-07-02: EuroPython 2012, Florence, Italy 56 days to go 2012-04-26: Released mxODBC 3.1.2 http://egenix.com/go28 2012-04-25: Released eGenix mx Base 3.2.4 http://egenix.com/go27 ::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/

M.-A. Lemburg wrote:
I don't think it was wrong to do that. The fact that the C slot was called "nonzero" was never visible to the Python programmer, who always thought of the operation it represents as truth-testing. If there's any fault here, it's with C type implementors who have taken "nonzero" too literally. -- Greg

On Tue, May 8, 2012 at 10:32 AM, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
The Python level special method in 2.x is also __nonzero__ (or you could just implement __len__). In 3.x, the two relevant special methods are now __bool__ and __len__. Type designers are, of course, still free to use "non-zero" as their definition for how they choose to implement __bool__. For myself, I don't see any harm in having the zero hour be treated as the zero hour at the language level ("zero hour" is another term for midnight, which, as far as I know, stems from the military Zulu notation where it's written as "0000Z"). Certainly I don't see adequate justification for changing the boolean behaviour of time objects at this late date. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Alexander Belopolsky wrote:
I think you have made a good point there: the behaviour of bool(midnight) is an artifact of the internal representation. Unless this behaviour is documented, that makes it an implementation detail, and therefore lowers (but not eliminates) the barrier to changing it. [...]
I think it is easy. Instead of either of these: if bool(some_time): ... if some_time: ... write this: _MIDNIGHT = datetime.time(0, 0) # defined once if some_time != _MIDNIGHT: ... For code where some_time could be None, write this: if not (some_time is None or some_time == _MIDNIGHT): ... Have I missed any common cases? -- Steven

Alexander Belopolsky wrote:
On Mon, May 7, 2012 at 1:31 PM, Steven D'Aprano <steve@pearwood.info> wrote:
But if you set the timezone, midnight is not necessarily false. py> class GMT5(datetime.tzinfo): ... def utcoffset(self,dt): ... return timedelta(hours=5) ... def tzname(self,dt): ... return "GMT +5" ... def dst(self,dt): ... return timedelta(0) ... py> gmt5 = GMT5() py> bool(datetime.time(0,0, tzinfo=gmt5)) True py> bool(datetime.time(5, 0, tzinfo=gmt5)) False So I assume anyone using tzinfo will probably know enough not to be testing against time objects directly. Or at least not be using bool(some_some) to detect midnight. -- Steven

Ram Rachum wrote:
Of course there is -- it is the witching hour, and witches are known to be deceivers whose words and actions are false. *wink* I fear that backwards compatibility will prevent any change, but I don't see any good reasons for treating any date or time as a false value. By the way, the "To:" address on your post is set to python-ideas@googlegroups.com, which does not exist. -- Steven

I agree for the date, time and datetime classes. Having timedelta(0) be False makes sense to me, though. But see: http://bugs.python.org/issue13936 Mark

On Mon, May 7, 2012 at 11:32 AM, Antoine Pitrou <solipsis@pitrou.net> wrote:
Well, less occasional puzzlement is an improvement in itself. Unintuitive behaviour is always a risk for software quality.
I don't find the current behavior unintuitive. It is common to represent time of day as an integer (number of minutes or seconds since midnight) or as a float (fraction of the 24-hour day). In these cases one gets bool(midnight) -> False as an artifact of the representation. For someone who wants to switch from typeless time variables to datetime module types, bool(midnight) -> True may present an extra hurdle. One can improve the quality of his software by avoiding constructs that he finds unintuitive. For example, I claim that in most cases a test for bool(t) is really a lazy version of the more appropriate test for t is None. Note that if we make bool(midnight) -> True, it will not be trivial to faithfully reproduce the old behavior. I want the proponents of the change to try it before I explain why it is not easy.

On Mon, 7 May 2012 11:57:34 -0400 Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote:
I'm not sure it's common. I don't remember seeing it myself. When I use an integer or a float as you say, it's to represent a *duration*, not an absolute time.
That's part of why the integer or float representation is worse than a higher-level structure.
From a purity standpoint, you are right, but people still do it intuitively, and it works for well-behaved types. Either we try to lecture people into "the one way of writing Python code using time objects", or we make it so that common uses are not broken (i.e. a piece of code that gets wrongly executed in the rare case they encounter a midnight time object).
Note that if we make bool(midnight) -> True, it will not be trivial to faithfully reproduce the old behavior.
Why do you want to reproduce it? Does midnight warrant any special shortcut for testing? Especially one that is confusing to many readers. Regards Antoine.

On Mon, May 7, 2012 at 12:06 PM, Antoine Pitrou <solipsis@pitrou.net> wrote:
Why do you want to reproduce it?
If I am porting my software to the hypothetical Python 3.4 and see that the time.__bool__ changed, I would prefer to simply replace every occurrence of time tested for truth with something equivalent. In a porting scenario, I don't want to second guess the intent of the original programmer or "improve" the code.
Does midnight warrant any special shortcut for testing?
I never needed it, but apparently it is common enough for users to notice an complain. That's why I asked my original question: if you've seen a time variable been tested for truth, was it a bug that can be fixed by a change in time.__bool__ or a deliberate test for the midnight value?
Especially one that is confusing to many readers.
I have a feeling that "readers" here are readers of documentation or tutorials rather than readers of actual code. If this is the case, we can discuss how to improve the documentation and not change the behavior.

On Mon, 7 May 2012 12:24:21 -0400 Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote:
How so? Those users complain that midnight is false, not that they have trouble testing for midnight. That's the whole point really: they don't think about midnight as a special value, and they are surprised that it is.
Most likely it's a bug, unless the code is written by an expert in the datetime module. I don't expect many people to remember such oddities (and I don't remember them myselves), let alone willfully rely on them instead of writing more explicit code.
I was talking about readers of code. If I read code where boolean testing of a time object is done, I wouldn't assume the intent is to test for midnight (unless there's a comment indicating so). Regards Antoine.

On Mon, May 7, 2012 at 12:33 PM, Antoine Pitrou <solipsis@pitrou.net> wrote:
I understand your hypothetical, but does such code actually exist in the wild or are we debating the number of angels than can dance at midnight? Yes, if I were to rely on bool(time(0)) exact behavior, I would comment my code. Do we know one way or another whether such code exists? As a matter of coding stile, I recommend avoiding use of datetime.time objects. More often than not, time values are meaningless when detached from the date value. This is particularly true when timezone aware instances are used. Lack of support for time + timedelta makes naked timevalues inconvenient even in reasonable applications that deal with schedules that repeat from day to day. If perceived uncertainly over the truth value will further dissuade anyone from using naked time objects, I am all for it. Note that since date range starts at date(1,1,1) we don't have the same problem with the date or datetime objects.

On Mon, 7 May 2012 12:57:47 -0400 Alexander Belopolsky <alexander.belopolsky@gmail.com> wrote:
Well, people complained about it, so they did try to write such code and got bitten, right? Whether or not such code still exists "in the wild" probably depends on how fast people get bitten and fix it :-) But regardless, it's still an annoyance for people who write new code. On the other hand, nobody chimed in to say that they relied on boolean testing to check for midnight.
I tend to agree.
Note that since date range starts at date(1,1,1) we don't have the same problem with the date or datetime objects.
That's fortunate. Regards Antoine.

On 5/7/2012 12:33 PM, Antoine Pitrou wrote:
It is only special in the representation because 24:00 == 00:00. I have the impression that European train timetables at least in decades past printed midnight arrival times as 24:00 instead of 00:00. I agree that that is an extremely thin reason for the current behavior. Someone printing timetables in that style should explicitly test arrivals for being midnight. There have been cultures that started the day at dawn or noon, and in the US at least, we still restart half-days at both noon and midnight, so both would be special here. Rather unusually, I disagree with Tim here: "It is odd, but really no odder than "zero values" of other types evaluating to false in Boolean contexts ;-)". Numerical 0 and empty collections are special and often need to be treated specially in a way that is untrue of midnight. I think treating it as special was a design mistake. There have been discussions on python-list to the effect that if one wants to branch on something being None or not, one should be explicit -- 'is None' or 'is not None' -- to avoid accidentally picking up other null values. -- Terry Jan Reedy

On 07/05/2012 18:27, Terry Reedy wrote:
My understanding is that 24:00 hours is only really used by the military to avoid misunderstanding over the actual day they're talking about. The night of 5th/6th June 1944 springs to my mind. I'll happily be corrected. -- Cheers. Mark Lawrence.

Mark Lawrence wrote:
It's in common use in Germany, e.g. for describing opening hours. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, May 07 2012)
2012-07-02: EuroPython 2012, Florence, Italy 56 days to go 2012-04-26: Released mxODBC 3.1.2 http://egenix.com/go28 2012-04-25: Released eGenix mx Base 3.2.4 http://egenix.com/go27 ::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/

On Mon, May 7, 2012 at 1:49 PM, M.-A. Lemburg <mal@egenix.com> wrote:
Properly supporting 24:00 timestamps in datetime module is actually a more interesting issue than what bool(time(0)) should be. See http://bugs.python.org/issue10427

Alexander Belopolsky wrote:
Just to clarify: 24:00 is used when describing times, but not in timestamps (those use 00:00 and the next day). E.g. it's common to write: "open 00:00-24:00" or "open 18:00-24:00". I've never seen anything like "2011-12-31 24:00" in Germany, but Google suggests that it's in common use in Asia: https://www.google.de/search?q=%222011-12-31+24:00%22 -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, May 07 2012)
2012-07-02: EuroPython 2012, Florence, Italy 56 days to go 2012-04-26: Released mxODBC 3.1.2 http://egenix.com/go28 2012-04-25: Released eGenix mx Base 3.2.4 http://egenix.com/go27 ::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/

On Tue, May 8, 2012 at 12:57 PM, Ethan Furman <ethan@stoneleaf.us> wrote:
The behavior is broken. Midnight is not False.
Whereas I disagree - I see having zero hour be false as perfectly reasonable behaviour (not necessarily *useful*, but then having all time objects report as True in boolean context isn't particularly useful either). In matters of opinion, the status quo reigns. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Tue, May 08, 2012 at 01:57:13PM +1000, Nick Coghlan wrote:
On the contrary, it can be very useful to have all objects of some classes treated as true. For example, we can write: mo = re.search(a, b) if mo: do_something_with(mo) without having to worry about the case where a valid MatchObject happens to be false. Consider: t = get_job_start_time() # returns a datetime.time object, or None if t: do_something_with(t) Oops, we have a bug. If the job happens to have started at exactly midnight, it will wrongly be treated as false. But wait, it's worse than that. Because it's not actually midnight that gets treated as false, but some arbitrary time of the day which depends on your timezone. It's only midnight if you don't specify a tzinfo, or if you do and happen to be using GMT. Midnight (modulo timezone) is not special enough to treat it as a false value. It's not an empty container or mapping, or the identity element under addition, or the only string that contains no substrings except itself. It's just another hour. (Midnight is only special if you care about the change from one day to another. But if you care about that, you're probably using datetime objects rather than time objects, and then you don't have this problem because "midnight last Tuesday" is not treated as false.) I believe that having time(0,0) be treated as false is at best a misfeature and at worst a bug. It is as unnecessary a special case as it would be to have the string "\0" treated as false. The only good defence for keeping it, in my opinion, would be fear that there is working code that relies on this.
In matters of opinion, the status quo reigns.
That's somewhat of an exaggeration. The mere existence of a single dissenting opinion isn't enough to block all progress/changes. (Not unless it's Guido *wink*.) Consensus doesn't require every single person to agree. -- Steven

On Tue, May 8, 2012 at 1:42 AM, Steven D'Aprano <steve@pearwood.info> wrote:
For what it's worth, I am also against changing the status quo. time(0) is special: it is the smallest possible value. If you deal with low resolution time values, say hourly schedules, it is not unreasonable to test for time(0). For example, when estimating daily averages, midnight samples can be weighted by 1/2 to account for the uncertainty in assigning midnight to a given day.

Alexander Belopolsky wrote:
Testing for midnight does not require midnight to be False. And no, I don't maintain any hope of winning this argument -- that's why I wrote my own class. With it it is possible to create an unspecified moment... and guess what? It evaluates to False; all actual times evaluate as True. (Including midnight. ;) ~Ethan~

On Tue, May 08, 2012 at 02:21:07AM -0400, Alexander Belopolsky wrote:
I think this demonstrates the incidious nature of this design flaw in time objects. Alexander, you caught me in a mistake earlier, when I neglected to take tzinfo into account, and here you are doing the same sort of thing: you can't reliably detect midnight with a simple bool(timevalue) test. -- Steven

On Tue, May 8, 2012 at 3:42 PM, Steven D'Aprano <steve@pearwood.info> wrote:
IMO, you've completely misdiagnosed the source of that bug. Never *ever* rely on boolean evaluation when testing against None. *Always* use the "is not None" trailer. Regards, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Tue, 8 May 2012 17:02:04 +1000 Nick Coghlan <ncoghlan@gmail.com> wrote:
IMO, you've completely misdiagnosed the source of that bug. Never *ever* rely on boolean evaluation when testing against None.
Nick, that's just plain silly. If we didn't want people to rely on boolean evaluation, we wouldn't define __bool__ at all (or we would make it return a random value). Regards Antoine.

The problem is not using boolean evaluation - it's assuming that boolean evaluation is defined as "x is not None". Doing so introduces a completely unnecessary dependency on the type of "x". I'm frankly astonished that so many people seem to think it's a reasonable thing to do. -- Sent from my phone, thus the relative brevity :) On May 8, 2012 8:01 PM, "Antoine Pitrou" <solipsis@pitrou.net> wrote:

Antoine Pitrou <solipsis@pitrou.net> writes:
Yet you mis-represent him, omitting the crucial qualifier “when testing against None” when you quote him as saying “we don't want people to rely on boolean evaluation”. Then you call your straw man silly. -- \ “I took it easy today. I just pretty much layed around in my | `\ underwear all day. … Got kicked out of quite a few places, | _o__) though.” —Bug-Eyed Earl, _Red Meat_ | Ben Finney

Nick Coghlan wrote:
Fully agreed. The above code is just plain wrong and often causes problems in larger applications - besides, it's also slower in most cases, esp. if determining the length of an object or converting it to a numeric value is slow. If you want to test for None return values, you need to use "if is None" or "if is not None". -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, May 08 2012)
2012-07-02: EuroPython 2012, Florence, Italy 55 days to go 2012-04-26: Released mxODBC 3.1.2 http://egenix.com/go28 2012-04-25: Released eGenix mx Base 3.2.4 http://egenix.com/go27 ::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/

On 8 May 2012 11:53, Antoine Pitrou <solipsis@pitrou.net> wrote:
I see no need - if you're testing for "true" return values, bool is correct. But if you're testing for None vs an actual value, it's not. The fact that testing for boolean true values is a lot rarer than people think, or that it's not appropriate in certain situations, doesn't mean it's useless. Paul.

On Tue, 8 May 2012 12:12:05 +0100 Paul Moore <p.f.moore@gmail.com> wrote:
Well, again, if that's the case, then __bool__ should be deprecated for all types where being "true" or "false" doesn't make obvious sense. Which is most types, actually. Of course, this is a completely vacuous discussion. The reality is that a __bool__ exists for all types, we are not deprecating it, and people rely on it even though PEP 8 zealots may recommend otherwise. Again, the question is whether time.__bool__ is sane and, if not, why not make it saner? Lecturing people on style doesn't make Python better. Regards Antoine.

On Tue, 8 May 2012 13:17:22 +0200 Antoine Pitrou <solipsis@pitrou.net> wrote:
Not quite, because "practicality beats purity". It should be "all types where there aren't obviously useful values for 'true' and 'false' in a boolean context." For container types, "not empty" provides obviously useful etc. For numeric types, "nonzero" does that. I think that covers most of the builtins. Arguably, the ability to write "if not <container>" instead of "if empty(<container>)" isn't worth the price of the all-to-common bug of writing "if not <container>" when you should be writing "if <container> is None" passing quietly instead of possibly throwing an exception. But that battle is already lost (and I prefer the current behavior anyway). For the case at hand - datetime.time() - the current behavior isn't obviously useful. If we were doing it from scratch, yeah, maybe it ought to be true all the time. Or maybe we should follow your suggestion here, and make converting a datetime.time() to bool throw an exception. But it doesn't appear to be a problem worth the cost of fixing.
PEP 8 doesn't recommend against using __bool__. It warns about the common python coding error of writing "if not x" instead of "if x is not None" when x may have a value that's both false and not None. This is a code correctness issue. Checking whether converting something to bool yields false when you're trying to see if it's some specific value that happens to convert to false is wrong. It's just as wrong to write "if not x" rather than "if len(x)" to check to see if x is an empty container if x might be None as to write "if not x" rather than "if x is not None" to check to see if x is None. The latter is the far more common bug in Python programs, which is why PEP 8 warns people about it instead of the former. <mike -- Mike Meyer <mwm@mired.org> http://www.mired.org/ Independent Software developer/SCM consultant, email for more information. O< ascii ribbon campaign - stop html mail - www.asciiribbon.org

I think there's nothing to be done. It's clear that making datetime.time() be false was a conscious decision when the datetime module was designed (we thought about *every* aspect of the design quite a lot -- see (*) below). At the same time knowing what I know now about common usage I wouldn't design it this way today. Note that the date and datetime types don't have this problem because a zero date is invalid; and for the timedelta type having zero be false is more useful than harmful. But for time, the use case is marginal and the "trap" is real, even if it is due to poor coding style. (Good API design should consider avoiding traps for poor coders one of its goals.) However, given that it's been a feature for so long I don't think we can change it. Perhaps we could call it out more in the documentation (though it's already quite prominent). (*) I trawled through some history. The original design wiki is only accessible on the wayback machine: http://wayback.archive.org/web/20020801000000*/http://zope.org/Members/fdrak... (has many versions between 2002 and 2006). The wiki has no mention of boolean interpretation for the time type, but the earliest docs for the C implementation mentions Boolean context: http://web.archive.org/web/20030119231337/http://www.python.org/dev/doc/deve... -- --Guido van Rossum (python.org/~guido)

On Tue, May 8, 2012 at 1:11 PM, Guido van Rossum <guido@python.org> wrote:
At the same time knowing what I know now about common usage I wouldn't design it this way today.
I agree with this. Note that the latest addition to the datetime module - the timezone type is designed differently:
bool(timezone.utc) True
In many ways the timezone type is similar to time: it represents a point on a 24-hour circle. Even though the "zero" timezone is even more special than midnight, the potential for a coding mistake testing for truth instead of identity with None is even greater because None is (unfortunately) a very common value for tzinfo. I still don't think we can change bool(time(0)), though.

Antoine Pitrou wrote:
I think I lost you there. What does the above have to do with __bool__ methods ? Whether or not a type implements the notion of a boolean value is really up to the specific implementation and not a question that can be answered in general. It's perfectly fine for time value to mimic a boolean value by following the same paradigm as a float "seconds since midnight" value. As such, reusing the __nonzero__ or __len__ slots for boolean values is fine as well. It may not always make sense in every conceivable way, but as long as there is a valid explanation that can be documented, I don't see that as problem. If you're purist, you'd probably disallow __bool__ methods on non-boolean types, but this is Python, so we pass on control to object and type implementers. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, May 08 2012)
2012-07-02: EuroPython 2012, Florence, Italy 55 days to go 2012-04-26: Released mxODBC 3.1.2 http://egenix.com/go28 2012-04-25: Released eGenix mx Base 3.2.4 http://egenix.com/go27 ::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/

On 5/8/2012 7:36 AM, M.-A. Lemburg wrote:
Ah, I think this is the key to the dispute as to whether midnight should be False or True. Is the implementation of time of day as seconds since midnight essential (then midnight should be False) or accidental (then midnight should be True like all other times)? Different discussants disagree on the premise and hence the conclusion. If one first implements time-of-day as a number representing seconds from midnight, then bool(midmight) is bool(0) is False, like it or not. If one later wraps the number as a Time object, as Python did, then seconds from midnight and the specialness of midnight is essential for the new object to be a completely back-compatible drop-in replacement (with augmentations). Anyway, if 'from midnight' is part of the core concept of the class, the current behavior is correct. If one starts with time-of-day as a concept independent of linear numbers, as smoothly flowing around a circle, then making any particular time of day (or point on the circle) special seems wrong. Indeed, time of day is the same as local rotation angle with respect to the sum. So it is as much geometric as numeric. Abstractly, the second viewpoint seems correct. Pragmatically, however, civilized humans (those with clocks ;-) have standardized on local nominal midnight as the base point for numerically measuring time of day. --- We can also argue the issue both ways from the viewpoint of code compactness. False: Let t be a Time instance and midnight be Time(0). Then False midnight allows 'if t == midnight', which is needed occasionally, to be abbreviated 'if not t'. True: Let t be a Time instance or None, such as might be the return from a function just prior to testing t. Then True midnight allows 'if t is None', which may be needed on such occasions, to be abbreviated 'if not t'. While I am comfortable with and love the abbreviations for 0 and empty (and occasionally None), I would be disinclined, at least at present, to use either abbreviation for Time conditions. Typing the actual conditions above was as fast as thinking about getting the abbreviation to match correctly. --- If the stdlib had an Elevation class, we could have the same argument about whether Elevation(0) should be True, like all others, or False. -- Terry Jan Reedy

Terry Reedy wrote:
If the stdlib had an Elevation class, we could have the same argument about whether Elevation(0) should be True, like all others, or False.
I liked your explanation, Terry. For me, it comes down to the something vs. nothing argument: empty containers, the number 0 (when it represents Nothing), False, etc., are all instances of nothing. I do not see midnight as a representation of nothing. I would probably go with Something for an elevation of zero as well. But not money. ;) positive is money I have, negative is money I owe, and zero is nothing. ~Ethan~

Ethan Furman writes:
But not money. ;) positive is money I have, negative is money I owe, and zero is nothing.
Accountants and economists take exception. Unless you live on a desert island with Friday, an aggregate zero is an accident, just like a Poisson arrival at exactly midnight is an accident. On the other hand, there are zeros that are None in accounting, but they are *always* associated with a real zero (no sales of an item, no production, worker absent, ...). I have to admit I find Terry's circular reasoning[1] compelling as an ex ante argument, but looking at the clock I discover it's ex post. And it's really not that big a deal; if a factory function is documented to return None, the test *should* be "x is not None", not "bool(x)". Footnotes: [1] Sorry, Terry, I couldn't resist!

Terry Reedy wrote:
I think you have to broaden that view a bit :-) The Julian day starts noon and in other date/time concepts, the day starts at sunrise or sunrise, so it depends on the location as well as the day of the year (and various other astronomical corrections). See e.g. http://en.wikipedia.org/wiki/Day The whole date/time topic is full of mysteries, oddities and very human errors and misconceptions. It also demonstrates that there's no single right way to capture date/time. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, May 08 2012)
2012-07-02: EuroPython 2012, Florence, Italy 55 days to go 2012-04-26: Released mxODBC 3.1.2 http://egenix.com/go28 2012-04-25: Released eGenix mx Base 3.2.4 http://egenix.com/go27 ::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/

On Tue, May 08, 2012 at 01:57:32PM -0400, Terry Reedy wrote:
If we implemented times as an Hour-Minute-Second tuple would that imply that midnight was True because the tuple (0, 0, 0) is not an empty tuple? I don't think so.
Just because times can be implemented as (say) floats doesn't mean it is sensible to treat them as floats. "Square root of 3:15pm" isn't a meaningful concept.
Precisely. Especially when that special-time-of-day depends on the timezone. [...]
Even if that is correct, and I think that orthodox Jews may disagree that the day begins at midnight (even those with clocks), the datetime.time class does not fit that model. Local midnight is not necessarily false. -- Steven

Nick Coghlan wrote:
On Tue, May 8, 2012 at 3:42 PM, Steven D'Aprano <steve@pearwood.info> wrote:
and then in a follow-up post:
I am perfectly aware that None is not the only falsey value, and that bool tests are not implemented as comparisons against None. It is unfortunate that this thread has been hijacked into a argument about testing objects in a bool context, because that's not the fundamental problem. In my example code I intentionally assumed that time values don't have a false value, to show how the time values encourage buggy code. At the time I wrote that example I thought that the behaviour of time values in a bool context was undocumented, an easy mistake to make: the only documentation I have found is buried under the entry for time.tzinfo: 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. http://docs.python.org/py3k/library/datetime.html#datetime.time.tzinfo The complicated nature of this bool context should have been a clue that it might have been a bad idea. The false time value is, perhaps "unpredictable" is a little strong, but certainly surprising. If my timezone calculations are correct, the local time which is falsey for me is 10am. Anyone keen to defend having 10am local time treated as false? Also, be careful about dogmatic prohibitions like "*never* ever rely on boolean evaluation when testing against None". The equivalent comparison for re MatchObjects is unproblematic. They are explicitly documented as always being true, with the explicit aim of allowing boolean evaluation to work correctly: "Match objects always have a boolean value of True. This lets you use a simple if-statement to test whether a match was found." http://docs.python.org/py3k/library/re.html#match-objects and indeed, there are at least five instances in the standard library that use the idiom mo = re.match(blah blah) if mo: ... or similar. It is unfortunate that time values don't operate similarly. -- Steven

On Tue, May 8, 2012 at 3:42 PM, Steven D'Aprano <steve@pearwood.info> wrote:
The current behaviour is perfectly consistent and well-defined, so changing it will break any code that relies on the current behaviour. The burden is not on me to prove that there *is* such code in the wild, it's on those proposing a change to prove that there *isn't* such code (which can't be done), or else to provide a sufficiently compelling rationale that the risk of breakage can be justified. "I don't like it" is not a valid argument for a change, nor is "I like using a boolean test when I really mean an 'is not None' test". *If* such a change were to be made, it would require at least one release where a DeprecationWarning was emitted before returning False, and then the return value could change in the next release. Why bother? Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

On Tue, May 8, 2012 at 8:42 AM, Steven D'Aprano <steve@pearwood.info> wrote: [...]
It's only midnight if you don't specify a tzinfo, or if you do and happen to be using GMT.
Arbitrary and unexpected times evaluating to False is a bug waiting to happen. Personally I'd prefer all datetime.time objects had no boolean value at all. The only good defence for keeping it, in my opinion, would be fear that
there is working code that relies on this.
+1 Yuval

On Mon, May 7, 2012 at 12:06 PM, Antoine Pitrou <solipsis@pitrou.net> wrote:
Why do you think that 0 represents midnight? *Because* it is zero, it will often be used as a default for missing data. Python at least offers alternatives, but that doesn't mean people will use them, and certainly doesn't mean that the data wasn't already corrupted before python ever saw it. And if I know the hour but not the minute or second, I myself would generally use zeros for the missing data even in python. Saying that it represents midnight because it is defined that way is true only in the same sense that evaluating to False is correct because it is defined that way. With a sufficiently powerful time machine, I would use 1-60 to represent the minute/second being traversed and leave 0 for missing data. (And making this the right answer would probably involve going back long before python considered the issue.) Without such a time machine, none of options are good enough to justify breaking backwards compatibility. -jJ

On Tue, 8 May 2012 20:53:58 -0400 Jim Jewett <jimjjewett@gmail.com> wrote:
Well, if you've decided upfront that midnight "is zero", then you may argue it's special. But as others have shown, there's nothing obvious about midnight being "zero", especially with timezones factored in. For example, there are no binary operators where midnight is a "zero" i.e. a neutral element. Besides, we have a special value called None exactly for the purpose of representing missing data. Regards Antoine.

Alexander Belopolsky wrote:
In Python 2.x, the slot used by bool() is called nb_nonzero, which returns 1/0 depending on whether a value is considered zero or not. This makes it quite natural for any special object representing a value akin to zero in its domain to be false. In Python 3.x, nb_nonzero was renamed to nb_bool without really paying attention to the fact that many types implemented the original meaning instead of a notion of boolean value, so I guess we'll just have to live with it, unless we want to introduce yet another subtle difference between Python 2.x and 3.x. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, May 07 2012)
2012-07-02: EuroPython 2012, Florence, Italy 56 days to go 2012-04-26: Released mxODBC 3.1.2 http://egenix.com/go28 2012-04-25: Released eGenix mx Base 3.2.4 http://egenix.com/go27 ::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/

M.-A. Lemburg wrote:
I don't think it was wrong to do that. The fact that the C slot was called "nonzero" was never visible to the Python programmer, who always thought of the operation it represents as truth-testing. If there's any fault here, it's with C type implementors who have taken "nonzero" too literally. -- Greg

On Tue, May 8, 2012 at 10:32 AM, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
The Python level special method in 2.x is also __nonzero__ (or you could just implement __len__). In 3.x, the two relevant special methods are now __bool__ and __len__. Type designers are, of course, still free to use "non-zero" as their definition for how they choose to implement __bool__. For myself, I don't see any harm in having the zero hour be treated as the zero hour at the language level ("zero hour" is another term for midnight, which, as far as I know, stems from the military Zulu notation where it's written as "0000Z"). Certainly I don't see adequate justification for changing the boolean behaviour of time objects at this late date. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia

Alexander Belopolsky wrote:
I think you have made a good point there: the behaviour of bool(midnight) is an artifact of the internal representation. Unless this behaviour is documented, that makes it an implementation detail, and therefore lowers (but not eliminates) the barrier to changing it. [...]
I think it is easy. Instead of either of these: if bool(some_time): ... if some_time: ... write this: _MIDNIGHT = datetime.time(0, 0) # defined once if some_time != _MIDNIGHT: ... For code where some_time could be None, write this: if not (some_time is None or some_time == _MIDNIGHT): ... Have I missed any common cases? -- Steven

Alexander Belopolsky wrote:
On Mon, May 7, 2012 at 1:31 PM, Steven D'Aprano <steve@pearwood.info> wrote:
But if you set the timezone, midnight is not necessarily false. py> class GMT5(datetime.tzinfo): ... def utcoffset(self,dt): ... return timedelta(hours=5) ... def tzname(self,dt): ... return "GMT +5" ... def dst(self,dt): ... return timedelta(0) ... py> gmt5 = GMT5() py> bool(datetime.time(0,0, tzinfo=gmt5)) True py> bool(datetime.time(5, 0, tzinfo=gmt5)) False So I assume anyone using tzinfo will probably know enough not to be testing against time objects directly. Or at least not be using bool(some_some) to detect midnight. -- Steven
participants (19)
-
Alexander Belopolsky
-
Antoine Pitrou
-
Ben Finney
-
Ethan Furman
-
Georg Brandl
-
Greg Ewing
-
Guido van Rossum
-
Jim Jewett
-
M.-A. Lemburg
-
Mark Dickinson
-
Mark Lawrence
-
Mike Meyer
-
Nick Coghlan
-
Paul Moore
-
Ram Rachum
-
Stephen J. Turnbull
-
Steven D'Aprano
-
Terry Reedy
-
Yuval Greenfield