我有以下日期范围:
begin: 2018-02-15
end: 2018-04-23
我想实现以下目标:
["2018-02-15 - 2018-02-28", "2018-03-01 - 2018-03-31", "2018-04-01 - 2018-04-23"]
基本上,我想将给定的日期范围划分为几个月。我想不出一种在Python中完成此操作的方法。
我已经考虑了解决方法here,但是,这会根据指定的时间间隔拆分日期范围。我希望能够动态分割日期范围。
因此,鉴于日期范围为2018年2月15日至2018年4月23日,我希望能够获得该范围内的各个月份,例如:
答案 0 :(得分:5)
循环中;从第一天开始,持续增加一天直到您到达结束日期;每当月份变化时,都会保存日期。
import datetime
begin = '2018-02-15'
end = '2018-04-23'
dt_start = datetime.datetime.strptime(begin, '%Y-%m-%d')
dt_end = datetime.datetime.strptime(end, '%Y-%m-%d')
one_day = datetime.timedelta(1)
start_dates = [dt_start]
end_dates = []
today = dt_start
while today <= dt_end:
#print(today)
tomorrow = today + one_day
if tomorrow.month != today.month:
start_dates.append(tomorrow)
end_dates.append(today)
today = tomorrow
end_dates.append(dt_end)
out_fmt = '%d %B %Y'
for start, end in zip(start_dates,end_dates):
print('{} to {}'.format(start.strftime(out_fmt), end.strftime(out_fmt)))
结果:
>>>
15 February 2018 to 28 February 2018
01 March 2018 to 31 March 2018
01 April 2018 to 23 April 2018
>>>
您可能想出了一种在开始日期和结束日期之间获得一个月范围的方法;为存储每个月的第一天和之前的日期创建一个datetime对象。但是,跨年的日期可能会出现问题。
答案 1 :(得分:2)
要使用方便的日期对象,请始终使用标准模块datetime
。这样可以包装字符串格式的日期,并允许更轻松的计算以及量身定制的输出格式。
不幸的是,它似乎丢失了一条重要的信息:给定年份的每个月的最后天(这是Februari所必需的)。还有一个附加模块calendar
返回the last day for a month,但是由于这是您所需要的,并且有一个简单的基于datetime
的函数可以完成相同的操作,因此我选择了后者。
这样,您可以设置任何begin
日期并将其与该月的最后一天一起添加到列表中,然后将begin
设置为下一个月的日期1,然后继续直到您通过end
。
一个警告/微调:我意识到如果begin
和end
都在同一月份之内是行不通的。这需要进行临时检查,因此我将最初的while begin < end
更改为while True
,并将用于结束日期的检查移到了单独的行中。
此外,要跨越一个年,还需要另外进行一次测试,因为否则语句month+1
会在12月失败。
import datetime
# borrowed from https://stackoverflow.com/a/13565185
# as noted there, the calendar module has a function of its own
def last_day_of_month(any_day):
next_month = any_day.replace(day=28) + datetime.timedelta(days=4) # this will never fail
return next_month - datetime.timedelta(days=next_month.day)
begin = "2018-02-15"
end = "2018-04-23"
def monthlist(begin,end):
begin = datetime.datetime.strptime(begin, "%Y-%m-%d")
end = datetime.datetime.strptime(end, "%Y-%m-%d")
result = []
while True:
if begin.month == 12:
next_month = begin.replace(year=begin.year+1,month=1, day=1)
else:
next_month = begin.replace(month=begin.month+1, day=1)
if next_month > end:
break
result.append ([begin.strftime("%Y-%m-%d"),last_day_of_month(begin).strftime("%Y-%m-%d")])
begin = next_month
result.append ([begin.strftime("%Y-%m-%d"),end.strftime("%Y-%m-%d")])
return result
date_list = monthlist(begin,end)
print (date_list)
产生
[ ['2018-02-15', '2018-02-28'],
['2018-03-01', '2018-03-31'],
['2018-04-01', '2018-04-23'] ]
(仅出于可读性而格式化)
答案 2 :(得分:1)
如果您不介意使用pandas
,那么有个不错的助手date_range
可以实现您想要的目标:
import pandas as pd
start = pd.Timestamp('20180215')
end = pd.Timestamp('20180423')
parts = list(pd.date_range(start, end, freq='M'))
# parts = [Timestamp('2018-02-28 00:00:00', freq='M'), Timestamp('2018-03-31 00:00:00', freq='M')]
if start != parts[0]:
parts.insert(0, start)
if end != parts[-1]:
parts.append(end)
parts[0] -= pd.Timedelta('1d') # we add back one day later
pairs = zip(map(lambda d: d + pd.Timedelta('1d'), parts[:-1]), parts[1:])
pairs_str = list(map(lambda t: t[0].strftime('%Y-%m-%d') + ' - ' + t[1].strftime('%Y-%m-%d'), pairs))
# pairs_str = ['2018-02-15 - 2018-02-28', '2018-03-01 - 2018-03-31', '2018-04-01 - 2018-04-23']
答案 3 :(得分:0)
使用python日历并计算年份变化
import calendar
from datetime import datetime
begin = '2018-02-15'
end= '2018-04-23'
begin_year, begin_month, begin_date = [int(i) for i in begin.split("-")]
end_year, end_month, end_date = [int(i) for i in end.split("-")]
years = end_year - begin_year
# if date range contains more than single year, we calculate total months
if years:
months = (12 - begin_month) + end_month + (12 * (years - 1))
else:
months = end_month - begin_month
dates = []
month = begin_month
year = begin_year
def create_datetime_object(y, m, d):
return datetime.strptime('{}-{}-{}'.format(y, m, d), '%Y-%m-%d')
# append the first date
dates.append(create_datetime_object(begin_year, begin_month, begin_date))
for i in range(months+1):
days_in_month = calendar.monthrange(year, month)[-1]
if month == begin_month and year == begin_year:
dates.append(create_datetime_object(begin_year, begin_month, days_in_month))
elif month == end_month and year == end_year:
dates.append(create_datetime_object(end_year, end_month, 1))
else:
dates.append(create_datetime_object(year, month, 1))
dates.append(create_datetime_object(year, month, days_in_month))
if month == 12:
month = 0
year += 1
month += 1
# append the last date
dates.append(create_datetime_object(end_year, end_month, end_date))
要获得问题清单,我们可以做类似的事情-
dates = [datetime.strftime(dt, '%Y-%m-%d') for dt in dates]
答案 4 :(得分:0)
对于使用https://stackoverflow.com/a/27205844/3390178的人:
start = pendulum.now().subtract(months=NUMBER_OF_MONTHS_BACK)
end = pendulum.today()
period = pendulum.period(start, end)
for dt in period.range("months"):
print(dt)
答案 5 :(得分:0)
我不得不做一个类似的操作并最终构建了这个函数。我在不同的用例(不同年份、同月...)上对其进行了测试,并且运行良好。
<块引用>它的灵感来自 S.Lott 的回答here Creating a range of dates in Python
import datetime
def get_segments(start_date, end_date):
"""
Divides input date range into associated months periods
Example:
Input: start_date = 2018-02-15
end_date = 2018-04-23
Output:
["2018-02-15 - 2018-02-28",
"2018-03-01 - 2018-03-31",
"2018-04-01 - 2018-04-23"]
"""
curr_date = start_date
curr_month = start_date.strftime("%m")
segments = []
loop = (curr_date!=end_date)
days_increment = 1
while loop:
# Get incremented date with 1 day
curr_date = start_date + datetime.timedelta(days=days_increment)
# Get associated month
prev_month = curr_month
curr_month = curr_date.strftime("%m")
# Add to segments if new month
if prev_month!=curr_month:
# get start of segment
if not segments:
start_segment = start_date
else:
start_segment = segments[-1][1] + datetime.timedelta(days=1)
# get end of segment
end_segment = curr_date - datetime.timedelta(days=1)
# define and add segment
segment = [start_segment, end_segment]
segments.append(segment)
# stop if last day reached
loop = (curr_date!=end_date)
# increment added days
days_increment += 1
if not segments or segments[-1][1]!=end_date:
if not segments:
start_last_segment = start_date
else:
start_last_segment = segments[-1][1] + datetime.timedelta(days=1)
last_segment = [start_last_segment, end_date]
segments.append(last_segment)
for i in range(len(segments)):
segments[i][0] = segments[i][0].strftime("%Y-%m-%d")
segments[i][1] = segments[i][1].strftime("%Y-%m-%d")
return segments
这是一个例子:
start_date = datetime.datetime(2020, 5, 27)
end_date = datetime.datetime(2021, 3, 1)
segments = get_segments(start_date, end_date)
for seg in segments:
print(seg)
输出:
['2020-05-27', '2020-05-31']
['2020-06-01', '2020-06-30']
['2020-07-01', '2020-07-31']
['2020-08-01', '2020-08-31']
['2020-09-01', '2020-09-30']
['2020-10-01', '2020-10-31']
['2020-11-01', '2020-11-30']
['2020-12-01', '2020-12-31']
['2021-01-01', '2021-01-31']
['2021-02-01', '2021-02-28']
['2021-03-01', '2021-03-01']
答案 6 :(得分:0)
我通过@wwii 扩展了解决方案
现在您不会有重复的开始和/或结束日期
def date_range_split_monthly(begin, end):
dt_start = datetime.strptime(begin, '%Y-%m-%d')
dt_end = datetime.strptime(end, '%Y-%m-%d')
one_day = timedelta(1)
start_dates = [dt_start]
end_dates = []
today = dt_start
while today <= dt_end:
# print(today)
tomorrow = today + one_day
if tomorrow.month != today.month:
if tomorrow <= dt_end:
start_dates.append(tomorrow)
end_dates.append(today)
today = tomorrow
end_dates.append(dt_end)
return start_dates, end_dates