How to *change* a struct_time object?

Question:

When dealing with times and dates in python, you will stumble across the time.struct_time object:

st = time.strptime("23.10.2012", "%d.%m.%Y")
print st
time.struct_time(tm_year=2012, tm_mon=10, tm_mday=23, tm_hour=0, tm_min=0,
                 tm_sec=0, tm_wday=1, tm_yday=297, tm_isdst=-1)

Now as this struct does not support item assignment (i.e. you cannot do something like st[1]+=1) how else is it possible to increase, say, the number of the month.

Solutions suggest to convert this time_struct into seconds and add the corresponding number of seconds, but this does not look nice. You also need to know how many days are in a month, or if the year is a leap year or not. I want an easy way to obtain a time_struct to be e.g.

time.struct_time(tm_year=2012, tm_mon=11, tm_mday=23, tm_hour=0, tm_min=0,
                 tm_sec=0, tm_wday=1, tm_yday=297, tm_isdst=-1)

with just the month increased by one. Creating a time_struct from scratch would be fine—but how? What ways are there?

Asked By: Alex

||

Answers:

Use the datetime module instead, which has a far richer set of objects to handle date(time) arithmetic:

import datetime
adate = datetime.datetime.strptime("23.10.2012", "%d.%m.%Y").date()
adate + datetime.timedelta(days=30)

You can use the excellent python-dateutil add-on module to get an ever richer set of options for dealing with deltas:

from dateutil.relativedelta import relativedelta
adate + relativedelta(months=1)

relativedelta knows about leap years, month lengths, etc. and will do the right thing when adding a month to, say, January 30th (you end up with February 28th or 29th, it won’t cross month boundaries).

Answered By: Martijn Pieters

You can first convert it to datetime, then add a timedelta offset to it:

from time import mktime
from datetime import datetime

dt = datetime.fromtimestamp(mktime(struct))
timedelta = datetime.timedelta(seconds=10)
dt = dt + timedelta

References:

  1. How do you convert a Python time.struct_time object into a datetime object?
  2. http://docs.python.org/library/datetime.html
Answered By: Dhara

So… the schema for the tuple underlying time_struct is given in the documentation here:
http://docs.python.org/2/library/time.html#time.struct_time

And even though it’s read-only as a time_struct, you can use casting to list() to get at that tuple directly (remembering to wrap-around your month after you increment it, and keep the end range in 1-12 rather than 0-11). Then the time.struct_time() function will convert a valid 9-tuple back into a time_struct:

st = time.strptime("23.10.2012", "%d.%m.%Y")
st_writable = list(st)
st_writable[1] = (st_writable[1] + 1)%12 + 1
st = time.struct_time(tuple(st_writable))
Answered By: Christine

If you want to avoid datetime all together and stick with time, you can convert to unix timestamp and then subtract the number of seconds you need. To get a time object for 24 hours ago:

time.gmtime(time.mktime(time.gmtime()) - 86400)

However, as OP pointed out this is not good for working with months and should be reserved for seconds, hours, and days.

Answered By: Andy Fraley

This question of the OP seems to be still unanswered:

Creating a time_struct from scratch would be fine—but how?

It can be done without importing any extra modules, but at the cost of taking an indirect and less efficient route:

def new_date( y, m, d ):
    return time.strptime( 
        str(y) + str(m).rjust(2,'0') + str(d).rjust(2,'0'), "%Y%m%d" );

or use use str.format() for brevity:

def new_date( y, m, d ):
    return time.strptime( "{0}{1:0>2}{2:0>2}".format(y, m, d), "%Y%m%d");

It is indeed starge that the module implementing a type has not provided a direct means of creating instances of that type…

Answered By: Anton Shepelev
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.