计算两个日期之间每个月的天数

2024-05-19 07:58:20 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在寻找一个函数,采取2个日期(入院和出院)和一个财政年度,并返回每个月这些日期之间的天数。在

财政年度为4月1日至3月31日

我目前有一个解决方案(如下),这是一个混乱的SPSS和Python,最终它将需要实现回SPSS,但作为一个更整洁的Python函数,不幸的是,这意味着它只能使用标准库(而不是Pandas)。在

例如

+-----------------+-----------------+------+--+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
|    Admission    |    Discharge    |  FY  |  | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec | Jan | Feb | Mar |
+-----------------+-----------------+------+--+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
| 01 January 2017 | 05 January 2017 | 1617 |  |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   4 |   0 |   0 |
| 01 January 2017 | 05 June 2017    | 1617 |  |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   0 |  31 |  28 |  31 |
| 01 January 2017 | 05 June 2017    | 1718 |  |  30 |  31 |   4 |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   0 |   0 |
| 01 January 2017 | 01 January 2019 | 1718 |  |  30 |  31 |  30 |  31 |  31 |  30 |  31 |  30 |  31 |  31 |  28 |  31 |
+-----------------+-----------------+------+--+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+

相关-How to calculate number of days between two given dates?

当前解决方案(SPSS代码)

^{pr2}$

Tags: 函数pandas标准解决方案aprspss天数june
3条回答

我想很多人的答案都是在OP给出fy如何发挥作用的关键信息之前(edit:很多人都读过这个编辑,现在他们的答案也更新了)。OP需要admission到{}之间的天数,该天数在财政年度内(1819年为2018年4月1日至2019年3月31日)。显然,每个人都知道,天数需要按日历月份划分。在

from datetime import datetime, timedelta

# Function taken from https://stackoverflow.com/a/13565185/9462009
def lastDateOfThisMonth(any_day):
    next_month = any_day.replace(day=28) + timedelta(days=4)
    return next_month - timedelta(days=next_month.day)

def monthlyBeddays(admission, discharge, fy):
    startFy = datetime.strptime('01-Apr-'+fy[:2], '%d-%b-%y')
    endFy = datetime.strptime('01-Apr-'+fy[2:], '%d-%b-%y')

    admissionDate = datetime.strptime(admission, '%d-%b-%Y')
    dischargeDate = datetime.strptime(discharge, '%d-%b-%Y')


    monthDates = {'Jan':0,'Feb':0,'Mar':0,'Apr':0,'May':0,'Jun':0,'Jul':0,'Aug':0,'Sep':0,'Oct':0,'Nov':0,'Dec':0}

    # if admitted after end of fy or discharged before beginning of fy, zero days counted
    if admissionDate > endFy or dischargeDate < startFy:
        return monthDates

    if admissionDate < startFy:
        # Jump ahead to start at the first day of fy if admission was prior to the beginning of fy
        now = startFy
    else:
        # If admission happened at or after the first day of fy, we begin counting from the admission date
        now = admissionDate

    while True:
        month = datetime.strftime(now,'%b')
        lastDateOfMonth = lastDateOfThisMonth(now)
        if now >= endFy:
            # If now is greater or equal to the first day of the next fy (endFy), we don't care about any of the following dates within the adm/dis date range
            break
        if month == datetime.strftime(dischargeDate,'%b') and datetime.strftime(now, '%Y') == datetime.strftime(dischargeDate, '%Y') and now >= startFy:
            # If we reach the discharge month, we count this month and we're done
            monthDates[month] = (dischargeDate - now).days # not adding one since in your example it seemed like you did not want to count the dischargeDate (Mar:4)
            break
        elif now < startFy:
            # If now is less than the first day of this fy (startFy), we move on from this month to the next month until we reach this fy
            pass
        else:
            # We are within this fy and have not reached the discharge month yet
            monthDates[month] = (lastDateOfMonth - now).days + 1
            month = datetime.strftime(now, '%b')
        now = lastDateOfMonth + timedelta(days=1) # advance to the 1st of the next month

    return monthDates

# Passes all six scenarios

# Scenario #1: admitted before fy, discharged before  fy (didn't stay at all during fy)
print(monthlyBeddays("01-Jan-2018", "30-Mar-2018", '1819')) # {'Jan': 0, 'Feb': 0, 'Mar': 0, 'Apr': 0, 'May': 0, 'Jun': 0, 'Jul': 0, 'Aug': 0, 'Sep': 0, 'Oct': 0, 'Nov': 0, 'Dec': 0}

# Scenario #2: admitted before fy, discharged during fy
print(monthlyBeddays("01-Jan-2018", "30-May-2018", '1819')) # {'Jan': 0, 'Feb': 0, 'Mar': 0, 'Apr': 30, 'May': 29, 'Jun': 0, 'Jul': 0, 'Aug': 0, 'Sep': 0, 'Oct': 0, 'Nov': 0, 'Dec': 0}

# Scenario #3: admitted during fy, discharged during fy
print(monthlyBeddays("15-Apr-2018", "30-May-2018", '1819')) # {'Jan': 0, 'Feb': 0, 'Mar': 0, 'Apr': 16, 'May': 29, 'Jun': 0, 'Jul': 0, 'Aug': 0, 'Sep': 0, 'Oct': 0, 'Nov': 0, 'Dec': 0}

# Scenario #4: admitted during fy, discharged after fy
print(monthlyBeddays("15-Apr-2018", "30-May-2019", '1819')) # {'Jan': 31, 'Feb': 28, 'Mar': 31, 'Apr': 16, 'May': 31, 'Jun': 30, 'Jul': 31, 'Aug': 31, 'Sep': 30, 'Oct': 31, 'Nov': 30, 'Dec': 31}

# Scenario #5: admitted before fy, discharged after fy (stayed the whole fy)
print(monthlyBeddays("15-Mar-2018", "30-May-2019", '1819')) # {'Jan': 31, 'Feb': 28, 'Mar': 31, 'Apr': 30, 'May': 31, 'Jun': 30, 'Jul': 31, 'Aug': 31, 'Sep': 30, 'Oct': 31, 'Nov': 30, 'Dec': 31}

# Scenario #6: admitted after fy, discharged after fy (didn't stay at all during fy)
print(monthlyBeddays("15-Mar-2018", "30-May-2019", '1718')) # {'Jan': 0, 'Feb': 0, 'Mar': 17, 'Apr': 0, 'May': 0, 'Jun': 0, 'Jul': 0, 'Aug': 0, 'Sep': 0, 'Oct': 0, 'Nov': 0, 'Dec': 0}

我能想到的唯一方法是循环每天并分析它所属的月份:

import time, collections
SECONDS_PER_DAY = 24 * 60 * 60
def monthlyBedDays(admission, discharge, fy=None):

    start = time.mktime(time.strptime(admission, '%d-%b-%Y'))
    end = time.mktime(time.strptime( discharge, '%d-%b-%Y'))
    if fy is not None:
        fy = str(fy)
        start = max(start, time.mktime(time.strptime('01-Apr-'+fy[:2], '%d-%b-%y')))
        end   = min(end,   time.mktime(time.strptime('31-Mar-'+fy[2:], '%d-%b-%y')))
    days = collections.defaultdict(int)
    for day in range(int(start), int(end) + SECONDS_PER_DAY, SECONDS_PER_DAY):
        day = time.localtime(day)
        key = time.strftime('%Y-%m', day)  # use '%b' to answer the question exactly, but that's not such a good idea
        days[ key ] += 1
    return days

output = monthlyBedDays(admission="01-Jan-2018", discharge="25-Apr-2018")
print(output)
# Prints:
# defaultdict(<class 'int'>, {'2018-01': 31, '2018-02': 28, '2018-03': 31, '2018-04': 25})

print(monthlyBedDays(admission="01-Jan-2018", discharge="25-Apr-2018", fy=1718))
# Prints:
# defaultdict(<class 'int'>, {'2018-01': 31, '2018-02': 28, '2018-03': 31})

print(monthlyBedDays(admission="01-Jan-2018", discharge="25-Apr-2018", fy=1819))
# Prints:
# defaultdict(<class 'int'>, {'2018-04': 25})

请注意,输出是一个defaultdict,因此,如果您向它询问任何月份中没有记录的天数(或任何键)(例如output['1999-12']),它将返回0。还请注意,我对输出键使用了'%Y-%m'格式。与使用最初请求的密钥类型('%b'->;'Jan')相比,对输出进行排序和消除月份间的歧义变得更加容易。在

首先,我建议使用datetime.date实例,这样您就可以在使用类似的方法之前解析日期:

import datetime
date = datetime.datetime.strptime('17-Jan-2018', '%d-%b-%Y').date()

然后您可以使用类似的方法在日期范围内迭代:

^{pr2}$

用法如下:

>>> d1 = datetime.date(2018, 2, 5)
>>> d2 = datetime.date(2019, 1, 17)


>>> r = f(d1, d2, '1718')
>>> for k, v in sorted(r.items()):
...     print(k, v)
2018-02 24
2018-03 31

>>> r = f(d1, d2, '1819')
>>> for k, v in sorted(r.items()):
...     print(k, v)
2018-04 30
2018-05 31
2018-06 30
2018-07 31
2018-08 31
2018-09 30
2018-10 31
2018-11 30
2018-12 31
2019-01 17

相关问题 更多 >

    热门问题