Iterate over chunks of date range












4












$begingroup$


def forward_date_range(start_dt, end_dt, span_days):
#
# Generate tuples with intervals from given range of dates (forward)
#
# forward_date_range('2012-01-01', '2012-01-5', 2)
#
# 1st yield = ('2012-01-01', '2012-01-03')
# 2nd yield = ('2012-01-04', '2012-01-05')
start_dt = datetime.datetime.strptime(start_dt, '%Y-%m-%d')
end_dt = datetime.datetime.strptime(end_dt, '%Y-%m-%d')
span = datetime.timedelta(days=span_days)
step = datetime.timedelta(days=1)

while start_dt + span < end_dt:
current = start_dt + span
yield start_dt.strftime('%Y-%m-%d'), current.strftime('%Y-%m-%d')
start_dt = current + step
else:
yield start_dt.strftime('%Y-%m-%d'), end_dt.strftime('%Y-%m-%d')

def backward_date_range(start_dt, end_dt, span_days):
#
# Generate tuples with intervals from given range of dates (backward)
#
# backward_date_range('2012-01-01', '2012-01-5', 2)
#
# 1st yield = ('2012-01-03', '2012-01-05')
# 2nd yield = ('2012-01-01', '2012-01-02')
start_dt = datetime.datetime.strptime(start_dt, '%Y-%m-%d')
end_dt = datetime.datetime.strptime(end_dt, '%Y-%m-%d')
span = datetime.timedelta(days=span_days)
step = datetime.timedelta(days=1)

while end_dt - span > start_dt:
current = end_dt - span
yield current.strftime('%Y-%m-%d'), end_dt.strftime('%Y-%m-%d')
end_dt = current - step
else:
yield start_dt.strftime('%Y-%m-%d'), end_dt.strftime('%Y-%m-%d')


This is currently what I have, needless to say it looks ugly. Is there a more elegant solution, especially if it is possible to get rid of the while loop altogether? or maybe compress it into one single line?



I am currently using python3.5










share|improve this question











$endgroup$












  • $begingroup$
    Hi, you might be able to get better reviews by providing a bit more context. Can you add a bit of explanation of why you need such feature and/or some code calling these functions?
    $endgroup$
    – Mathias Ettinger
    Apr 30 '16 at 8:42










  • $begingroup$
    @MathiasEttinger its a helper function that is used to supply start and end "range subsets" to SQL queries that wouldve clogged up a network. Something like "1996-2016" is split into smaller chunks for convenience
    $endgroup$
    – AlanSTACK
    Apr 30 '16 at 10:40










  • $begingroup$
    @Alan: Surely adding LIMIT to the SQL queries would have been a better solution to this problem?
    $endgroup$
    – Gareth Rees
    May 8 '16 at 17:04










  • $begingroup$
    @GarethRees The data structure of this case prevents this
    $endgroup$
    – AlanSTACK
    May 9 '16 at 2:19
















4












$begingroup$


def forward_date_range(start_dt, end_dt, span_days):
#
# Generate tuples with intervals from given range of dates (forward)
#
# forward_date_range('2012-01-01', '2012-01-5', 2)
#
# 1st yield = ('2012-01-01', '2012-01-03')
# 2nd yield = ('2012-01-04', '2012-01-05')
start_dt = datetime.datetime.strptime(start_dt, '%Y-%m-%d')
end_dt = datetime.datetime.strptime(end_dt, '%Y-%m-%d')
span = datetime.timedelta(days=span_days)
step = datetime.timedelta(days=1)

while start_dt + span < end_dt:
current = start_dt + span
yield start_dt.strftime('%Y-%m-%d'), current.strftime('%Y-%m-%d')
start_dt = current + step
else:
yield start_dt.strftime('%Y-%m-%d'), end_dt.strftime('%Y-%m-%d')

def backward_date_range(start_dt, end_dt, span_days):
#
# Generate tuples with intervals from given range of dates (backward)
#
# backward_date_range('2012-01-01', '2012-01-5', 2)
#
# 1st yield = ('2012-01-03', '2012-01-05')
# 2nd yield = ('2012-01-01', '2012-01-02')
start_dt = datetime.datetime.strptime(start_dt, '%Y-%m-%d')
end_dt = datetime.datetime.strptime(end_dt, '%Y-%m-%d')
span = datetime.timedelta(days=span_days)
step = datetime.timedelta(days=1)

while end_dt - span > start_dt:
current = end_dt - span
yield current.strftime('%Y-%m-%d'), end_dt.strftime('%Y-%m-%d')
end_dt = current - step
else:
yield start_dt.strftime('%Y-%m-%d'), end_dt.strftime('%Y-%m-%d')


This is currently what I have, needless to say it looks ugly. Is there a more elegant solution, especially if it is possible to get rid of the while loop altogether? or maybe compress it into one single line?



I am currently using python3.5










share|improve this question











$endgroup$












  • $begingroup$
    Hi, you might be able to get better reviews by providing a bit more context. Can you add a bit of explanation of why you need such feature and/or some code calling these functions?
    $endgroup$
    – Mathias Ettinger
    Apr 30 '16 at 8:42










  • $begingroup$
    @MathiasEttinger its a helper function that is used to supply start and end "range subsets" to SQL queries that wouldve clogged up a network. Something like "1996-2016" is split into smaller chunks for convenience
    $endgroup$
    – AlanSTACK
    Apr 30 '16 at 10:40










  • $begingroup$
    @Alan: Surely adding LIMIT to the SQL queries would have been a better solution to this problem?
    $endgroup$
    – Gareth Rees
    May 8 '16 at 17:04










  • $begingroup$
    @GarethRees The data structure of this case prevents this
    $endgroup$
    – AlanSTACK
    May 9 '16 at 2:19














4












4








4


1



$begingroup$


def forward_date_range(start_dt, end_dt, span_days):
#
# Generate tuples with intervals from given range of dates (forward)
#
# forward_date_range('2012-01-01', '2012-01-5', 2)
#
# 1st yield = ('2012-01-01', '2012-01-03')
# 2nd yield = ('2012-01-04', '2012-01-05')
start_dt = datetime.datetime.strptime(start_dt, '%Y-%m-%d')
end_dt = datetime.datetime.strptime(end_dt, '%Y-%m-%d')
span = datetime.timedelta(days=span_days)
step = datetime.timedelta(days=1)

while start_dt + span < end_dt:
current = start_dt + span
yield start_dt.strftime('%Y-%m-%d'), current.strftime('%Y-%m-%d')
start_dt = current + step
else:
yield start_dt.strftime('%Y-%m-%d'), end_dt.strftime('%Y-%m-%d')

def backward_date_range(start_dt, end_dt, span_days):
#
# Generate tuples with intervals from given range of dates (backward)
#
# backward_date_range('2012-01-01', '2012-01-5', 2)
#
# 1st yield = ('2012-01-03', '2012-01-05')
# 2nd yield = ('2012-01-01', '2012-01-02')
start_dt = datetime.datetime.strptime(start_dt, '%Y-%m-%d')
end_dt = datetime.datetime.strptime(end_dt, '%Y-%m-%d')
span = datetime.timedelta(days=span_days)
step = datetime.timedelta(days=1)

while end_dt - span > start_dt:
current = end_dt - span
yield current.strftime('%Y-%m-%d'), end_dt.strftime('%Y-%m-%d')
end_dt = current - step
else:
yield start_dt.strftime('%Y-%m-%d'), end_dt.strftime('%Y-%m-%d')


This is currently what I have, needless to say it looks ugly. Is there a more elegant solution, especially if it is possible to get rid of the while loop altogether? or maybe compress it into one single line?



I am currently using python3.5










share|improve this question











$endgroup$




def forward_date_range(start_dt, end_dt, span_days):
#
# Generate tuples with intervals from given range of dates (forward)
#
# forward_date_range('2012-01-01', '2012-01-5', 2)
#
# 1st yield = ('2012-01-01', '2012-01-03')
# 2nd yield = ('2012-01-04', '2012-01-05')
start_dt = datetime.datetime.strptime(start_dt, '%Y-%m-%d')
end_dt = datetime.datetime.strptime(end_dt, '%Y-%m-%d')
span = datetime.timedelta(days=span_days)
step = datetime.timedelta(days=1)

while start_dt + span < end_dt:
current = start_dt + span
yield start_dt.strftime('%Y-%m-%d'), current.strftime('%Y-%m-%d')
start_dt = current + step
else:
yield start_dt.strftime('%Y-%m-%d'), end_dt.strftime('%Y-%m-%d')

def backward_date_range(start_dt, end_dt, span_days):
#
# Generate tuples with intervals from given range of dates (backward)
#
# backward_date_range('2012-01-01', '2012-01-5', 2)
#
# 1st yield = ('2012-01-03', '2012-01-05')
# 2nd yield = ('2012-01-01', '2012-01-02')
start_dt = datetime.datetime.strptime(start_dt, '%Y-%m-%d')
end_dt = datetime.datetime.strptime(end_dt, '%Y-%m-%d')
span = datetime.timedelta(days=span_days)
step = datetime.timedelta(days=1)

while end_dt - span > start_dt:
current = end_dt - span
yield current.strftime('%Y-%m-%d'), end_dt.strftime('%Y-%m-%d')
end_dt = current - step
else:
yield start_dt.strftime('%Y-%m-%d'), end_dt.strftime('%Y-%m-%d')


This is currently what I have, needless to say it looks ugly. Is there a more elegant solution, especially if it is possible to get rid of the while loop altogether? or maybe compress it into one single line?



I am currently using python3.5







python python-3.x






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Apr 30 '16 at 8:43









Mathias Ettinger

24.4k33184




24.4k33184










asked Apr 30 '16 at 8:20









AlanSTACKAlanSTACK

16027




16027












  • $begingroup$
    Hi, you might be able to get better reviews by providing a bit more context. Can you add a bit of explanation of why you need such feature and/or some code calling these functions?
    $endgroup$
    – Mathias Ettinger
    Apr 30 '16 at 8:42










  • $begingroup$
    @MathiasEttinger its a helper function that is used to supply start and end "range subsets" to SQL queries that wouldve clogged up a network. Something like "1996-2016" is split into smaller chunks for convenience
    $endgroup$
    – AlanSTACK
    Apr 30 '16 at 10:40










  • $begingroup$
    @Alan: Surely adding LIMIT to the SQL queries would have been a better solution to this problem?
    $endgroup$
    – Gareth Rees
    May 8 '16 at 17:04










  • $begingroup$
    @GarethRees The data structure of this case prevents this
    $endgroup$
    – AlanSTACK
    May 9 '16 at 2:19


















  • $begingroup$
    Hi, you might be able to get better reviews by providing a bit more context. Can you add a bit of explanation of why you need such feature and/or some code calling these functions?
    $endgroup$
    – Mathias Ettinger
    Apr 30 '16 at 8:42










  • $begingroup$
    @MathiasEttinger its a helper function that is used to supply start and end "range subsets" to SQL queries that wouldve clogged up a network. Something like "1996-2016" is split into smaller chunks for convenience
    $endgroup$
    – AlanSTACK
    Apr 30 '16 at 10:40










  • $begingroup$
    @Alan: Surely adding LIMIT to the SQL queries would have been a better solution to this problem?
    $endgroup$
    – Gareth Rees
    May 8 '16 at 17:04










  • $begingroup$
    @GarethRees The data structure of this case prevents this
    $endgroup$
    – AlanSTACK
    May 9 '16 at 2:19
















$begingroup$
Hi, you might be able to get better reviews by providing a bit more context. Can you add a bit of explanation of why you need such feature and/or some code calling these functions?
$endgroup$
– Mathias Ettinger
Apr 30 '16 at 8:42




$begingroup$
Hi, you might be able to get better reviews by providing a bit more context. Can you add a bit of explanation of why you need such feature and/or some code calling these functions?
$endgroup$
– Mathias Ettinger
Apr 30 '16 at 8:42












$begingroup$
@MathiasEttinger its a helper function that is used to supply start and end "range subsets" to SQL queries that wouldve clogged up a network. Something like "1996-2016" is split into smaller chunks for convenience
$endgroup$
– AlanSTACK
Apr 30 '16 at 10:40




$begingroup$
@MathiasEttinger its a helper function that is used to supply start and end "range subsets" to SQL queries that wouldve clogged up a network. Something like "1996-2016" is split into smaller chunks for convenience
$endgroup$
– AlanSTACK
Apr 30 '16 at 10:40












$begingroup$
@Alan: Surely adding LIMIT to the SQL queries would have been a better solution to this problem?
$endgroup$
– Gareth Rees
May 8 '16 at 17:04




$begingroup$
@Alan: Surely adding LIMIT to the SQL queries would have been a better solution to this problem?
$endgroup$
– Gareth Rees
May 8 '16 at 17:04












$begingroup$
@GarethRees The data structure of this case prevents this
$endgroup$
– AlanSTACK
May 9 '16 at 2:19




$begingroup$
@GarethRees The data structure of this case prevents this
$endgroup$
– AlanSTACK
May 9 '16 at 2:19










2 Answers
2






active

oldest

votes


















4












$begingroup$

I'm going to assume that the corner cases are what you want, i.e. a date
"range" of a single day is acceptable - otherwise you'd have to overhaul
the calculation a bit.



I'm not going to attempt to remove the while loops - I don't know a
better alternative; six lines self-contained logic each isn't too bad
IMO.



First things first, I'd recommend a couple of things for better
readability and interactivity:




  • Use proper docstrings instead of comments, so that both tools and
    human readers know what the functions are about. At the moment
    help(forward_date_range) isn't too helpful, but with that change it
    would be much more so.

  • Similarly, the _dt suffix isn't that readable either. I'd either
    drop it or expand it to _date so that it's more obvious.


Next, I'd try to remove some more duplication:




  • The string '%Y-%m-%d' comes up a total of twelve times. Either put
    that into a constant (if you don't ever want users to supply a
    different format), or make it an optional function parameter.

  • The structure of both functions is also quite similar, so while the
    loop might not be worth to factor out, the pre- and postprocessing
    definitely is. Also, timedelta(days=1) is another constant in the
    code.

  • Importing the right names (e.g. datetime and timedelta) could also
    cut down the number of tokens to read.

  • There's also an opportunity to cache the result of end - span into a
    separate variable so as to not repeat it in every loop.

  • (From @mathias-ettinger) The loop doesn't have a break to exit it
    early, so the else branch will always be executed. In that case it
    makes sense to just put it on its own line without the else which
    doesn't change the meaning in any respect.


Lastly, I'd probably say that the conversion from and to datetime
objects doesn't belong into these functions and should be done
separately instead. If you want to keep it like it is there are still
some opportunities for helper functions to cut down the noise.



The result I'm posting below can still be compressed further, but at
that point it would be generally getting more functional and less like
regular Python code.



So there we have it:



from datetime import datetime, timedelta


DATE_FORMAT = '%Y-%m-%d'
DATE_STEP = timedelta(days=1)


def _strptime(string):
return datetime.strptime(string, DATE_FORMAT)


def _strftime(date):
return date.strftime(DATE_FORMAT)


def _date_range_parameters(start, end, span_days):
start = _strptime(start)
end = _strptime(end)
span = timedelta(days=span_days)
return start, end, span


def forward_date_range(start, end, span_days):
"""
Generate tuples with intervals from given range of dates (forward).

forward_date_range('2012-01-01', '2012-01-5', 2)

1st yield = ('2012-01-01', '2012-01-03')
2nd yield = ('2012-01-04', '2012-01-05')
"""
start, end, span = _date_range_parameters(start, end, span_days)
stop = end - span

while start < stop:
current = start + span
yield _strftime(start), _strftime(current)
start = current + DATE_STEP

yield _strftime(start), _strftime(end)


def backward_date_range(start, end, span_days):
"""
Generate tuples with intervals from given range of dates (backward)

backward_date_range('2012-01-01', '2012-01-5', 2)

1st yield = ('2012-01-03', '2012-01-05')
2nd yield = ('2012-01-01', '2012-01-02')
"""
start, end, span = _date_range_parameters(start, end, span_days)
stop = start + span

while end > stop:
current = end - span
yield _strftime(current), _strftime(end)
end = current - DATE_STEP

yield _strftime(start), _strftime(end)





share|improve this answer











$endgroup$





















    0












    $begingroup$

    You can use Arrow for this: https://arrow.readthedocs.io/en/latest/



    import copy
    import arrow

    end = arrow.utcnow()
    start = copy.deepcopy(end).shift(months=-2)

    hours_between = [r for r in arrow.Arrow.span_range('hour', start, end)]
    days_between = [r for r in arrow.Arrow.span_range('day', start, end)]


    print(days_between)

    [(<Arrow [2018-12-09T00:00:00+00:00]>,
    <Arrow [2018-12-09T23:59:59.999999+00:00]>),
    (<Arrow [2018-12-10T00:00:00+00:00]>,
    <Arrow [2018-12-10T23:59:59.999999+00:00]>),
    (<Arrow [2018-12-11T00:00:00+00:00]>,
    <Arrow [2018-12-11T23:59:59.999999+00:00]>),
    (<Arrow [2018-12-12T00:00:00+00:00]>,
    <Arrow [2018-12-12T23:59:59.999999+00:00]>),
    (<Arrow [2018-12-13T00:00:00+00:00]>,
    <Arrow [2018-12-13T23:59:59.999999+00:00]>),
    (<Arrow [2018-12-14T00:00:00+00:00]>,
    ...
    ]





    share|improve this answer








    New contributor




    user503582 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.






    $endgroup$













      Your Answer





      StackExchange.ifUsing("editor", function () {
      return StackExchange.using("mathjaxEditing", function () {
      StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
      StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
      });
      });
      }, "mathjax-editing");

      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "196"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: false,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: null,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f127124%2fiterate-over-chunks-of-date-range%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      2 Answers
      2






      active

      oldest

      votes








      2 Answers
      2






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      4












      $begingroup$

      I'm going to assume that the corner cases are what you want, i.e. a date
      "range" of a single day is acceptable - otherwise you'd have to overhaul
      the calculation a bit.



      I'm not going to attempt to remove the while loops - I don't know a
      better alternative; six lines self-contained logic each isn't too bad
      IMO.



      First things first, I'd recommend a couple of things for better
      readability and interactivity:




      • Use proper docstrings instead of comments, so that both tools and
        human readers know what the functions are about. At the moment
        help(forward_date_range) isn't too helpful, but with that change it
        would be much more so.

      • Similarly, the _dt suffix isn't that readable either. I'd either
        drop it or expand it to _date so that it's more obvious.


      Next, I'd try to remove some more duplication:




      • The string '%Y-%m-%d' comes up a total of twelve times. Either put
        that into a constant (if you don't ever want users to supply a
        different format), or make it an optional function parameter.

      • The structure of both functions is also quite similar, so while the
        loop might not be worth to factor out, the pre- and postprocessing
        definitely is. Also, timedelta(days=1) is another constant in the
        code.

      • Importing the right names (e.g. datetime and timedelta) could also
        cut down the number of tokens to read.

      • There's also an opportunity to cache the result of end - span into a
        separate variable so as to not repeat it in every loop.

      • (From @mathias-ettinger) The loop doesn't have a break to exit it
        early, so the else branch will always be executed. In that case it
        makes sense to just put it on its own line without the else which
        doesn't change the meaning in any respect.


      Lastly, I'd probably say that the conversion from and to datetime
      objects doesn't belong into these functions and should be done
      separately instead. If you want to keep it like it is there are still
      some opportunities for helper functions to cut down the noise.



      The result I'm posting below can still be compressed further, but at
      that point it would be generally getting more functional and less like
      regular Python code.



      So there we have it:



      from datetime import datetime, timedelta


      DATE_FORMAT = '%Y-%m-%d'
      DATE_STEP = timedelta(days=1)


      def _strptime(string):
      return datetime.strptime(string, DATE_FORMAT)


      def _strftime(date):
      return date.strftime(DATE_FORMAT)


      def _date_range_parameters(start, end, span_days):
      start = _strptime(start)
      end = _strptime(end)
      span = timedelta(days=span_days)
      return start, end, span


      def forward_date_range(start, end, span_days):
      """
      Generate tuples with intervals from given range of dates (forward).

      forward_date_range('2012-01-01', '2012-01-5', 2)

      1st yield = ('2012-01-01', '2012-01-03')
      2nd yield = ('2012-01-04', '2012-01-05')
      """
      start, end, span = _date_range_parameters(start, end, span_days)
      stop = end - span

      while start < stop:
      current = start + span
      yield _strftime(start), _strftime(current)
      start = current + DATE_STEP

      yield _strftime(start), _strftime(end)


      def backward_date_range(start, end, span_days):
      """
      Generate tuples with intervals from given range of dates (backward)

      backward_date_range('2012-01-01', '2012-01-5', 2)

      1st yield = ('2012-01-03', '2012-01-05')
      2nd yield = ('2012-01-01', '2012-01-02')
      """
      start, end, span = _date_range_parameters(start, end, span_days)
      stop = start + span

      while end > stop:
      current = end - span
      yield _strftime(current), _strftime(end)
      end = current - DATE_STEP

      yield _strftime(start), _strftime(end)





      share|improve this answer











      $endgroup$


















        4












        $begingroup$

        I'm going to assume that the corner cases are what you want, i.e. a date
        "range" of a single day is acceptable - otherwise you'd have to overhaul
        the calculation a bit.



        I'm not going to attempt to remove the while loops - I don't know a
        better alternative; six lines self-contained logic each isn't too bad
        IMO.



        First things first, I'd recommend a couple of things for better
        readability and interactivity:




        • Use proper docstrings instead of comments, so that both tools and
          human readers know what the functions are about. At the moment
          help(forward_date_range) isn't too helpful, but with that change it
          would be much more so.

        • Similarly, the _dt suffix isn't that readable either. I'd either
          drop it or expand it to _date so that it's more obvious.


        Next, I'd try to remove some more duplication:




        • The string '%Y-%m-%d' comes up a total of twelve times. Either put
          that into a constant (if you don't ever want users to supply a
          different format), or make it an optional function parameter.

        • The structure of both functions is also quite similar, so while the
          loop might not be worth to factor out, the pre- and postprocessing
          definitely is. Also, timedelta(days=1) is another constant in the
          code.

        • Importing the right names (e.g. datetime and timedelta) could also
          cut down the number of tokens to read.

        • There's also an opportunity to cache the result of end - span into a
          separate variable so as to not repeat it in every loop.

        • (From @mathias-ettinger) The loop doesn't have a break to exit it
          early, so the else branch will always be executed. In that case it
          makes sense to just put it on its own line without the else which
          doesn't change the meaning in any respect.


        Lastly, I'd probably say that the conversion from and to datetime
        objects doesn't belong into these functions and should be done
        separately instead. If you want to keep it like it is there are still
        some opportunities for helper functions to cut down the noise.



        The result I'm posting below can still be compressed further, but at
        that point it would be generally getting more functional and less like
        regular Python code.



        So there we have it:



        from datetime import datetime, timedelta


        DATE_FORMAT = '%Y-%m-%d'
        DATE_STEP = timedelta(days=1)


        def _strptime(string):
        return datetime.strptime(string, DATE_FORMAT)


        def _strftime(date):
        return date.strftime(DATE_FORMAT)


        def _date_range_parameters(start, end, span_days):
        start = _strptime(start)
        end = _strptime(end)
        span = timedelta(days=span_days)
        return start, end, span


        def forward_date_range(start, end, span_days):
        """
        Generate tuples with intervals from given range of dates (forward).

        forward_date_range('2012-01-01', '2012-01-5', 2)

        1st yield = ('2012-01-01', '2012-01-03')
        2nd yield = ('2012-01-04', '2012-01-05')
        """
        start, end, span = _date_range_parameters(start, end, span_days)
        stop = end - span

        while start < stop:
        current = start + span
        yield _strftime(start), _strftime(current)
        start = current + DATE_STEP

        yield _strftime(start), _strftime(end)


        def backward_date_range(start, end, span_days):
        """
        Generate tuples with intervals from given range of dates (backward)

        backward_date_range('2012-01-01', '2012-01-5', 2)

        1st yield = ('2012-01-03', '2012-01-05')
        2nd yield = ('2012-01-01', '2012-01-02')
        """
        start, end, span = _date_range_parameters(start, end, span_days)
        stop = start + span

        while end > stop:
        current = end - span
        yield _strftime(current), _strftime(end)
        end = current - DATE_STEP

        yield _strftime(start), _strftime(end)





        share|improve this answer











        $endgroup$
















          4












          4








          4





          $begingroup$

          I'm going to assume that the corner cases are what you want, i.e. a date
          "range" of a single day is acceptable - otherwise you'd have to overhaul
          the calculation a bit.



          I'm not going to attempt to remove the while loops - I don't know a
          better alternative; six lines self-contained logic each isn't too bad
          IMO.



          First things first, I'd recommend a couple of things for better
          readability and interactivity:




          • Use proper docstrings instead of comments, so that both tools and
            human readers know what the functions are about. At the moment
            help(forward_date_range) isn't too helpful, but with that change it
            would be much more so.

          • Similarly, the _dt suffix isn't that readable either. I'd either
            drop it or expand it to _date so that it's more obvious.


          Next, I'd try to remove some more duplication:




          • The string '%Y-%m-%d' comes up a total of twelve times. Either put
            that into a constant (if you don't ever want users to supply a
            different format), or make it an optional function parameter.

          • The structure of both functions is also quite similar, so while the
            loop might not be worth to factor out, the pre- and postprocessing
            definitely is. Also, timedelta(days=1) is another constant in the
            code.

          • Importing the right names (e.g. datetime and timedelta) could also
            cut down the number of tokens to read.

          • There's also an opportunity to cache the result of end - span into a
            separate variable so as to not repeat it in every loop.

          • (From @mathias-ettinger) The loop doesn't have a break to exit it
            early, so the else branch will always be executed. In that case it
            makes sense to just put it on its own line without the else which
            doesn't change the meaning in any respect.


          Lastly, I'd probably say that the conversion from and to datetime
          objects doesn't belong into these functions and should be done
          separately instead. If you want to keep it like it is there are still
          some opportunities for helper functions to cut down the noise.



          The result I'm posting below can still be compressed further, but at
          that point it would be generally getting more functional and less like
          regular Python code.



          So there we have it:



          from datetime import datetime, timedelta


          DATE_FORMAT = '%Y-%m-%d'
          DATE_STEP = timedelta(days=1)


          def _strptime(string):
          return datetime.strptime(string, DATE_FORMAT)


          def _strftime(date):
          return date.strftime(DATE_FORMAT)


          def _date_range_parameters(start, end, span_days):
          start = _strptime(start)
          end = _strptime(end)
          span = timedelta(days=span_days)
          return start, end, span


          def forward_date_range(start, end, span_days):
          """
          Generate tuples with intervals from given range of dates (forward).

          forward_date_range('2012-01-01', '2012-01-5', 2)

          1st yield = ('2012-01-01', '2012-01-03')
          2nd yield = ('2012-01-04', '2012-01-05')
          """
          start, end, span = _date_range_parameters(start, end, span_days)
          stop = end - span

          while start < stop:
          current = start + span
          yield _strftime(start), _strftime(current)
          start = current + DATE_STEP

          yield _strftime(start), _strftime(end)


          def backward_date_range(start, end, span_days):
          """
          Generate tuples with intervals from given range of dates (backward)

          backward_date_range('2012-01-01', '2012-01-5', 2)

          1st yield = ('2012-01-03', '2012-01-05')
          2nd yield = ('2012-01-01', '2012-01-02')
          """
          start, end, span = _date_range_parameters(start, end, span_days)
          stop = start + span

          while end > stop:
          current = end - span
          yield _strftime(current), _strftime(end)
          end = current - DATE_STEP

          yield _strftime(start), _strftime(end)





          share|improve this answer











          $endgroup$



          I'm going to assume that the corner cases are what you want, i.e. a date
          "range" of a single day is acceptable - otherwise you'd have to overhaul
          the calculation a bit.



          I'm not going to attempt to remove the while loops - I don't know a
          better alternative; six lines self-contained logic each isn't too bad
          IMO.



          First things first, I'd recommend a couple of things for better
          readability and interactivity:




          • Use proper docstrings instead of comments, so that both tools and
            human readers know what the functions are about. At the moment
            help(forward_date_range) isn't too helpful, but with that change it
            would be much more so.

          • Similarly, the _dt suffix isn't that readable either. I'd either
            drop it or expand it to _date so that it's more obvious.


          Next, I'd try to remove some more duplication:




          • The string '%Y-%m-%d' comes up a total of twelve times. Either put
            that into a constant (if you don't ever want users to supply a
            different format), or make it an optional function parameter.

          • The structure of both functions is also quite similar, so while the
            loop might not be worth to factor out, the pre- and postprocessing
            definitely is. Also, timedelta(days=1) is another constant in the
            code.

          • Importing the right names (e.g. datetime and timedelta) could also
            cut down the number of tokens to read.

          • There's also an opportunity to cache the result of end - span into a
            separate variable so as to not repeat it in every loop.

          • (From @mathias-ettinger) The loop doesn't have a break to exit it
            early, so the else branch will always be executed. In that case it
            makes sense to just put it on its own line without the else which
            doesn't change the meaning in any respect.


          Lastly, I'd probably say that the conversion from and to datetime
          objects doesn't belong into these functions and should be done
          separately instead. If you want to keep it like it is there are still
          some opportunities for helper functions to cut down the noise.



          The result I'm posting below can still be compressed further, but at
          that point it would be generally getting more functional and less like
          regular Python code.



          So there we have it:



          from datetime import datetime, timedelta


          DATE_FORMAT = '%Y-%m-%d'
          DATE_STEP = timedelta(days=1)


          def _strptime(string):
          return datetime.strptime(string, DATE_FORMAT)


          def _strftime(date):
          return date.strftime(DATE_FORMAT)


          def _date_range_parameters(start, end, span_days):
          start = _strptime(start)
          end = _strptime(end)
          span = timedelta(days=span_days)
          return start, end, span


          def forward_date_range(start, end, span_days):
          """
          Generate tuples with intervals from given range of dates (forward).

          forward_date_range('2012-01-01', '2012-01-5', 2)

          1st yield = ('2012-01-01', '2012-01-03')
          2nd yield = ('2012-01-04', '2012-01-05')
          """
          start, end, span = _date_range_parameters(start, end, span_days)
          stop = end - span

          while start < stop:
          current = start + span
          yield _strftime(start), _strftime(current)
          start = current + DATE_STEP

          yield _strftime(start), _strftime(end)


          def backward_date_range(start, end, span_days):
          """
          Generate tuples with intervals from given range of dates (backward)

          backward_date_range('2012-01-01', '2012-01-5', 2)

          1st yield = ('2012-01-03', '2012-01-05')
          2nd yield = ('2012-01-01', '2012-01-02')
          """
          start, end, span = _date_range_parameters(start, end, span_days)
          stop = start + span

          while end > stop:
          current = end - span
          yield _strftime(current), _strftime(end)
          end = current - DATE_STEP

          yield _strftime(start), _strftime(end)






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Apr 30 '16 at 11:16

























          answered Apr 30 '16 at 11:01









          feradaferada

          9,1461554




          9,1461554

























              0












              $begingroup$

              You can use Arrow for this: https://arrow.readthedocs.io/en/latest/



              import copy
              import arrow

              end = arrow.utcnow()
              start = copy.deepcopy(end).shift(months=-2)

              hours_between = [r for r in arrow.Arrow.span_range('hour', start, end)]
              days_between = [r for r in arrow.Arrow.span_range('day', start, end)]


              print(days_between)

              [(<Arrow [2018-12-09T00:00:00+00:00]>,
              <Arrow [2018-12-09T23:59:59.999999+00:00]>),
              (<Arrow [2018-12-10T00:00:00+00:00]>,
              <Arrow [2018-12-10T23:59:59.999999+00:00]>),
              (<Arrow [2018-12-11T00:00:00+00:00]>,
              <Arrow [2018-12-11T23:59:59.999999+00:00]>),
              (<Arrow [2018-12-12T00:00:00+00:00]>,
              <Arrow [2018-12-12T23:59:59.999999+00:00]>),
              (<Arrow [2018-12-13T00:00:00+00:00]>,
              <Arrow [2018-12-13T23:59:59.999999+00:00]>),
              (<Arrow [2018-12-14T00:00:00+00:00]>,
              ...
              ]





              share|improve this answer








              New contributor




              user503582 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
              Check out our Code of Conduct.






              $endgroup$


















                0












                $begingroup$

                You can use Arrow for this: https://arrow.readthedocs.io/en/latest/



                import copy
                import arrow

                end = arrow.utcnow()
                start = copy.deepcopy(end).shift(months=-2)

                hours_between = [r for r in arrow.Arrow.span_range('hour', start, end)]
                days_between = [r for r in arrow.Arrow.span_range('day', start, end)]


                print(days_between)

                [(<Arrow [2018-12-09T00:00:00+00:00]>,
                <Arrow [2018-12-09T23:59:59.999999+00:00]>),
                (<Arrow [2018-12-10T00:00:00+00:00]>,
                <Arrow [2018-12-10T23:59:59.999999+00:00]>),
                (<Arrow [2018-12-11T00:00:00+00:00]>,
                <Arrow [2018-12-11T23:59:59.999999+00:00]>),
                (<Arrow [2018-12-12T00:00:00+00:00]>,
                <Arrow [2018-12-12T23:59:59.999999+00:00]>),
                (<Arrow [2018-12-13T00:00:00+00:00]>,
                <Arrow [2018-12-13T23:59:59.999999+00:00]>),
                (<Arrow [2018-12-14T00:00:00+00:00]>,
                ...
                ]





                share|improve this answer








                New contributor




                user503582 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                Check out our Code of Conduct.






                $endgroup$
















                  0












                  0








                  0





                  $begingroup$

                  You can use Arrow for this: https://arrow.readthedocs.io/en/latest/



                  import copy
                  import arrow

                  end = arrow.utcnow()
                  start = copy.deepcopy(end).shift(months=-2)

                  hours_between = [r for r in arrow.Arrow.span_range('hour', start, end)]
                  days_between = [r for r in arrow.Arrow.span_range('day', start, end)]


                  print(days_between)

                  [(<Arrow [2018-12-09T00:00:00+00:00]>,
                  <Arrow [2018-12-09T23:59:59.999999+00:00]>),
                  (<Arrow [2018-12-10T00:00:00+00:00]>,
                  <Arrow [2018-12-10T23:59:59.999999+00:00]>),
                  (<Arrow [2018-12-11T00:00:00+00:00]>,
                  <Arrow [2018-12-11T23:59:59.999999+00:00]>),
                  (<Arrow [2018-12-12T00:00:00+00:00]>,
                  <Arrow [2018-12-12T23:59:59.999999+00:00]>),
                  (<Arrow [2018-12-13T00:00:00+00:00]>,
                  <Arrow [2018-12-13T23:59:59.999999+00:00]>),
                  (<Arrow [2018-12-14T00:00:00+00:00]>,
                  ...
                  ]





                  share|improve this answer








                  New contributor




                  user503582 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                  Check out our Code of Conduct.






                  $endgroup$



                  You can use Arrow for this: https://arrow.readthedocs.io/en/latest/



                  import copy
                  import arrow

                  end = arrow.utcnow()
                  start = copy.deepcopy(end).shift(months=-2)

                  hours_between = [r for r in arrow.Arrow.span_range('hour', start, end)]
                  days_between = [r for r in arrow.Arrow.span_range('day', start, end)]


                  print(days_between)

                  [(<Arrow [2018-12-09T00:00:00+00:00]>,
                  <Arrow [2018-12-09T23:59:59.999999+00:00]>),
                  (<Arrow [2018-12-10T00:00:00+00:00]>,
                  <Arrow [2018-12-10T23:59:59.999999+00:00]>),
                  (<Arrow [2018-12-11T00:00:00+00:00]>,
                  <Arrow [2018-12-11T23:59:59.999999+00:00]>),
                  (<Arrow [2018-12-12T00:00:00+00:00]>,
                  <Arrow [2018-12-12T23:59:59.999999+00:00]>),
                  (<Arrow [2018-12-13T00:00:00+00:00]>,
                  <Arrow [2018-12-13T23:59:59.999999+00:00]>),
                  (<Arrow [2018-12-14T00:00:00+00:00]>,
                  ...
                  ]






                  share|improve this answer








                  New contributor




                  user503582 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                  Check out our Code of Conduct.









                  share|improve this answer



                  share|improve this answer






                  New contributor




                  user503582 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                  Check out our Code of Conduct.









                  answered 15 mins ago









                  user503582user503582

                  1




                  1




                  New contributor




                  user503582 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                  Check out our Code of Conduct.





                  New contributor





                  user503582 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                  Check out our Code of Conduct.






                  user503582 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
                  Check out our Code of Conduct.






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Code Review Stack Exchange!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      Use MathJax to format equations. MathJax reference.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f127124%2fiterate-over-chunks-of-date-range%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Список кардиналов, возведённых папой римским Каликстом III

                      Deduzione

                      Mysql.sock missing - “Can't connect to local MySQL server through socket”