Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

simpler increment of time values?

78 views
Skip to first unread message

Vlastimil Brom

unread,
Jul 4, 2012, 8:29:10 PM7/4/12
to pytho...@python.org
Hi all,
I'd like to ask about the possibilities to do some basic manipulation
on timestamps - such as incrementing a given time (hour.minute -
string) by some minutes.
Very basic notion of "time" is assumed, i.e. dateless,
timezone-unaware, DST-less etc.
I first thought, it would be possible to just add a timedelta to a
time object, but, it doesn't seem to be the case.

The code I came up with (using time and datetime modules) seems rather
convoluted and I would like to ask about some possible more
straightforward alternatives I missed.
The equivalent function (lacking validation) without the (date)time
libraries seems simple enough (for this limited and individual task).
Although it is probably mostly throw-away code, which seems to do what
I need, I'd be interested in better/more elegant... solutions.

# # #
import time
import datetime
import re

print re.sub(r"^0","", (datetime.datetime(*list(time.strptime("8.45",
"%H.%M"))[:6]) + datetime.timedelta(minutes=30)).strftime("%H.%M"))
# 9.15

# # # # # # # # #

def add_minutes(hour_min_str, separator=".", minutes_to_add=0):
h, m = [int(s) for s in hour_min_str.split(separator)]
sum_minutes = h * 60 + m + minutes_to_add
h, m = divmod(sum_minutes, 60)
h = h % 24
return "%s%s%s" % (h, separator, m)

print add_minutes(hour_min_str="8.45", separator='.', minutes_to_add=30)
# 9.15

# # # # # # # # #

Is it true, that timedelta cannot be used with dateless time values?
(Is there some other possibility than the current one, where strptime
actually infers 1. 1. 1900?)
Is there some simpler way to adapt the incompatible output of strptime
as the input of datetime?
Is it possible to get one-digit hours formatted without the leading zero?

Thanks in advance for any suggestions or remarks;
regards,
Vlastimil Brom

Chris Angelico

unread,
Jul 4, 2012, 9:09:50 PM7/4/12
to pytho...@python.org
On Thu, Jul 5, 2012 at 10:29 AM, Vlastimil Brom
<vlastim...@gmail.com> wrote:
> I'd like to ask about the possibilities to do some basic manipulation
> on timestamps - such as incrementing a given time (hour.minute -
> string) by some minutes.
> Very basic notion of "time" is assumed, i.e. dateless,
> timezone-unaware, DST-less etc.

My first suggestion would be to work with Unix times (that is, store
your time as "seconds since 1970", also called a time_t). That's
forced to be on UTC (but ignoring leap seconds) so you don't have to
worry about timezones or DST, and incrementing by X minutes is simply
adding X*60 to it. It makes your job a lot easier!

ChrisA

Mark Lawrence

unread,
Jul 4, 2012, 10:35:29 PM7/4/12
to pytho...@python.org
On 05/07/2012 01:29, Vlastimil Brom wrote:
> Hi all,
> I'd like to ask about the possibilities to do some basic manipulation
> on timestamps - such as incrementing a given time (hour.minute -
> string) by some minutes.
> Very basic notion of "time" is assumed, i.e. dateless,
> timezone-unaware, DST-less etc.
from dateutil.relativedelta import relativedelta should simplify things
for you

google and ye shall find :)

--
Cheers.

Mark Lawrence.



Jason Friedman

unread,
Jul 5, 2012, 12:57:23 AM7/5/12
to pytho...@python.org
> Hi all,
> I'd like to ask about the possibilities to do some basic manipulation
> on timestamps - such as incrementing a given time (hour.minute -
> string) by some minutes.
> Very basic notion of "time" is assumed, i.e. dateless,
> timezone-unaware, DST-less etc.
> I first thought, it would be possible to just add a timedelta to a
> time object, but, it doesn't seem to be the case.
>
> The code I came up with (using time and datetime modules) seems rather
> convoluted and I would like to ask about some possible more
> straightforward alternatives I missed.
> The equivalent function (lacking validation) without the (date)time
> libraries seems simple enough (for this limited and individual task).
> Although it is probably mostly throw-away code, which seems to do what
> I need, I'd be interested in better/more elegant... solutions.

I have some thoughts on a solution, but first, what is 12:45 plus 12
hours? What is 12:45 minus 13 hours?

Devin Jeanpierre

unread,
Jul 5, 2012, 1:05:45 AM7/5/12
to Jason Friedman, pytho...@python.org
On Thu, Jul 5, 2012 at 12:57 AM, Jason Friedman <ja...@powerpull.net> wrote:
> I have some thoughts on a solution, but first, what is 12:45 plus 12
> hours? What is 12:45 minus 13 hours?

Is there anything unusual that can happen for a notion of time that is
dateless, timezone-unaware, and DST-free?

I would imagine that under these constraints the answers are always
0:45 and 23:45 respectively. Although I guess it doesn't hurt to check.

-- Devin

Vlastimil Brom

unread,
Jul 5, 2012, 9:18:09 AM7/5/12
to pytho...@python.org
Many thanks to all for your suggestions!

@ChrisA
Yes, the calculations with seconds since the Unix epoch is very
convenient for real times, but trying to make it dateless seemed to
make it more complicated for me.

The expected output for the increments asked by Jason was already
correctly stated by Devin; i.e.: 12:45 plus 12 hours is 0:45 and 12:45
minus 13 hours is 23:45.

Thanks for reminding me of dateutil.relativedelta, Mark, I didn't
think of it in this context (I always thought, the "relative" stands
for time and date calculations with regard to the current time and
date). There doesn't seem to be a way to use dateless time either
(unless I missed it),
however, it turns out, that one can probably work with this naive
"times" like with deltas (possibly ignoring other units than hours and
minutes in the result):

>>> td = dateutil.relativedelta.relativedelta(hours=9, minutes=45) + dateutil.relativedelta.relativedelta(minutes=30)
>>> "%s.%s" % (td.hours, td.minutes)
'10.15'
>>>
Which is probably the simplest and the most robust way, I found sofar.
It likely isn't the expected use case for relativedelta, but it seems
to work ok. (Are there maybe some drawbacks I am missing?)
(Well I just found one possible pitfall , if floats are passed:
>>> td = dateutil.relativedelta.relativedelta(hours=9, minutes=45) + dateutil.relativedelta.relativedelta(minutes=30.5)
>>> "%s.%s" % (td.hours, td.minutes)
'10.0.15.5'
, but its beyond my current use case, and the validation can always be added.)


The same would be doable using the built in timedelta too, but there
are no hours and minutes in its output, hence these are to be
converted from the seconds count.

>>> dttd=datetime.timedelta(hours=9, minutes=45) + datetime.timedelta(minutes=30)
>>> dttd
datetime.timedelta(0, 36900)
>>> dttd.seconds
36900
>>> s = dttd.seconds
>>> h,s = divmod(s,3600)
>>> m,s = divmod(s,60)
>>> h,m,s
(10, 15, 0)
>>> "%s.%s" % (h, m)
'10.15'
>>>

Any thoughts?
thanks again,

vbr

Chris Angelico

unread,
Jul 5, 2012, 9:56:37 AM7/5/12
to pytho...@python.org
On Thu, Jul 5, 2012 at 11:18 PM, Vlastimil Brom
<vlastim...@gmail.com> wrote:
> Yes, the calculations with seconds since the Unix epoch is very
> convenient for real times, but trying to make it dateless seemed to
> make it more complicated for me.
>
> The expected output for the increments asked by Jason was already
> correctly stated by Devin; i.e.: 12:45 plus 12 hours is 0:45 and 12:45
> minus 13 hours is 23:45.

I'm not familiar with the Python classes (I tend to think in terms of
language-agnostic algorithms first, and specific libraries/modules/etc
second), but if you're working with simple integer seconds, your
datelessness is just modulo arithmetic.

time1 + time2 --> (time1 + time2) % 86400
time1 - time2 --> (time1 + 86400 - time2) % 86400

Or alternatively, bounding afterward:

if time > 86400: time-=86400
if time < 86400: time+=86400

(The "magic number" 86400 is a well-known number, being seconds in a
day. Feel free to replace it with 24*60*60 if it makes you feel
better; I'm pretty sure Python will translate it into a constant at
parse time. Or alternatively, have a module-level constant
SECONDS_IN_A_DAY = 86400, in case that number should ever change.)

ChrisA

ru...@yahoo.com

unread,
Jul 5, 2012, 10:11:38 AM7/5/12
to pytho...@python.org
If it's any consolation, I had to add a small constant time
delta to all the times in a video subtitles file and my code
ended up looking very much like yours. What should have take
five minutes to write took several hours,

I remain surprised and disappointed that doing something so
simple (read time text into time object, add timedelta, print
result) was so awkward in Python.

ru...@yahoo.com

unread,
Jul 5, 2012, 10:11:38 AM7/5/12
to comp.lan...@googlegroups.com, pytho...@python.org
On Wednesday, July 4, 2012 6:29:10 PM UTC-6, Vlastimil Brom wrote:

Steven D'Aprano

unread,
Jul 5, 2012, 11:19:35 AM7/5/12
to
On Thu, 05 Jul 2012 23:56:37 +1000, Chris Angelico wrote:

> (The "magic number" 86400 is a well-known number, being seconds in a
> day.

Does that include leap seconds?


> Feel free to replace it with 24*60*60 if it makes you feel better;
> I'm pretty sure Python will translate it into a constant at parse time.
> Or alternatively, have a module-level constant SECONDS_IN_A_DAY = 86400,
> in case that number should ever change.)

"In case"?

The number of seconds in a day (true solar day) varies by between 13 and
30 seconds depending on the time of the year and the position of the sun.

The mean solar day fluctuates randomly by about 5ms due to friction
between the core and the mantle; it is also systematically slowing due to
tidal friction. The 2004 Indian Ocean earthquake reduced the length of a
day by about 3ms; the 2011 Japan earthquake slowed it down by about 2ms.
Apart from these random changes, there are systematic changes of the
order of 1ms per year, so in a mere thousand years, the length of the
mean solar day will be about a second longer.

Imagine how much extra work we'll be able to get done!

The stellar day (Earth's rotational period relative to the distant stars)
is slightly more than 86164.098 seconds; the sidereal day is slightly
more than 86164.090 seconds. Both are approximately 3 minutes 56 seconds
shorter than the mean solar day.



--
Steven

Rick Johnson

unread,
Jul 5, 2012, 11:39:03 AM7/5/12
to
On Jul 5, 10:19 am, Steven D'Aprano <steve
+comp.lang.pyt...@pearwood.info> wrote:
> The number of seconds in a day (true solar day) varies by between 13 and
> 30 seconds depending on the time of the year and the position of the sun.

Indeed. Which proves that a time keeping system based on the haphazard
movements of celestial bodies is antiquated technology. Talk about job
security!

Chris Angelico

unread,
Jul 5, 2012, 11:53:19 AM7/5/12
to pytho...@python.org
On Fri, Jul 6, 2012 at 1:19 AM, Steven D'Aprano
<steve+comp....@pearwood.info> wrote:
> On Thu, 05 Jul 2012 23:56:37 +1000, Chris Angelico wrote:
>
>> (The "magic number" 86400 is a well-known number, being seconds in a
>> day.
>
> Does that include leap seconds?

No it doesn't, hence...

>> Feel free to replace it with 24*60*60 if it makes you feel better;
>> I'm pretty sure Python will translate it into a constant at parse time.
>> Or alternatively, have a module-level constant SECONDS_IN_A_DAY = 86400,
>> in case that number should ever change.)
>
> "In case"?

... this not being entirely tongue-in-cheek. I believe the UTC spec
allows for a progressive increase in the number of leap seconds, from
the current "maybe one a year" at either Dec or June to having them
multiple months a year, to having a leap second optionally every day,
to having one a day and multiple leap seconds on some days. But it's
not until we're actually in that last state (or close to it) that I
would consider changing SECONDS_IN_A_DAY to 86401. Leap seconds are
largely outside the concept of "dateless time".

> The number of seconds in a day (true solar day) varies ...
> it is also systematically slowing due to tidal friction.
> ... in a mere thousand years, the length of the
> mean solar day will be about a second longer.

Yes. It's rather more likely to be an issue than, say, "PI = 3.14159"
needing to change, but you do still have to consider what your
definition of time is. If you're actually counting real
time-since-epoch, then you need to either include leap seconds in your
count (like TAI does) or ignore them (like Unix time does - divide
Unix time by 86400 and you get the number of days since 1970, but a
second's worth of Unix times "happen twice" when a positive leap
second occurs). However, I think it would only surprise people if:

23:30 + 03:45 = 03:14:59

and they'd think it was an easter egg for displaying one of a geek's
favorite numbers.

> Imagine how much extra work we'll be able to get done!

Oh, I reckon most people will waste it on sleep...

ChrisA

Chris Angelico

unread,
Jul 5, 2012, 11:59:19 AM7/5/12
to pytho...@python.org
The current *time keeping system* is based atomic clocks. It's only
when you want to display this thing called "civil time" (so-called
because it causes very uncivil arguments) that you concern yourself
with astronomy, base 60, base 24, and other constructs.

Now, if you want to argue about a poor choice of standard, look at
so-called "Internet Time" that a Swiss watch company invented, which
divides a day into 1000 beats. Why use the day as your basis and make
it hard to convert to SI units reliably?

ChrisA

Ian Kelly

unread,
Jul 5, 2012, 1:06:41 PM7/5/12
to Python
On Thu, Jul 5, 2012 at 7:56 AM, Chris Angelico <ros...@gmail.com> wrote:
> I'm not familiar with the Python classes (I tend to think in terms of
> language-agnostic algorithms first, and specific libraries/modules/etc
> second), but if you're working with simple integer seconds, your
> datelessness is just modulo arithmetic.
>
> time1 + time2 --> (time1 + time2) % 86400
> time1 - time2 --> (time1 + 86400 - time2) % 86400

The "+ 86400" is redundant; you'll get the same answer with or without
it. There is nothing to fear from going negative when doing modulo
arithmetic, because unlike C, Python actually has well-defined
semantics regarding modulo division of negative numbers.

>>> (13382 + 86400 - 42597) % 86400
57185
>>> (13382 - 42597) % 86400
57185

Cheers,
Ian

Chris Angelico

unread,
Jul 5, 2012, 1:16:16 PM7/5/12
to pytho...@python.org
On Fri, Jul 6, 2012 at 3:06 AM, Ian Kelly <ian.g...@gmail.com> wrote:
> The "+ 86400" is redundant; you'll get the same answer with or without
> it. There is nothing to fear from going negative when doing modulo
> arithmetic, because unlike C, Python actually has well-defined
> semantics regarding modulo division of negative numbers.
>
>>>> (13382 + 86400 - 42597) % 86400
> 57185
>>>> (13382 - 42597) % 86400
> 57185

Ah good. There's a lot of languages like that these days, but I've
gotten so into the habit of not depending on it that I just
automatically add N first. It's redundant but insignificant.

So it's even easier than I said. And bonus lesson for the day: Try
things in the interactive interpreter before you post. :)

ChrisA

John Nagle

unread,
Jul 5, 2012, 1:34:16 PM7/5/12
to
On 7/4/2012 5:29 PM, Vlastimil Brom wrote:
> Hi all,
> I'd like to ask about the possibilities to do some basic manipulation
> on timestamps - such as incrementing a given time (hour.minute -
> string) by some minutes.
> Very basic notion of "time" is assumed, i.e. dateless,
> timezone-unaware, DST-less etc.
> I first thought, it would be possible to just add a timedelta to a
> time object, but, it doesn't seem to be the case.

That's correct. A datetime.time object is a time within a day.
A datetime.date object is a date without a time. A datetime.datetime
object contains both.

You can add a datetime.timedelta object to a datetime.datetime
object, which will yield a datetime.datetime object.

You can also call time.time(), and get the number of seconds
since the epoch (usually 1970-01-01 00:00:00 UTC). That's just
a number, and you can do arithmetic on that.

Adding a datetime.time to a datetime.timedelta isn't that
useful. It would have to return a value error if the result
crossed a day boundary.

John Nagle


ru...@yahoo.com

unread,
Jul 5, 2012, 2:15:04 PM7/5/12
to
On Thursday, July 5, 2012 11:34:16 AM UTC-6, John Nagle wrote:
>[...]
> You can also call time.time(), and get the number of seconds
> since the epoch (usually 1970-01-01 00:00:00 UTC). That's just
> a number, and you can do arithmetic on that.
>
> Adding a datetime.time to a datetime.timedelta isn't that
> useful.

It certainly is useful and I gave an obvious and real-
world example in my previous post.

> It would have to return a value error if the result
> crossed a day boundary.

Why? When I turn the adjustment knob on my analog
clock it crosses the day boundary from 23:59 to 0:00
with no problem whatsoever. Why is Python unable
to do what billions of clocks do?

Instead I have to convert everything to seconds and
do the same math I would have done in fortran in 1980.
Phew.

Another example of Pythonic "purity beats practicality"

Steven D'Aprano

unread,
Jul 5, 2012, 10:34:25 PM7/5/12
to
On Thu, 05 Jul 2012 11:15:04 -0700, rurpy wrote:

> On Thursday, July 5, 2012 11:34:16 AM UTC-6, John Nagle wrote:
>>[...]
>> You can also call time.time(), and get the number of seconds
>> since the epoch (usually 1970-01-01 00:00:00 UTC). That's just a
>> number, and you can do arithmetic on that.
>>
>> Adding a datetime.time to a datetime.timedelta isn't that
>> useful.
>
> It certainly is useful and I gave an obvious and real- world example in
> my previous post.

Agreed.

A timedelta of less than one day magnitude should be usable with time
objects, and wrap around at midnight. That's a clear and useful extension
to the current functionality.

I can't see a feature request (rejected or otherwise) on the bug tracker.
Perhaps you should raise one for Python 3.4. It will have a better chance
of being accepted if you include a patch, or at least tests.

http://bugs.python.org/



--
Steven
Message has been deleted

Rick Johnson

unread,
Jul 6, 2012, 12:40:15 PM7/6/12
to
On Jul 5, 12:16 pm, Chris Angelico <ros...@gmail.com> wrote:
>
> So it's even easier than I said. And bonus lesson for the day: Try
> things in the interactive interpreter before you post. :)

but first: be sure to familiarize yourself with the many built-in
"python classes"(sic). Re-inventing the wheel is breaking the lazy
programmers' creed. It should only be broken if you wish to understand
how things work "under the hood".


Vlastimil Brom

unread,
Jul 6, 2012, 5:48:22 PM7/6/12
to pytho...@python.org
Thanks to all for further comments!
Just for completeness and in case somebody would like to provide some
suggestions or corrections;
the following trivial class should be able to deal with the initial
requirement of adding or subtracting dateless time values
(hour:minute).

regards,
vbr

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #

import re

class TrivialTime(object):
"""
Trivial, dateless, DST-less, TZN-less time in 24-hours cycle
only supporting hours and minutes; allows addition and subtraction.
"""

def __init__(self, hours=0, minutes=0):
self.total_minutes = (int(hours) * 60 + int(minutes)) % (60 * 24)
self.hours, self.minutes = divmod(self.total_minutes, 60)

def __add__(self, other):
return TrivialTime(minutes=self.total_minutes + other.total_minutes)

def __sub__(self, other):
return TrivialTime(minutes=self.total_minutes - other.total_minutes)

def __repr__(self):
return "TrivialTime({}, {})".format(self.hours, self.minutes)

def __str__(self):
return "{}.{:0>2}".format(self.hours, self.minutes)

@staticmethod
def fromstring(time_string, format_re=r"^([0-2]?\d?)[.:,-]\s*([0-5]\d)$"):
"""
Returns a TrivialTime instance according to the data from the
given string
with respect to the regex time format (two parethesised groups
for minutes and seconds respectively).
"""
time_string_match = re.match(format_re, time_string)
if not time_string_match:
raise ValueError("Time data cannot be obtained from the
given string and the format regex.")
return TrivialTime(hours=int(time_string_match.group(1)),
minutes=int(time_string_match.group(2)))

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
0 new messages