571

I have the following code to do this, but how can I do it better? Right now I think it's better than nested loops, but it starts to get Perl-one-linerish when you have a generator in a list comprehension.

day_count = (end_date - start_date).days + 1
for single_date in [d for d in (start_date + timedelta(n) for n in range(day_count)) if d <= end_date]:
    print strftime("%Y-%m-%d", single_date.timetuple())

Notes

  • I'm not actually using this to print. That's just for demo purposes.
  • The start_date and end_date variables are datetime.date objects because I don't need the timestamps. (They're going to be used to generate a report).

Sample Output

For a start date of 2009-05-30 and an end date of 2009-06-09:

2009-05-30
2009-05-31
2009-06-01
2009-06-02
2009-06-03
2009-06-04
2009-06-05
2009-06-06
2009-06-07
2009-06-08
2009-06-09
7
  • 5
    Just to point out: I don't think there's any difference between 'time.strftime("%Y-%m-%d", single_date.timetuple())' and the shorter 'single_date.strftime("%Y-%m-%d")'. Most answers seem to be copying the longer style. Commented Sep 21, 2010 at 13:20
  • 15
    Wow, these answers are much too complicated. Try this: stackoverflow.com/questions/7274267/… Commented Sep 19, 2012 at 19:47
  • @GringoSuave: what is complicated about Sean Cavanagh's answer? Commented Apr 24, 2015 at 0:30
  • 2
    @GringoSuave That link is a duplicate to: stackoverflow.com/questions/1060279/… Commented Apr 19, 2017 at 17:52
  • Does this answer your question? Creating a range of dates in Python Commented Feb 18, 2020 at 2:38

30 Answers 30

821

Why are there two nested iterations? For me it produces the same list of data with only one iteration:

for single_date in (start_date + timedelta(n) for n in range(day_count)):
    print ...

And no list gets stored, only one generator is iterated over. Also the "if" in the generator seems to be unnecessary.

After all, a linear sequence should only require one iterator, not two.

Update after discussion with John Machin:

Maybe the most elegant solution is using a generator function to completely hide/abstract the iteration over the range of dates:

from datetime import date, timedelta

def daterange(start_date: date, end_date: date):
    days = int((end_date - start_date).days)
    for n in range(days):
        yield start_date + timedelta(n)

start_date = date(2013, 1, 1)
end_date = date(2015, 6, 2)
for single_date in daterange(start_date, end_date):
    print(single_date.strftime("%Y-%m-%d"))

NB: For consistency with the built-in range() function this iteration stops before reaching the end_date. So for inclusive iteration use the next day, as you would with range().

Sign up to request clarification or add additional context in comments.

16 Comments

@John Machin: Okay. I do however prever an iteration over while loops with explicit incrementation of some counter or value. The interation pattern is more pythonic (at least in my personal view) and also more general, as it allows to express an iteration while hiding the details of how that iteration is done.
@Ber: I don't like it at all; it's DOUBLY bad. You ALREADY had an iteration! By wrapping the complained-about constructs in a generator, you have added even more execution overhead plus diverted the user's attention to somewhere else to read your 3-liner's code and/or docs. -2
@John Machin: I disagree. The point is not about reducing the number of lines to the absolute minimum. After all, we're not talking Perl here. Also, my code does only one iteration (that's how the generator works, but I guess you know that). *** My point is about abstracting concepts for re-use and self explanatory code. I maintain that this is far more worthwhile than have the shortest code possible.
If you're going for terseness you can use a generator expression: (start_date + datetime.timedelta(n) for n in range((end_date - start_date).days))
for n in range(int ((end_date - start_date).days+1)): For the end_date to be included
|
379

This might be more clear:

from datetime import date, timedelta

start_date = date(2019, 1, 1)
end_date = date(2020, 1, 1)
delta = timedelta(days=1)
while start_date <= end_date:
    print(start_date.strftime("%Y-%m-%d"))
    start_date += delta

3 Comments

Very clear and short, but doesn't work well if you want to use continue
This is good, but I do marginally prefer solutions using a for loop, hence the mechanism of iteration (i.e. the dding of one day) gets hidden within the sequence the for loop is iterating over, rather than having to pollute the body of the loop. That makes the mechanism reusable in other contexts. It's not a big deal.
There is a bug, if start and end are datetimes (which is a subclass of date, and hence should be substitutable) and the time portion of end is less than the time portion of start, then you will terminate the loop one day early.
210

Use the dateutil library:

from datetime import date
from dateutil.rrule import rrule, DAILY

a = date(2009, 5, 30)
b = date(2009, 6, 9)

for dt in rrule(DAILY, dtstart=a, until=b):
    print dt.strftime("%Y-%m-%d")

This python library has many more advanced features, some very useful, like relative deltas—and is implemented as a single file (module) that's easily included into a project.

3 Comments

Note that the final date in the for loop here is inclusive of until whereas the final date of the daterange method in Ber's answer is exclusive of end_date.
downvote for using an external library when the stdlib handles this just fine
134

Pandas is great for time series in general, and has direct support for date ranges.

import pandas as pd
daterange = pd.date_range(start_date, end_date)

You can then loop over the daterange to print the date:

for single_date in daterange:
    print (single_date.strftime("%Y-%m-%d"))

It also has lots of options to make life easier. For example if you only wanted weekdays, you would just swap in bdate_range. See http://pandas.pydata.org/pandas-docs/stable/timeseries.html#generating-ranges-of-timestamps

The power of Pandas is really its dataframes, which support vectorized operations (much like numpy) that make operations across large quantities of data very fast and easy.

EDIT: You could also completely skip the for loop and just print it directly, which is easier and more efficient:

print(daterange)

3 Comments

"much like numpy" - Pandas is built on numpy :P
You can also use floor, round,ceil to start on exact dates: pd.date_range(start=start_date.floor('d'),end=end_date.floor('d'), freq = 'd')
downvote for using an external library when the stdlib handles this just fine
19

This is the most human-readable solution I can think of.

import datetime

def daterange(start, end, step=datetime.timedelta(1)):
    curr = start
    while curr < end:
        yield curr
        curr += step

2 Comments

This is fine, but I marginally prefer solutions using a for loop, then you can bundle the date range logic, including the mechanism that adds one day, into the sequence, allowing the sequence to be used in other contexts.
There is a bug, if start and end are datetimes (which is a subclass of date) and the time portion of end is less than the time portion of start, then you will terminate the loop one day early.
18
import datetime

def daterange(start, stop, step=datetime.timedelta(days=1), inclusive=False):
  # inclusive=False to behave like range by default
  if step.days > 0:
    while start < stop:
      yield start
      start = start + step
      # not +=! don't modify object passed in if it's mutable
      # since this function is not restricted to
      # only types from datetime module
  elif step.days < 0:
    while start > stop:
      yield start
      start = start + step
  if inclusive and start == stop:
    yield start

# ...

for date in daterange(start_date, end_date, inclusive=True):
  print strftime("%Y-%m-%d", date.timetuple())

This function does more than you strictly require, by supporting negative step, etc. As long as you factor out your range logic, then you don't need the separate day_count and most importantly the code becomes easier to read as you call the function from multiple places.

6 Comments

Thanks, renamed to more closely match range's parameters, forgot to change in the body.
+1 ... but as you are allowing the step to be a timedelta, you should either (a) call it dateTIMErange() and make steps of e.g. timedelta(hours=12) and timedelta(hours=36) work properly or (b) trap steps that aren't an integral number of days or (c) save the caller the hassle and express the step as a number of days instead of a timedelta.
Any timedelta should work already, but I did add datetime_range and date_range to my personal scrap collection after writing this, because of (a). Not sure another function is worthwhile for (c), the most common case of days=1 is already taken care of, and having to pass an explicit timedelta avoids confusion. Maybe uploading it somewhere is best: bitbucket.org/kniht/scraps/src/tip/python/gen_range.py
to make this work on increments other than days you should check against step.total_seconds(), and not step.days
Downvoted because this is much more complicated than an answer to the OPs question needs to be.
|
15

Why not try:

import datetime as dt

start_date = dt.datetime(2012, 12,1)
end_date = dt.datetime(2012, 12,5)

total_days = (end_date - start_date).days + 1 #inclusive 5 days

for day_number in range(total_days):
    current_date = (start_date + dt.timedelta(days = day_number)).date()
    print current_date

1 Comment

Be aware, there is a bug, if start and end are datetimes (which is a subclass of date, and so should be substitutable), and if you expect that any date which is even partially included in the date range should be included in the output (which I think would be the normal expectation?). If the time portion of end is less than the time portion of start, then you will terminate the loop one day early.
15

Numpy's arange function can be applied to dates:

import numpy as np
from datetime import datetime, timedelta
d0 = datetime(2009, 1,1)
d1 = datetime(2010, 1,1)
dt = timedelta(days = 1)
dates = np.arange(d0, d1, dt).astype(datetime)

The use of astype is to convert from numpy.datetime64 to an array of datetime.datetime objects.

5 Comments

Super lean construction ! The last line works for me with dates = np.arange(d0, d1, dt).astype(datetime.datetime)
+1 for posting a generic one-liner solution which allows any timedelta, instead of a fixed rounded step such as hourly/minutely/… .
Downvoted for using external library when the stdlib handles this just fine
@JonathanHartley Could you give an example? I get a TypeError if I try this with range
Ah, I beg your pardon for being misleading. I didn't mean that range() works with stdlib dates, I meant that stdlib dates can be used to neatly solve the OP's problem. See my own answer as one example.
10

For completeness, Pandas also has a period_range function for timestamps that are out of bounds:

import pandas as pd

pd.period_range(start='1/1/1626', end='1/08/1627', freq='D')

1 Comment

downvoted for using external library when the stdlib handles this just fine
10

Show the next n days from today:

import datetime
for i in range(0, 100):
    print((datetime.date.today() + datetime.timedelta(i)).isoformat())

Output:

2016-06-29
2016-06-30
2016-07-01
2016-07-02
2016-07-03
2016-07-04
...

6 Comments

Please add round brackets, like print((datetime.date.today() + datetime.timedelta(i)).isoformat())
@TitanFighter please feel free to do edits, I'll accept them.
I tried. Editing requires minimum 6 chars, but in this case it is necessary to add just 2 chars, "(" and ")"
print((datetime.date.today() + datetime.timedelta(i))) without the .isoformat() gives exactly the same output. I need my script to print YYMMDD. Anyone know how to do that?
Just do this in the for loop instead of the print statement d = datetime.date.today() + datetime.timedelta(i); d.strftime("%Y%m%d")
|
6
import datetime

def daterange(start, stop, step_days=1):
    current = start
    step = datetime.timedelta(step_days)
    if step_days > 0:
        while current < stop:
            yield current
            current += step
    elif step_days < 0:
        while current > stop:
            yield current
            current += step
    else:
        raise ValueError("daterange() step_days argument must not be zero")

if __name__ == "__main__":
    from pprint import pprint as pp
    lo = datetime.date(2008, 12, 27)
    hi = datetime.date(2009, 1, 5)
    pp(list(daterange(lo, hi)))
    pp(list(daterange(hi, lo, -1)))
    pp(list(daterange(lo, hi, 7)))
    pp(list(daterange(hi, lo, -7))) 
    assert not list(daterange(lo, hi, -1))
    assert not list(daterange(hi, lo))
    assert not list(daterange(lo, hi, -7))
    assert not list(daterange(hi, lo, 7)) 

1 Comment

Downvoting due to complexity. This should be a two-liner.
6
for i in range(16):
    print datetime.date.today() + datetime.timedelta(days=i)

2 Comments

Love the straightforwardness.
In my humble opinion, this answer and my own (stackoverflow.com/a/79672545/10176) are the only two reasonable answers on this page.
6

You can generate a series of date between two dates using the pandas library simply and trustfully

import pandas as pd

print pd.date_range(start='1/1/2010', end='1/08/2018', freq='M')

You can change the frequency of generating dates by setting freq as D, M, Q, Y (daily, monthly, quarterly, yearly )

1 Comment

Downvoting due to using external library when stdlib handles this just fine
4
import datetime
from dateutil.rrule import DAILY,rrule

date=datetime.datetime(2019,1,10)

date1=datetime.datetime(2019,2,2)

for i in rrule(DAILY , dtstart=date,until=date1):
     print(i.strftime('%Y%b%d'),sep='\n')

OUTPUT:

2019Jan10
2019Jan11
2019Jan12
2019Jan13
2019Jan14
2019Jan15
2019Jan16
2019Jan17
2019Jan18
2019Jan19
2019Jan20
2019Jan21
2019Jan22
2019Jan23
2019Jan24
2019Jan25
2019Jan26
2019Jan27
2019Jan28
2019Jan29
2019Jan30
2019Jan31
2019Feb01
2019Feb02

2 Comments

Welcome to Stack Overflow! While this code may solve the question, including an explanation of how and why this solves the problem, especially on questions with too many good answers, would really help to improve the quality of your post, and probably result in more upvotes. Remember that you are answering the question for readers in the future, not just the person asking now. Please edit your answer to add explanations and give an indication of what limitations and assumptions apply. From Review
downvoting due to using external library when the stdlib handles this just fine
4

Using pendulum.period:

import pendulum

start = pendulum.from_format('2020-05-01', 'YYYY-MM-DD', formatter='alternative')
end = pendulum.from_format('2020-05-02', 'YYYY-MM-DD', formatter='alternative')

period = pendulum.period(start, end)

for dt in period:
    print(dt.to_date_string())

1 Comment

Downvote due to using external library when stdlib handles this just fine.
4

For those who are interested in Pythonic functional way:

from datetime import date, timedelta
from itertools import count, takewhile

for d in takewhile(lambda x: x<=date(2009,6,9), map(lambda x:date(2009,5,30)+timedelta(days=x), count())):
    print(d)

1 Comment

Downvoting due to extra complexity over a straightforward solution
3

I have a similar problem, but I need to iterate monthly instead of daily.

This is my solution

import calendar
from datetime import datetime, timedelta

def days_in_month(dt):
    return calendar.monthrange(dt.year, dt.month)[1]

def monthly_range(dt_start, dt_end):
    forward = dt_end >= dt_start
    finish = False
    dt = dt_start

    while not finish:
        yield dt.date()
        if forward:
            days = days_in_month(dt)
            dt = dt + timedelta(days=days)            
            finish = dt > dt_end
        else:
            _tmp_dt = dt.replace(day=1) - timedelta(days=1)
            dt = (_tmp_dt.replace(day=dt.day))
            finish = dt < dt_end

Example #1

date_start = datetime(2016, 6, 1)
date_end = datetime(2017, 1, 1)

for p in monthly_range(date_start, date_end):
    print(p)

Output

2016-06-01
2016-07-01
2016-08-01
2016-09-01
2016-10-01
2016-11-01
2016-12-01
2017-01-01

Example #2

date_start = datetime(2017, 1, 1)
date_end = datetime(2016, 6, 1)

for p in monthly_range(date_start, date_end):
    print(p)

Output

2017-01-01
2016-12-01
2016-11-01
2016-10-01
2016-09-01
2016-08-01
2016-07-01
2016-06-01

1 Comment

Downvoting due to not answering the OP's question
2
> pip install DateTimeRange

from datetimerange import DateTimeRange

def dateRange(start, end, step):
        rangeList = []
        time_range = DateTimeRange(start, end)
        for value in time_range.range(datetime.timedelta(days=step)):
            rangeList.append(value.strftime('%m/%d/%Y'))
        return rangeList

    dateRange("2018-09-07", "2018-12-25", 7)  

    Out[92]: 
    ['09/07/2018',
     '09/14/2018',
     '09/21/2018',
     '09/28/2018',
     '10/05/2018',
     '10/12/2018',
     '10/19/2018',
     '10/26/2018',
     '11/02/2018',
     '11/09/2018',
     '11/16/2018',
     '11/23/2018',
     '11/30/2018',
     '12/07/2018',
     '12/14/2018',
     '12/21/2018']

1 Comment

Downvote due to using external library when stdlib handles this just fine.
2
from datetime import date,timedelta
delta = timedelta(days=1)
start = date(2020,1,1)
end=date(2020,9,1)
loop_date = start
while loop_date<=end:
    print(loop_date)
    loop_date+=delta

1 Comment

I downvoted this because I thought it required too much code. Maybe that was harsh. If I added the declarations of 'start' and 'end' then my own solution would be, um, 3 lines shorter. I guess that is a real difference. OK, I feel a bit guilty but stand by my downvote.
2

You can use Arrow:

This is example from the docs, iterating over hours:

from arrow import Arrow

>>> start = datetime(2013, 5, 5, 12, 30)
>>> end = datetime(2013, 5, 5, 17, 15)
>>> for r in 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]>

To iterate over days, you can use like this:

>>> start = Arrow(2013, 5, 5)
>>> end = Arrow(2013, 5, 5)
>>> for r in Arrow.range('day', start, end):
...     print repr(r)

(Didn't check if you can pass datetime.date objects, but anyways Arrow objects are easier in general)

1 Comment

Downvote due to using external library when stdlib handles this just fine
2

If you are going to use dynamic timedelta then you can use:

1. With while loop

def datetime_range(start: datetime, end: datetime, delta: timedelta) -> Generator[datetime, None, None]:
    while start <= end:
        yield start
        start += delta

2. With for loop

from datetime import datetime, timedelta
from typing import Generator


def datetime_range(start: datetime, end: datetime, delta: timedelta) -> Generator[datetime, None, None]:
    delta_units = int((end - start) / delta)

    for _ in range(delta_units + 1):
        yield start
        start += delta

3. If you are using async/await

async def datetime_range(start: datetime, end: datetime, delta: timedelta) -> AsyncGenerator[datetime, None]:
    delta_units = int((end - start) / delta)

    for _ in range(delta_units + 1):
        yield start
        start += delta

4. List comprehension

def datetime_range(start: datetime, end: datetime, delta: timedelta) -> List[datetime]:
    delta_units = int((end - start) / delta)
    return [start + (delta * index) for index in range(delta_units + 1)]

Then 1 and 2 solutions simply can be used like this

start = datetime(2020, 10, 10, 10, 00)
end = datetime(2022, 10, 10, 18, 00)
delta = timedelta(minutes=30)

result = [time_part for time_part in datetime_range(start, end, delta)]
# or 
for time_part in datetime_range(start, end, delta):
    print(time_part)

3-third solution can be used like this in async context. Because it retruns an async generator object, which can be used only in async context

start = datetime(2020, 10, 10, 10, 00)
end = datetime(2022, 10, 10, 18, 00)
delta = timedelta(minutes=30)

result = [time_part async for time_part in datetime_range(start, end, delta)]

async for time_part in datetime_range(start, end, delta):
    print(time_part)

The benefit of the solutions about is that all of them are using dynamic timedelta. This can be very usefull in cases when you do not know which time delta you will have.

3 Comments

The inclusion of dynamic timedelta is a needless complication that the OP (and most readers) do not want. Downvoting because other solutions are significantly simpler.
Sorry for the inconvenience of seeing more than 3 lines of code — some people do enjoy understanding things beyond the bare minimum. This answer is for them.
Hey. Sorry to be harsh, but I will vote for answers that answer the question that was asked, not a different one.
1

What about the following for doing a range incremented by days:

for d in map( lambda x: startDate+datetime.timedelta(days=x), xrange( (stopDate-startDate).days ) ):
  # Do stuff here
  • startDate and stopDate are datetime.date objects

For a generic version:

for d in map( lambda x: startTime+x*stepTime, xrange( (stopTime-startTime).total_seconds() / stepTime.total_seconds() ) ):
  # Do stuff here
  • startTime and stopTime are datetime.date or datetime.datetime object (both should be the same type)
  • stepTime is a timedelta object

Note that .total_seconds() is only supported after python 2.7 If you are stuck with an earlier version you can write your own function:

def total_seconds( td ):
  return float(td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6

2 Comments

xrange is python2 only. Otherwise, this is a reasonable answer, I think, other than I marginally would prefer a simple incrementing integer to generate the dates, rather than a full timedelta addition.
I've changed my mind. Downvoting because the use of map and lambda make this slightly more complicated to understand than it needs to be.
1

This function has some extra features:

  • can pass a string matching the DATE_FORMAT for start or end and it is converted to a date object
  • can pass a date object for start or end
  • error checking in case the end is older than the start

    import datetime
    from datetime import timedelta
    
    
    DATE_FORMAT = '%Y/%m/%d'
    
    def daterange(start, end):
          def convert(date):
                try:
                      date = datetime.datetime.strptime(date, DATE_FORMAT)
                      return date.date()
                except TypeError:
                      return date
    
          def get_date(n):
                return datetime.datetime.strftime(convert(start) + timedelta(days=n), DATE_FORMAT)
    
          days = (convert(end) - convert(start)).days
          if days <= 0:
                raise ValueError('The start date must be before the end date.')
          for n in range(0, days):
                yield get_date(n)
    
    
    start = '2014/12/1'
    end = '2014/12/31'
    print list(daterange(start, end))
    
    start_ = datetime.date.today()
    end = '2015/12/1'
    print list(daterange(start, end))
    

3 Comments

The string conversions and other extra features are not what the OP asks for, and makes it really hard to trust the code without significant tests. A two liner is the appropriate answer to this question.
So what is the two-liner? Why can it be trusted without tests?
Hi! The two-liner is in my recent answer stackoverflow.com/a/79672545/10176, and you are right that no code can be trusted without tests, but testing two lines is easier than testing 20.
1

Here's code for a general date range function, similar to Ber's answer, but more flexible:

def count_timedelta(delta, step, seconds_in_interval):
    """Helper function for iterate.  Finds the number of intervals in the timedelta."""
    return int(delta.total_seconds() / (seconds_in_interval * step))


def range_dt(start, end, step=1, interval='day'):
    """Iterate over datetimes or dates, similar to builtin range."""
    intervals = functools.partial(count_timedelta, (end - start), step)

    if interval == 'week':
        for i in range(intervals(3600 * 24 * 7)):
            yield start + datetime.timedelta(weeks=i) * step

    elif interval == 'day':
        for i in range(intervals(3600 * 24)):
            yield start + datetime.timedelta(days=i) * step

    elif interval == 'hour':
        for i in range(intervals(3600)):
            yield start + datetime.timedelta(hours=i) * step

    elif interval == 'minute':
        for i in range(intervals(60)):
            yield start + datetime.timedelta(minutes=i) * step

    elif interval == 'second':
        for i in range(intervals(1)):
            yield start + datetime.timedelta(seconds=i) * step

    elif interval == 'millisecond':
        for i in range(intervals(1 / 1000)):
            yield start + datetime.timedelta(milliseconds=i) * step

    elif interval == 'microsecond':
        for i in range(intervals(1e-6)):
            yield start + datetime.timedelta(microseconds=i) * step

    else:
        raise AttributeError("Interval must be 'week', 'day', 'hour' 'second', \
            'microsecond' or 'millisecond'.")

1 Comment

Neither the OP, nor the majority of readers, will want this flexibility. A two-liner is the appropriate answer to this question.
1

in polars it can be done as well, if eager is set to True

start = datetime.date(year=2009, month=5, day=30)
end = datetime.date(year=2009, month=6, day=9)
dates = pl.date_range(start, end, eager=True)

for date in dates:
    print(date)

2 Comments

In polars v1, if your interval is not in 1d increments, use pl.datetime_range instead.
Voting down just for using an external library, when the stdlib has always handled this just fine.
0

Slightly different approach to reversible steps by storing range args in a tuple.

def date_range(start, stop, step=1, inclusive=False):
    day_count = (stop - start).days
    if inclusive:
        day_count += 1

    if step > 0:
        range_args = (0, day_count, step)
    elif step < 0:
        range_args = (day_count - 1, -1, step)
    else:
        raise ValueError("date_range(): step arg must be non-zero")

    for i in range(*range_args):
        yield start + timedelta(days=i)

3 Comments

Parameterizing the step value makes this way more complicated than the OP asks for. It should be a two liner, surely?
You are making a lot of assumptions. It's also possible the OP wanted a comprehensive solution, surely?
Hey. Yes, that is possible. But in defense of my downvote, I think that if the OP wanted that, then they would have asked for that. They did not, and I think that it takes fewer assumptions to answer the question that was actually asked than it does to speculate on some larger question instead.
0

Here is an alternative solution based on leftjoin solution.

For pendulum below 3.0.0

def test_iteration():
    start = pendulum.from_format('2018-01', 'YYYY-MM')
    end = pendulum.from_format('2020-01', 'YYYY-MM')

    interval = pendulum.period(start, end)

    for dt in interval.range('months'):
        print(dt.format('YYYY-MM'))

And for pendulum 3.0.0 and above (they have renamed period to interval) https://github.com/sdispater/pendulum/pull/676

def test_iteration():
    start = pendulum.from_format('2018-01', 'YYYY-MM')
    end = pendulum.from_format('2020-01', 'YYYY-MM')

    interval = pendulum.interval(start, end)

    for dt in interval.range('months'):
        print(dt.format('YYYY-MM'))

You can change de range values from the list on the documentation here : https://pendulum.eustace.io/docs/#range

Supported units for range() are: years, months, weeks, days, hours, minutes, seconds and microseconds

1 Comment

Using external library which results in WAY more code than just using the stdlib
0

Lots of answers here. All of them are terrible. :-D

from datetime import date
for o in range(start.toordinal(), end.toordinal() + 1):
    print(date.fromordinal(o))

to/from ordinal converts dates to and from monotonic integers.

Life is easier when you know how to live.

Like a lot of solutions here, this works for input values that are dates or datetimes (which are a subclass).

1 Comment

Thanks, that is far more simpler than my solution !
0

The more_itertools package provides the numeric_range function, which allows the following:

from more_itertools import numeric_range

for single_date in numeric_range(start_date, end_date, timedelta(days=1)):
    ...

Comments

-1

Answer for Python API Call Loop

using datetime and timedelta from the datetime module

from datetime import datetime, timedelta

import requests


headers = {"accept": "application/json", "x-api-key": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}

delta = timedelta(days=30)
start = datetime(year=2022, month=1, day=1)
num_ranges = 10

for _ in range(num_ranges):
    start_str = start.strftime("%Y-%m-%d")
    end = start + delta
    end_str = end.strftime("%Y-%m-%d")
    url = f"https://apiendpoint.com/v2/dapps/xxxx/history/uaw?dateFrom={start_str}&dateTo={end_str}"
    response = requests.request("GET", url, headers=headers)
    # TODO: append response json to result
    start = end

1 Comment

This answer contains lots of code, most of which has nothing to do with the question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.