How to *change* a struct_time object? - python

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?

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).

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))

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:
How do you convert a Python time.struct_time object into a datetime object?
http://docs.python.org/library/datetime.html

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.

Related

strptime() error - Time from sensor with more than 24 hours (e.g. 24:01:53)

I am using datetime.strptime() to convert a string containing time and date from a sensor into a datetime object.
The code sometimes fails. Minimal example:
datetime.strptime('1/9/2021 24:01:53', '%d/%m/%Y %H:%M:%S')
Output error:
ValueError: time data '1/9/2021 24:01:53' does not match format '%d/%m/%Y %H:%M:%S'
I am guessing this has to do with the fact that the time is more than 23:59:59 - which seems to me a non-realistic time (I would think that 1/9/2021 24:01:53 could potentially be 2/9/2021 00:01:53 - a time format which I have never seen).
Is this a non-standard way of representing time or possibly a hardware/software issue with the sensor acquisition system? If it is a different way of representing time, how can I convert it to a standard datetime object?
Kind regards,
D.F.
If the hour exceeds 23 in a variable representing time, a good option is to create a timedelta from it, which you can then add to a datetime object. For given example that might look like
from datetime import datetime, timedelta
def custom_todatetime(s):
"""
split date/time string formatted as 'DD/MM/YYYY hh:mm:ss' into date and time parts.
parse date part to datetime and add time part as timedelta.
"""
parts = s.split(' ')
seconds = sum(int(x) * 60 ** i for i, x in enumerate(reversed(parts[1].split(':'))))
return datetime.strptime(parts[0], "%d/%m/%Y") + timedelta(seconds=seconds)
s = '1/9/2021 24:01:53'
print(custom_todatetime(s))
# 2021-09-02 00:01:53
Note: conversion of hh:mm:ss to seconds taken from here - give a +1 there if helpful.

How to parse datetime and time from strings and how to add these parsed datetime and time values in Python

I am parsing datetime objects from strings,
In these situation I faced a problem where I have to add datetime object with time object together to create combined timestamp.
I know there is a datetime.combine method but unfortunately I could not use it in this situation
e.g. there are two strings, one contains formatted datetime and other has formatted time like below
dt_str = "2018/11/27 14:12:32"
tm_str = "1:23:45.678" # 1 hour 23 minutes 11 seconds and 750 micro seconds
dt_str = "2018/11/27 14:12:32"
tm_str = "1:23:45.678"
First we need to import from python's standard libraries i.e. datetime, time and timedelta
import datetime, time
from datetime import timedelta
Then we will parse dt_str as datetime object and tm_str as time object
dt = datetime.datetime.strptime(dt_str, "%Y/%m/%d %H:%M:%S")
tm = time.strptime(tm_str, "%H:%M:%S.%f")
Now we will use timedelta class to add hours, minutes and seconds to the datetime object from time object
timestamp = dt + timedelta(hours=tm.tm_hour) + \
timedelta(minutes=tm.tm_min) + timedelta(seconds=tm.tm_sec)
Results
print("dt:", dt)
print("tm:", tm)
print("timestamp: ", timestamp)
Note: You can not add microseconds value, at least I do not know the method. If someone knows better way to do above operations please put your solutions below
dt: 2018-11-27 14:12:32
tm: time.struct_time(tm_year=1900, tm_mon=1, tm_mday=1, tm_hour=1, tm_min=23, tm_sec=45, tm_wday=0, tm_yday=1, tm_isdst=-1)
timestamp: 2018-11-27 15:36:17

How to convert dateutil.relativedelta object to datetime.timedelta object?

How can I convert a dateutil.relativedelta object to a datetime.timedelta object?
e.g.,
# pip install python-dateutil
from dateutil.relativedelta import relativedelta
from datetime import timedelta
rel_delta = relativedelta(months=-2)
# How can I convert rel_delta to a timedelta object so that I can call total_seconds() ?
time_delta = ???(rel_delta)
time_delta.total_seconds() # call the timedelta.total_seconds() method
You can't, for one huge reason: They don't store the same information. datetime.timedelta only stores days, seconds, and milliseconds, whereas dateutil.relativedelta stores every single time component fed to it.
That dateutil.relativedelta does so is important for storing things such as a difference of 1 month, but since the length of a month can vary this means that there is no way at all to express the same thing in datetime.timedelta.
In case someone is looking to convert a relativedelta to a timedelta from a specific date, simply add and subtract the known time:
utcnow = datetime.utcnow()
rel_delta = relativedelta(months=-2)
time_delta = utcnow + rel_delta - utcnow # e.g, datetime.timedelta(days=-62)
As a commenter points out, the resulting timedelta value will differ based on what month it is.
Depending on why you want to call total_seconds, it may be possible to refactor your code to avoid the conversion altogether. For example, consider a check on whether or not a user is over 18 years old:
datetime.date.today() - user['dateOfBirth'] < datetime.timedelta(days=365*18)
This check is not a good idea, because the timedelta object does not account for things like leap years. It's tempting to rewrite as:
datetime.date.today() - user['dateOfBirth'] < dateutil.relativedelta.relativedelta(years=18)
which would require comparing a timedelta (LHS) to a relativedelta (RHS), or converting one to the other. However, you can refactor the check to avoid this conversion altogether:
user['dateOfBirth'] + dateutil.relativedelta.relativedelta(years=18) > datetime.date.today()

How to add two offset in python?

I am getting a offset form the pytz library from the following line:
offset = datetime.datetime.now(pytz.timezone(timezone)).strftime('%z')
First i pass the US/Eastern in timezone variable
and then i pass the Asia/Kolkata in timezone variable which prints the following value
local_utc = -0400
user_utc = +0530
After getting these values i converted it from string to int by following code:
local_utc = int(local_utc)
user_urc = int(user_utc)
Apart from this i have a timetuple also:
hour, minute,days = (timezone_tuple.tm_hour, timezone_tuple.tm_min,
timezone_tuple.tm_mday)
I want to add the difference of local_utc and user_utc to above tuple such as -0400: 04 such as hour and 00 as minutes.
For example: difference will be : 0930. And 09 will be add to timezone_tuple.tm_hour and 30 will be add to timezone_tuple.tm_min
I didn't found any situation. how can it be possible?
Is there any way to do with spilit method
Your post showed how to find local_utc and user_utc as integers. You could just take the difference local_utc-user_utc to determine the relative offset.
However, datetime, time and pytz should give you all the tools you need to manipulate times without having to parse offsets and do such calculations "manually".
For example,
import pytz
import datetime as dt
import time
eastern = pytz.timezone('US/Eastern')
kolkata = pytz.timezone('Asia/Kolkata')
naive_timetuple = time.localtime(0)
print(naive_timetuple)
# time.struct_time(tm_year=1969, tm_mon=12, tm_mday=31, tm_hour=19, tm_min=0, tm_sec=0, tm_wday=2, tm_yday=365, tm_isdst=0)
Above, I defined a naive timetuple. Below, I "localize" it to US/Eastern time -- that is, make it a timezone-aware datetime:
naive_datetime = dt.datetime(*naive_timetuple[:6])
print(naive_datetime)
# 1969-12-31 19:00:00
localized_datetime = eastern.localize(naive_datetime)
print(localized_datetime)
# 1969-12-31 19:00:00-05:00
Now to convert a timezone-aware datetime to any other timezone, use the astimezone method:
kolkata_datetime = localized_datetime.astimezone(kolkata)
print(kolkata_datetime)
# 1970-01-01 05:30:00+05:30
And if you need to convert a datetime back to a timetuple, use the timetuple method:
kolkata_timetuple = kolkata_datetime.timetuple()
print(kolkata_timetuple)
# time.struct_time(tm_year=1970, tm_mon=1, tm_mday=1, tm_hour=5, tm_min=30, tm_sec=0, tm_wday=3, tm_yday=1, tm_isdst=0)

Override operator + to make date + time = datetime in Python

I have a couple classes extending builtin datetime.*
Is there any good reason to not overload + (MyTime.__radd___) so MyDate + MyTime returns a MyDateTime?
This is already implemented as a class method, datetime.datetime.combine:
import datetime
d = datetime.date(2010, 12, 5)
t = datetime.time(10, 22, 15)
dt = datetime.datetime.combine(d, t)
print dt
prints
2010-12-05 10:22:15
This would generally be frowned upon because you're really combining rather than adding; this is why the actual datetime library has a combine method rather than using addition in this way.
I'm not aware of any other cases in Python where <instance of TypeA> + <instance of TypeB> produces <instance of TypeC>. Thus, the Principle of least astonishment suggests that you should simply provide a combine method rather than overload addition.
Yes, there is at least one good reason not to: the resulting instance is completely different from the two input instances. Is this important? I don't think so -- consider that date - date yields timedelta.
The way I see it:
Does adding two dates together make sense? No.
Does adding two times together make sense? No.
Does adding a date and a time together make sense? Yup!
Does adding a date and a timedelta togethor make sense? Maybe.
Does adding a time and a timedelta together make sense? Maybe.
and for subtraction
Does subtracting two dates make sense? Yes.
Does subtracting two times make sense? Yes.
Does subtracting a time from a date make sense? Nope.
Does subtracting a timedelta from a date make sense? Maybe.
Does subtracting a timedelta from a time make sense? Maybe.
Developing along the lines of what makes sense:
date + time => datetime
date + timedelta => date | datetime or exception or silently drop time portion
time + date => datetime
time + timedelta => time | wrap-around or exception
date - date => timedelta
date - timedelta => date | datetime or exception or silently drop time portion
time - time => timedelta
time - timedelta => time | wrap-around or exception
datetime + timedelta => datetime
datetime - timedelta => datetime
So, if it were me and I were designing a Date, Time, DateTime, TimeDelta framework, I would allow:
date + time
date - date
time - time
datetime + timedelta
datetime - timedelta
and for these:
date +/- timedelta
time +/- timedelta
I would default to returning the same type if the timedelta had none of the other type, and raising an exception if the timedelta did have some of the other type, but there would be a setting that would control that. The other possible behavior would be to drop the unneeded portion -- so a date combined with a timedelta that had hours would drop the hours and return a date.
Due to the existence of the date, time, and datetime cross-type addition and subtraction operators, I would think that this is fine, so long as it is well defined.
Currently (2.7.2):
date = date + timedelta
date = date - timedelta
timedelta = date - date
datetime = datetime + timedelta
datetime = datetime - timedelta
timedelta = datetime - datetime
I believe the following is also reasonable for an extension:
timedelta = time - time
datetime = date + time
I was going to suggest the following as well, but time has very specific min and max values for hour, minute, second, and microsecond, thus requiring a silent wraparound of values or returning of a different type:
time = time + timedelta
time = time - timedelta
Similarly, date cannot handle a timedelta of less than a day being added to it. Often I have been told to simply use Duck Typing with Python, because that's the intent. If that is true, then I would propose the following completed interface:
[date|datetime] = date + timedelta
[date|datetime] = date - timedelta
timedelta = date - date
[time|timedelta] = time + timedelta
[time|timedelta] = time - timedelta
timedelta = time - time
datetime = datetime + timedelta
datetime = datetime - timedelta
datetime = date + time
datetime = date - time
timedelta = datetime - datetime
timedelta = datetime - date
timedelta = timedelta + timedelta
timedelta = timedelta - timedelta
In which, given the case that date has precision loss (for timedelta's with partial days), it is promoted to datetime. Similarly, given the case that time has precision loss (for timedelta's that yield a result of more than one day, or negative time), it is promoted to timedelta. However, I'm not fully comfortable with [time|timedelta]. It makes sense given the rest of the interface from parallelism and precision views, but I do think it might be more elegant to just wraparound the time to the proper hour, thus changing all the [time|timedelta]'s to simply time, but unfortunately that leaves us with lost precision.
In my opinion, the most valuable uses of operator overloading are situations where many input values can be combined. You'd never want to deal with:
concat(concat(concat("Hello", ", "), concat("World", "!")), '\n');
or
distance = sqrt(add(add(x*x, y*y), z*z));
So we overload math symbols to create a more intuitive syntax. Another way to deal with this problem is variadic functions, like + in Scheme.
With your date + time = datetime, it doesn't make sense to add datetime + datetime, datetime + time, or datetime + date, so you could never encounter a situation like those above.
In my opinion, once again, the right thing is to use a constructor method. In a language with strong typing like C++, you'd have DateTime(const Date &d, const Time &t). With Python's dynamic typing, I guess they gave the function a name, datetime.combine(date, time), to make the code clearer when the types of the input variables are not visible in the code.
I guess most important things are functionality and efficiency. Of course using a simple + operator will be easier to use, but i am not sure about functionality.
If we compare it to datetime.combine, What combine do is:
dt = date(2011,01,01)
tm = time(20,00)
dtm = datetime.combine(dt, tm)
For dtm
If dt is a date object and tm is a time object, than date info is taken from dt, time info and tzinfo is taken from tm object
if dt is a datetime object, than its time and tzinfo attributes will be ignored.
From that point of view, working with datetime objects do not seem to be simple objects, but more compex structures with diffrent attributes, like timezone info.
Probably thats why datetime objects have some additional functions that is used for formatting object type and data structure of the object.
Python have a motto (something like that):
In python, nothing is unchangable, if you know what you are doing. If not, it is better to leave library functions as they are...
So, in my opinion, it is better you use combine that overloading + operator

Categories