Iterate over chunks of date range
$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
python python-3.x
$endgroup$
add a comment |
$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
python python-3.x
$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 addingLIMIT
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
add a comment |
$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
python python-3.x
$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
python python-3.x
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 addingLIMIT
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
add a comment |
$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 addingLIMIT
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
add a comment |
2 Answers
2
active
oldest
votes
$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
andtimedelta
) 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 theelse
branch will always be executed. In that case it
makes sense to just put it on its own line without theelse
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)
$endgroup$
add a comment |
$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]>,
...
]
New contributor
$endgroup$
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
$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
andtimedelta
) 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 theelse
branch will always be executed. In that case it
makes sense to just put it on its own line without theelse
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)
$endgroup$
add a comment |
$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
andtimedelta
) 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 theelse
branch will always be executed. In that case it
makes sense to just put it on its own line without theelse
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)
$endgroup$
add a comment |
$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
andtimedelta
) 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 theelse
branch will always be executed. In that case it
makes sense to just put it on its own line without theelse
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)
$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
andtimedelta
) 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 theelse
branch will always be executed. In that case it
makes sense to just put it on its own line without theelse
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)
edited Apr 30 '16 at 11:16
answered Apr 30 '16 at 11:01
feradaferada
9,1461554
9,1461554
add a comment |
add a comment |
$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]>,
...
]
New contributor
$endgroup$
add a comment |
$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]>,
...
]
New contributor
$endgroup$
add a comment |
$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]>,
...
]
New contributor
$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]>,
...
]
New contributor
New contributor
answered 15 mins ago
user503582user503582
1
1
New contributor
New contributor
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
$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