classification
Title: 24:00 Hour in DateTime
Type: enhancement Stage: resolved
Components: Extension Modules, Library (Lib) Versions: Python 3.3
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: belopolsky Nosy List: belopolsky, cvrebert, ezio.melotti, ingo.janssen, mark.dickinson, merwok, rbp
Priority: normal Keywords:

Created on 2010-11-15 14:37 by ingo.janssen, last changed 2013-08-04 19:50 by belopolsky. This issue is now closed.

Messages (9)
msg121230 - (view) Author: ingo janssen (ingo.janssen) Date: 2010-11-15 14:37
Short:
make the DateTime class and related also accept 24 for the hour instead of stopping at 23:59:59.

from the python doc:

"class datetime.datetime(year, month, day[, hour[, minute[, second[,
microsecond[, tzinfo]]]]])
The year, month and day arguments are required. tzinfo may be None, or
an instance of a tzinfo subclass. The remaining arguments may be ints
or longs, in the following ranges:
[...]
0 <= hour < 24
[...]
If an argument outside those ranges is given, ValueError is raised."


from http://en.wikipedia.org/wiki/ISO_8601 :

"ISO 8601 uses the 24-hour clock system. The basic format is
[hh][mm][ss] and the extended format is [hh]:[mm]:[ss].

* [hh] refers to a zero-padded hour between 00 and 24 (where 24 is
only used to notate midnight at the end of a calendar day).
[...]
Midnight is a special case and can be referred to as both "00:00" and
"24:00". The notation "00:00" is used at the beginning of a calendar
day and is the more frequently used. At the end of a day use "24:00".
Note that "2007-04-05T24:00" is the same instant as "2007-04-06T00:00"
(see Combined date and time representations below)."

The use of 24:00 is very comfortable when using hourly datasets, the
first set of a day is saved under 1:00, the fifth (4:00 to 5:00) under
5:00 and the last (23:00 - 24:00) under 24:00. No need to suddenly use
23:59:59 or 0:00 the next day. 

Actually in another part of Python SQLlite's date and time functions accept and outputs the 24:00. Adding some Python to an existing database made me aware of the problem.
msg121234 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2010-11-15 15:24
+1

Note that in Python, semi-open intervals are favored, but specifying the last hour of the day is awkward when using datetime (as OP mentioned) and impossible using just time.  Using closed intervals is not a good work-around in many cases because it requires the user to be explicit about precision:  is the last hour [23:00-23:59], [23:00:00-23:59:00], or [23:00:00.999999-23:59:00.999999]?

I offer to write the patch for the C implementation if someone comes up with a patch for datetime.py including tests.
msg121956 - (view) Author: Rodrigo Bernardo Pimentel (rbp) (Python committer) Date: 2010-11-21 18:24
I was writing tests for this issue, when something struck me: ok, datetime(year, month, day, 24) is valid. But is datetime(year, month, day, 24, 1) valid? Or datetime(year, month, day, 24, 0, 0, 1)?

I would say those aren't valid, although that makes checking forvalid hour values slightly weird (as in not straightforward): 24 is valid if minutes, seconds and microseconds are all 0, but invalid otherwise.

What do you think?
msg121967 - (view) Author: ingo janssen (ingo.janssen) Date: 2010-11-21 19:18
On Sun, Nov 21, 2010 at 7:24 PM, Rodrigo Bernardo Pimentel wrote:

> I would say those aren't valid, although that makes checking forvalid hour values slightly weird
>(as in not straightforward): 24 is valid if minutes, seconds and microseconds are all 0, but invalid
>otherwise.

Indeed anything beyond 24:0:0 is invalid

ingo
msg121980 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2010-11-21 20:29
On Sun, Nov 21, 2010 at 1:24 PM, Rodrigo Bernardo Pimentel
<report@bugs.python.org> wrote:
..
> I was writing tests for this issue, when something struck me: ok, datetime(year, month, day, 24) is valid.
> But is datetime(year, month, day, 24, 1) valid? Or datetime(year, month, day, 24, 0, 0, 1)?
>

As you make progress on the patch, you will face more questions.  For
example, what should datetime(y, m, d, 24).date() return?  date(y, m,
d) or date(y, m, d) + timedelta(1)?  Should strptime() parse '24' as a
valid %H field? Similarly, should strftime() produce '24'?  Is
datetime(y, m, d, 24) equal to datetime(y, m, d) + timedelta(1)? If
so, hash calculation should be special cased.

On your original question, I feel that hour=24 should be allowed
regardless of the other values.  I would recommend, however, that you
review python-dev discussion about allowing second=60 (search for
"leap second").  Once you have a reference implementation you will
need to explain motivations behind your choices on python-dev.
msg121985 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2010-11-21 20:48
> As you make progress on the patch, you will face more questions.

Well, if all that's wanted is for hour==24 to be legal on input, with the datetime object itself being automatically normalized at creation time, then the choices seem simple:  e.g.,

> For example, what should datetime(y, m, d, 24).date() return?  date(y,
> m, d) or date(y, m, d) + timedelta(1)?

The latter, since the two datetime objects would be indistinguishable...

> Should strptime() parse '24' as a valid %H field?

I'd say yes, provided that any minute, second or subsecond fields are all zero.

FWIW, I'm in the camp that says hour==24 should only be legal if all values for smaller time units are zero.


If a datetime object created with 'hour==24' doesn't immediately normalize itself, then I agree there are going to be a lot of hairy questions...
msg121988 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2010-11-21 20:55
On Sun, Nov 21, 2010 at 3:48 PM, Mark Dickinson <report@bugs.python.org> wrote:
..
> Well, if all that's wanted is for hour==24 to be legal on input, with the datetime object
> itself being automatically normalized at creation time, then the choices seem simple: ..

What about time objects? If we take the "normalized at creation time"
approach, time(24) may still be disallowed.
msg121989 - (view) Author: Mark Dickinson (mark.dickinson) * (Python committer) Date: 2010-11-21 20:58
> What about time objects? If we take the "normalized at creation time"
> approach, time(24) may still be disallowed.

Yes, I guess that would follow.  That wouldn't bother me too much. :-)
msg133284 - (view) Author: Alexander Belopolsky (belopolsky) * (Python committer) Date: 2011-04-08 01:06
Is there still interest in pursuing this?   Normalizing out of bounds arguments to datetime constructor is easy, but rather pointless.  It is trivial to implement this functionality using existing timedelta constructor:

def normdatetime(Y, M, D, h, m, s):
    return datetime(Y, M, D) + timedelta(hours=h, minutes=m, seconds=s)

It would be much more interesting to allow datetime objects to store ISO 8601 data without loss of information.  It may be important to some applications that datetime(Y, M, D, 24).date() is date(Y, M, D) rather than the next day.  Other applications may want to distinguish between datetime(Y, M, D, 24) and datetime(Y, M, D) + timedelta(1) in some other ways.  For leap seconds accounting, normalizing ISO 8601 timestamps with seconds=60 to the next minute is certainly wrong.  An application that is unfortunate enough to record data during a leap second would clearly want to distinguish that data from data recorded a second later.

I believe that from practical applications point of view, it would be best to allow storing hour=24 and second=60 in datetime structure and order the resulting objects in the same way as ISO 8601 strings are ordered alphabetically.  In other words, 

Y-M-D 24:00 < Y-M-D+1 00:00

and

Y-M-D 23:59:60 < Y-M-D+1 00:00:00.

There is no sane choice for subtraction operation involving these special values or for adding timedeltas to them.  It is probably best to raise an exception in those cases.
History
Date User Action Args
2013-08-04 19:50:47belopolskysetstatus: open -> closed
resolution: rejected
stage: test needed -> resolved
2012-05-07 18:31:24cvrebertsetnosy: + cvrebert
2011-04-08 01:06:03belopolskysetmessages: + msg133284
2010-11-21 20:58:13mark.dickinsonsetmessages: + msg121989
2010-11-21 20:55:39belopolskysetmessages: + msg121988
2010-11-21 20:48:52mark.dickinsonsetnosy: + mark.dickinson
messages: + msg121985
2010-11-21 20:29:06belopolskysetmessages: + msg121980
2010-11-21 19:18:57ingo.janssensetmessages: + msg121967
2010-11-21 18:24:39rbpsetnosy: + rbp
messages: + msg121956
2010-11-16 03:18:11merwoksetnosy: + merwok
2010-11-15 15:24:32belopolskysetnosy: belopolsky, ezio.melotti, ingo.janssen
messages: + msg121234

assignee: belopolsky
components: + Extension Modules
stage: test needed
2010-11-15 14:41:30ezio.melottisetnosy: + ezio.melotti, belopolsky

components: + Library (Lib)
versions: - Python 2.6, Python 2.5, Python 3.1, Python 2.7, Python 3.2
2010-11-15 14:37:58ingo.janssencreate