如何在Python中将日期范围划分为几个月?

时间:2018-07-11 20:20:27

标签: python python-3.x datetime

我有以下日期范围:

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日,我希望能够获得该范围内的各个月份,例如:

  • 2018年2月15日至2018年2月28日
  • 2018年3月1日至2018年3月31日
  • 2018年4月1日至2018年4月23日

7 个答案:

答案 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

一个警告/微调:我意识到如果beginend都在同一月份之内是行不通的。这需要进行临时检查,因此我将最初的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)

Pendulum

答案 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