How to iterate over a timespan after days, hours, weeks and months? - python

How do I iterate over a timespan after days, hours, weeks or months?
Something like:
for date in foo(from_date, to_date, delta=HOURS):
print date
Where foo is a function, returning an iterator. I've been looking at the calendar module, but that only works for one specific year or month, not between dates.

Use dateutil and its rrule implementation, like so:
from dateutil import rrule
from datetime import datetime, timedelta
now = datetime.now()
hundredDaysLater = now + timedelta(days=100)
for dt in rrule.rrule(rrule.MONTHLY, dtstart=now, until=hundredDaysLater):
print dt
Output is
2008-09-30 23:29:54
2008-10-30 23:29:54
2008-11-30 23:29:54
2008-12-30 23:29:54
Replace MONTHLY with any of YEARLY, MONTHLY, WEEKLY, DAILY, HOURLY, MINUTELY, or SECONDLY. Replace dtstart and until with whatever datetime object you want.
This recipe has the advantage for working in all cases, including MONTHLY. Only caveat I could find is that if you pass a day number that doesn't exist for all months, it skips those months.

I don't think there is a method in Python library, but you can easily create one yourself using datetime module:
from datetime import date, datetime, timedelta
def datespan(startDate, endDate, delta=timedelta(days=1)):
currentDate = startDate
while currentDate < endDate:
yield currentDate
currentDate += delta
Then you could use it like this:
>>> for day in datespan(date(2007, 3, 30), date(2007, 4, 3),
>>> delta=timedelta(days=1)):
>>> print day
2007-03-30
2007-03-31
2007-04-01
2007-04-02
Or, if you wish to make your delta smaller:
>>> for timestamp in datespan(datetime(2007, 3, 30, 15, 30),
>>> datetime(2007, 3, 30, 18, 35),
>>> delta=timedelta(hours=1)):
>>> print timestamp
2007-03-30 15:30:00
2007-03-30 16:30:00
2007-03-30 17:30:00
2007-03-30 18:30:00

I achieved this using pandas and datetime libraries as follows. It was much more convenient for me.
import pandas as pd
from datetime import datetime
DATE_TIME_FORMAT = '%Y-%m-%d %H:%M:%S'
start_datetime = datetime.strptime('2018-05-18 00:00:00', DATE_TIME_FORMAT)
end_datetime = datetime.strptime('2018-05-23 13:00:00', DATE_TIME_FORMAT)
timedelta_index = pd.date_range(start=start_datetime, end=end_datetime, freq='H').to_series()
for index, value in timedelta_index.iteritems():
dt = index.to_pydatetime()
print(dt)

For iterating over months you need a different recipe, since timedeltas can't express "one month".
from datetime import date
def jump_by_month(start_date, end_date, month_step=1):
current_date = start_date
while current_date < end_date:
yield current_date
carry, new_month = divmod(current_date.month - 1 + month_step, 12)
new_month += 1
current_date = current_date.replace(year=current_date.year + carry,
month=new_month)
(NB: you have to subtract 1 from the month for the modulus operation then add it back to new_month, since months in datetime.dates start at 1.)

Month iteration approach:
def months_between(date_start, date_end):
months = []
# Make sure start_date is smaller than end_date
if date_start > date_end:
tmp = date_start
date_start = date_end
date_end = tmp
tmp_date = date_start
while tmp_date.month <= date_end.month or tmp_date.year < date_end.year:
months.append(tmp_date) # Here you could do for example: months.append(datetime.datetime.strftime(tmp_date, "%b '%y"))
if tmp_date.month == 12: # New year
tmp_date = datetime.date(tmp_date.year + 1, 1, 1)
else:
tmp_date = datetime.date(tmp_date.year, tmp_date.month + 1, 1)
return months
More code but it will do fine dealing with long periods of time checking that the given dates are in order...

Also can use the module arrow
https://arrow.readthedocs.io/en/latest/guide.html#ranges-spans
>>> start = datetime(2013, 5, 5, 12, 30)
>>> end = datetime(2013, 5, 5, 17, 15)
>>> for r in arrow.Arrow.range('hour', start, end):
... print(repr(r))
...
<Arrow [2013-05-05T12:30:00+00:00]>
<Arrow [2013-05-05T13:30:00+00:00]>
<Arrow [2013-05-05T14:30:00+00:00]>
<Arrow [2013-05-05T15:30:00+00:00]>
<Arrow [2013-05-05T16:30:00+00:00]>

This library provides a handy calendar tool: mxDateTime, that should be enough :)

You should modify this line to make this work correctly:
current_date = current_date.replace(year=current_date.year + carry,month=new_month,day=1)
;)

Related

How can I get the first day of the next month in Python?

How can I get the first date of the next month in Python? For example, if it's now 2019-12-31, the first day of the next month is 2020-01-01. If it's now 2019-08-01, the first day of the next month is 2019-09-01.
I came up with this:
import datetime
def first_day_of_next_month(dt):
'''Get the first day of the next month. Preserves the timezone.
Args:
dt (datetime.datetime): The current datetime
Returns:
datetime.datetime: The first day of the next month at 00:00:00.
'''
if dt.month == 12:
return datetime.datetime(year=dt.year+1,
month=1,
day=1,
tzinfo=dt.tzinfo)
else:
return datetime.datetime(year=dt.year,
month=dt.month+1,
day=1,
tzinfo=dt.tzinfo)
# Example usage (assuming that today is 2021-01-28):
first_day_of_next_month(datetime.datetime.now())
# Returns: datetime.datetime(2021, 2, 1, 0, 0)
Is it correct? Is there a better way?
Here is a 1-line solution using nothing more than the standard datetime library:
(dt.replace(day=1) + datetime.timedelta(days=32)).replace(day=1)
Examples:
>>> dt = datetime.datetime(2016, 2, 29)
>>> print((dt.replace(day=1) + datetime.timedelta(days=32)).replace(day=1))
2016-03-01 00:00:00
>>> dt = datetime.datetime(2019, 12, 31)
>>> print((dt.replace(day=1) + datetime.timedelta(days=32)).replace(day=1))
2020-01-01 00:00:00
>>> dt = datetime.datetime(2019, 12, 1)
>>> print((dt.replace(day=1) + datetime.timedelta(days=32)).replace(day=1))
2020-01-01 00:00:00
Using dateutil you can do it the most literally possible:
import datetime
from dateutil import relativedelta
today = datetime.date.today()
next_month = today + relativedelta.relativedelta(months=1, day=1)
In English: add 1 month(s) to the today's date and set the day (of the month) to 1. Note the usage of singular and plural forms of day(s) and month(s). Singular sets the attribute to a value, plural adds the number of periods.
You can store this relativedelta.relativedelta object to a variable and the pass it around. Other answers involve more programming logic.
EDIT You can do it with the standard datetime library as well, but it's not so beautiful:
next_month = (today.replace(day=1) + datetime.timedelta(days=32)).replace(day=1)
sets the date to the 1st of the current month, adds 32 days (or any number between 31 and 59 which guarantees to jump into the next month) and then sets the date to the 1st of that month.
you can use calendar to get the number of days in a given month, then add timedelta(days=...), like this:
from datetime import date, timedelta
from calendar import monthrange
days_in_month = lambda dt: monthrange(dt.year, dt.month)[1]
today = date.today()
first_day = today.replace(day=1) + timedelta(days_in_month(today))
print(first_day)
if you're fine with external deps, you can use dateutil (which I love...)
from datetime import date
from dateutil.relativedelta import relativedelta
today = date.today()
first_day = today.replace(day=1) + relativedelta(months=1)
print(first_day)
Extract the year and month, add 1 and form a new date using the year, month and day=1:
from datetime import date
now = date(2020,12,18)
y,m = divmod(now.year*12+now.month,12)
nextMonth = date(y,m+1,1)
print(now,nextMonth)
# 2020-12-18 2021-01-01
Your way looks good yet I would have done it this way:
import datetime
from dateutil import relativedelta
dt = datetime.datetime(year=1998,
month=12,
day=12)
nextmonth = dt + relativedelta.relativedelta(months=1)
nextmonth.replace(day=1)
print(nextmonth)
Using only python standard libraries:
import datetime
today = datetime.date.today()
first_of_next_month = return date.replace(
day=1,
month=date.month % 12 + 1,
year=date.year + (date.month // 12)
)
could be generalized to...
def get_first_of_month(date, month_offset=0):
# zero based indexing of month to make math work
month_count = date.month - 1 + month_offset
return date.replace(
day=1, month=month_count % 12 + 1, year=date.year + (month_count // 12)
)
first_of_next_month = get_first_of_month(today, 1)
Other solutions that don't require 3rd party libraries include:
Toby Petty's answer is another good option.
If the exact timedelta is helpful to you,
a slight modification on Adam.Er8's answer might be convenient:
import calendar, datetime
today = datetime.date.today()
time_until_next_month = datetime.timedelta(
calendar.monthrange(today.year, today.month)[1] - today.day + 1
)
first_of_next_month = today + time_until_next_month
With Zope's DateTime library a very simple solution is possible
from DateTime.DateTime import DateTime
date = DateTime() # today
while date.day() != 1:
date += 1
print(date)
I see so many wonderful solutions to this problem I personally was looking for a solution for getting the first and last day of the previous month when I stmbled on this question.
But here is a solution I like to think is quite simple and elegant:
date = datetime.datetime.now().date()
same_time_next_month = date + datetime.timedelta(days = date.day)
first_day_of_next_month_from_date = same_time_next_month - datetime.timedelta(days = same_time_next_month.day - 1)
Here we simply add the day of the target date to the date to get the same time of the next month, and then remove the number of days elapsed from the new date gotten.
Try this, for starting day of each month, change MonthEnd(1) to MonthBegin(1):
import pandas as pd
from pandas.tseries.offsets import MonthBegin, MonthEnd
date_list = (pd.date_range('2021-01-01', '2022-01-31',
freq='MS') + MonthEnd(1)).strftime('%Y-%m-%d').tolist()
date_list
Out:
['2021-01-31',
'2021-02-28',
'2021-03-31',
'2021-04-30',
'2021-05-31',
'2021-06-30',
'2021-07-31',
'2021-08-31',
'2021-09-30',
'2021-10-31',
'2021-11-30',
'2021-12-31',
'2022-01-31']
With python-dateutil:
from datetime import date
from dateutil.relativedelta import relativedelta
last day of current month:
date.today() + relativedelta(day=31)
first day of next month:
date.today() + relativedelta(day=31) + relativedelta(days=1)

How to convert datetime from decimal to ā€œ%y-%m-%d %H:%M:%Sā€ given the time origin?

I did my search around but I couldn't find an answer that satisfies my problem.
I am using python 3.7 and I need to convert a series of decimal numbers into datetime object in the form %Y-%m-%d %H:%M:%S. While doing so I need to consider an origin point, for example:
t = 0.000000 equals to 2016-06-25 00:00:00
t = 0.010417 equals to ..... ?
and so on. I know that in my decimal time the integer part is day since start, decimal part is fraction of day.
I have found an answer using R here. I also think that I might need to use the class method date.fromordinal(ordinal)or something similar but I cannot figure it out how to do it.
This is what I have tried so far:
example t = 1.010416
import datetime as DT
from datetime import timedelta
day = int(x)
datetime_object = DT.datetime.strptime("2016-06-25 00:00:00", '%Y-%m-%d %H:%M:%S')
python_datetime = DT.datetime.fromordinal(day) + timedelta(days=datetime_object.day-1)
I get:
datetime.datetime(1, 1, 25, 0, 0)
But I cannot add the year 2016 nor the month. Also, for every case in which int(t) = 0, I get:
ValueError: ordinal must be >= 1
Thank you very much for your answers
Just to leave a clear answer here, taking into account my comments on the other answers:
from datetime import datetime,timedelta
base_date = datetime(2016, 6, 25)
deltas = (2.34857, 0.010417, 1.010416)
for delta in deltas:
print((base_date + timedelta(delta)).strftime('%Y-%m-%d %H:%M:%S'))
That code yields the following ouput:
>>> from datetime import datetime,timedelta
>>>
>>> base_date = datetime(2016, 6, 25)
>>> deltas = (2.34857, 0.010417, 1.010416)
>>>
>>> for delta in deltas:
... print((base_date + timedelta(delta)).strftime('%Y-%m-%d %H:%M:%S'))
...
2016-06-27 08:21:56
2016-06-25 00:15:00
2016-06-26 00:14:59
>>>
timedelta stores its data in this format: (DAYS, SECONDS) so you can calculate it easily:
import datetime
t = 2.34857
# Full days
days = int(t)
# Part of a day
part_of_day = t - int(t)
seconds = int(part_of_day * 24 * 60 * 60)
# Calculate the time delta
dt = datetime.timedelta(
days=days,
seconds=seconds
)
# Add t-delta to the first day
first_day = datetime.datetime(2016, 6, 25)
current_time = first_day + dt
current_time
will return:
datetime.datetime(2016, 6, 27, 8, 21, 56)
Then you can convert it to a string with this function:
datetime.datetime.strftime(current_time, '%Y-%m-%d %H:%M:%S')
'2016-06-27 08:21:56'
Edit 1: Instead of constructing the timedelta by days-seconds, one can use just float days as parameter (thanks to accdias!):
dt = datetime.timedelta(days=t)

How to get a specific date from the previous month given the current date in python?

I want to get the 20th of previous month, given the current_date()
I am trying to use time.strftime but not able to subtract the value from it.
timestr = time.strftime("%Y-(%m-1)%d")
This is giving me error. The expected output is 2019-03-20 if my current_date is in April. Not sure how to go about it.
I read the posts from SO and most of them address getting the first day / last day of the month. Any help would be appreciated.
from datetime import date, timedelta
today = date.today()
last_day_prev_month = today - timedelta(days=today.day)
twenty_prev_month = last_day_prev_month.replace(day=20)
print(twenty_prev_month) # 2019-03-20
Use datetime.replace
import datetime
current_date = datetime.date.today()
new_date = current_date.replace(
month = current_date.month - 1,
day = 20
)
print(new_date)
#2019-03-20
Edit
That won't work for Jan so this is a workaround:
import datetime
current_date = datetime.date(2019, 2, 17)
month = current_date.month - 1
year = current_date.year
if not month:
month, year = 12, year - 1
new_date = datetime.date(year=year, month=month, day=20)
I imagine it is the way dates are parsed. It is my understanding that with your code it is looking for
2019-(03-1)20 or 2019-(12-1)15, etc..
Because the %y is not a variable, but a message about how the date is to be expected within a string of text, and other characters are what should be expected, but not processed (like "-")
This seems entirely not what you are going for. I would just parse the date like normal and then reformat it to be a month earlier:
import datetime
time = datetime.datetime.today()
print(time)
timestr = time.strftime("%Y-%m-%d")
year, month, day = timestr.split("-")
print("{}-{}-{}".format(year, int(month)-1, day))
This would be easier with timedelta objects, but sadly there isn't one for months, because they are of various lengths.
To be more robust if a new year is involved:
import datetime
time = datetime.datetime.today()
print(time)
timestr = time.strftime("%Y-%m-%d")
year, month, day = timestr.split("-")
if month in [1, "01", "1"]: # I don't remember how January is represented
print("{}-{}-{}".format(int(year) - 1, 12, day)) # use December of last year
else:
print("{}-{}-{}".format(year, int(month)-1, day))
This will help:
from datetime import date, timedelta
dt = date.today() - timedelta(30)// timedelta(days No.)
print('Current Date :',date.today())
print(dt)
It is not possible to do math inside a string passed to time.strftime, but you can do something similar to what you're asking very easily using the time module
in Python 3
# Last month
t = time.gmtime()
print(f"{t.tm_year}-{t.tm_mon-1}-20")
or in Python 2
print("{0}-{1}-{2}".format(t.tm_year, t.tm_mon -1, 20))
If you have fewer constraints, you can just use the datetime module instead.
You could use datetime, dateutil or arrow to find the 20th day of the previous month. See examples below.
Using datetime:
from datetime import date
d = date.today()
month, year = (d.month-1, d.year) if d.month != 1 else (12, d.year-1)
last_month = d.replace(day=20, month=month, year=year)
print(last_month)
Using datetime and timedelta:
from datetime import date
from datetime import timedelta
d = date.today()
last_month = (d - timedelta(days=d.day)).replace(day=20)
print(last_month)
Using datetime and dateutil:
from datetime import date
from dateutil.relativedelta import relativedelta # pip install python-dateutil
d = date.today()
last_month = d.replace(day=20) - relativedelta(months=1)
print(last_month)
Using arrow:
import arrow # pip install arrow
d = arrow.now()
last_month = d.shift(months=-1).replace(day=20).datetime.date()
print(last_month)

python: adding any number of days to date

I am hoping someone can point me in the right direction in working with dates and timedelta.
my understanding is that to add any number (ex: 10 days) to a date, you need to convert it to a timedelta.
if that is correct, how can I add any number to a date when it is an integer?
any documentation or links would be great - thank you.
code example, my date is as follows:
x = 20100103 (formatted as YYYYMMDD)
>>> import datetime
>>> today = datetime.datetime.now().date()
>>> today
datetime.date(2016, 6, 14)
>>> today + datetime.timedelta(days=10)
datetime.date(2016, 6, 24)
There's no need to convert it to a timedelta. Just use the timedelta function, if you want to add days, use days=N, for hours, timedelta(hours=20)
x=20100103
x2 = int((datetime.datetime.strptime(str(x),"%Y%m%d") + datetime.timedelta(days=10)).strftime("%Y%m%d"))
to break it down
x=20100103
x_as_datetime = datetime.datetime.strptime(str(x),"%Y%m%d")
new_datetime = x_as_datetime + datetime.timedelta(days=10) #add your timedelta
x2 = new_datetime.strftime("%Y%m%d") # format it back how you want
int(x2) # if you just want an integer ...
from datetime import datetime
from datetime import timedelta
StartDate = "20100103"
Date = datetime.strptime(StartDate, "%Y%m%d")
EndDate = Date + timedelta(days=10)

How do I find the nth day of the next month in Python?

I am trying to get the date delta by subtracting today's date from the nth day of the next month.
delta = nth_of_next_month - todays_date
print delta.days
How do you get the date object for the 1st (or 2nd, 3rd.. nth) day of the next month. I tried taking the month number from the date object and increasing it by 1. Which is obviously a dumb idea because 12 + 1 = 13. I also tried adding one month to today and tried to get to the first of the month. I am sure that there is a much more efficient way of doing this.
The dateutil library is useful for this:
from dateutil.relativedelta import relativedelta
from datetime import datetime
# Where day is the day you want in the following month
dt = datetime.now() + relativedelta(months=1, day=20)
This should be straightforward unless I'm missing something in your question:
import datetime
now = datetime.datetime.now()
nth_day = 5
next_month = now.month + 1 if now.month < 12 else 1 # February
year = now.year if now.month < 12 else now.year+1
nth_of_next_month = datetime.datetime(year, next_month, nth_day)
print(nth_of_next_month)
Result:
2014-02-05 00:00:00
Using dateutil as suggested in another answer is a much better idea than this, though.
Another alternative is to use delorean library:
Delorean is a library that provides easy and convenient datetime
conversions in Python.
>>> from delorean import Delorean
>>> d = Delorean()
>>> d.next_month()
Delorean(datetime=2014-02-15 18:51:14.325350+00:00, timezone=UTC)
>>> d.next_month().next_day(2)
Delorean(datetime=2014-02-17 18:51:14.325350+00:00, timezone=UTC)
My approach to calculating the next month without external libraries:
def nth_day_of_next_month(dt, n):
return dt.replace(
year=dt.year + (dt.month // 12), # +1 for december, +0 otherwise
month=(dt.month % 12) + 1, # december becomes january
day=n)
This works for both datetime.datetime() and datetime.date() objects.
Demo:
>>> import datetime
>>> def nth_day_of_next_month(dt, n):
... return dt.replace(year=dt.year + (dt.month // 12), month=(dt.month % 12) + 1, day=n)
...
>>> nth_day_of_next_month(datetime.datetime.now(), 4)
datetime.datetime(2014, 2, 4, 19, 20, 51, 177860)
>>> nth_day_of_next_month(datetime.date.today(), 18)
datetime.date(2014, 2, 18)
Without using any external library, this can be achived as follows
from datetime import datetime, timedelta
def nth_day_of_next_month(n):
today = datetime.now()
next_month_dt = today + timedelta(days=32-today.day)
return next_month_dt.replace(day=n)

Categories