为了账号安全,请及时绑定邮箱和手机立即绑定

测量两个日期之间的月份:月份的立法定义

测量两个日期之间的月份:月份的立法定义

绝地无双 2022-08-25 14:39:07
我希望构建代码,代表澳大利亚立法中月份的定义 - 解释法(1987)。请注意,我仍然是Python的相对新手。法律定义定义如下:(1) 在任何法案中,月是指一个时期:(a)从其中一个日历月的任何一天的开始开始;(b)结束:(i)在下一个日历月相应日期开始前一刻结束;或。(ii) 如果没有这样的日期,即在下一个日历月的月底。我被告知,这个定义意味着如果一个月的开始时间从16/07/2019开始,例如,出于a)的目的,相关月份直到11:59:59:59:etc:pm在15/08/2019结束 - 或者从功能上讲,16/08/2019。因此,出于 b) 的目的,“月末”的定义与该月相关最后一天的 11:59:59:etc:etc:pm 类似。因此,如果您有两个日期 - 31 / 08 / 2019和30 / 09 / 2019 - 相关月份直到30 / 09 / 2019的11:59:59:etc:pm为止 - 或者功能上是01 / 10 / 2019。我需要以月为单位输出两个日期之间的差异,以反映我正在编码的立法要求两个日期之间的差异,特别是以月份为单位。如果可能的话,我希望使用datetime或datetime64对象来执行此操作,以避免不必要地在变量之间进行转换。到目前为止,我尝试过什么。我使用以下代码来查找月份中两个日期之间的差异,使用相对delta:from datetime import datetimefrom dateutil import relativedeltadate1 = datetime.strptime('2019-08-15', '%Y-%m-%d')date2 = datetime.strptime('2020-02-05', '%Y-%m-%d')r = relativedelta.relativedelta(date2, date1)r.months + (12*r.years)print(r)我的预期产量是5个月,因为有五个完整的月份,然后是一个月的一小部分,而不是在date2之前完成。这将返回预期的结果,并在立法中复制 a) 的功能。但是,当我尝试使用以下代码复制b)时:from datetime import datetimefrom dateutil import relativedeltadate1 = datetime.strptime('2019-08-31', '%Y-%m-%d')date2 = datetime.strptime('2019-11-30', '%Y-%m-%d')r = relativedelta.relativedelta(date2, date1)r.months + (12*r.years)print(r)这将返回 4 个月的结果。由于2019-11-30不是相关日历月的结束,这是不正确的 - 我应该获得此代码的3个月结果,因为该月直到11:59:59:etc才完成。预期成果下面是我用来测试此代码结果的四个测试用例。from datetime import datetimefrom dateutil import relativedeltadate1 = datetime.strptime('2019-08-25', '%Y-%m-%d')date2 = datetime.strptime('2019-09-10', '%Y-%m-%d')r = relativedelta.relativedelta(date2, date1)r.months + (12*r.years)r.months = 0from datetime import datetimefrom dateutil import relativedeltadate1 = datetime.strptime('2019-08-25', '%Y-%m-%d')date2 = datetime.strptime('2019-09-25', '%Y-%m-%d')r = relativedelta.relativedelta(date2, date1)r.months + (12*r.years)r.months = 1
查看完整描述

3 回答

?
幕布斯6054654

TA贡献1876条经验 获得超7个赞

这可以在不转换为日期类型的情况下进行计算,但边缘情况除外,其中日期是该月的最后一天(它们实际上对应于下个月的第零天)。


from datetime import date


def isLastDay(y,m,d):

    return date.fromordinal(date(y,m,d).toordinal()+1).month != m


def legalMonthDif(date1,date2):

    y1,m1,d1 = map(int,date1.split("-"))

    y2,m2,d2 = map(int,date2.split("-"))

    if isLastDay(y1,m1,d1): m1,d1 = m1+1,0

    if isLastDay(y2,m2,d2): m2,d2 = m2+1,0

    return y2*12+m2 -y1*12-m1 -(d2<d1)

输出:


legalMonthDif('2019-08-15','2020-02-05') #5

legalMonthDif('2019-08-31','2019-11-30') #3

legalMonthDif('2019-08-25','2019-09-10') #0

legalMonthDif('2019-08-25','2019-09-25') #1

legalMonthDif('2019-08-31','2019-11-30') #3

legalMonthDif('2019-08-01','2019-12-01') #4 

legalMonthDif('2019-08-31','2019-12-01') #3

legalMonthDif('2019-08-15','2019-12-01') #3

您也可以通过实现daysOfMonth函数来计算任何月份中的天数,完全无需datetime库即可完成此操作:


def daysOfMonth(y,m):

    return 30+(m+m//8)%2-(m==2)*(2-(y%4==0 and not y%100==0 or y%400==0))


def legalMonthDif(date1,date2):

    y1,m1,d1 = map(int,date1.split("-"))

    y2,m2,d2 = map(int,date2.split("-"))

    if daysOfMonth(y1,m1) == d1: m1,d1 = m1+1,0

    if daysOfMonth(y2,m2) == d2: m2,d2 = m2+1,0

    return y2*12+m2 -y1*12-m1 -(d2<d1)


查看完整回答
反对 回复 2022-08-25
?
收到一只叮咚

TA贡献1821条经验 获得超5个赞

我最终编写了以下函数,这些函数捕获了此立法的预期功能:


def find_corresponding_date(start_date):

day = start_date.day

month = start_date.month

year = start_date.year

next_month = month + 1

next_year = year


if month == 12:

    next_month = 1

    next_year = year + 1

try:

    new_date = py_datetime(year=next_year, month=next_month, day=day)

except ValueError:

    next_month = next_month + 1

    if next_month == 13:

        next_month = 1

        next_year = next_year + 1

    new_date = py_datetime(year=next_year, month=next_month, day=1)

    return new_date


else:

    return new_date



def toPyDateTime(numpyDate):

    return py_datetime.strptime(str(numpyDate), "%Y-%m-%d")



def count_months(sdate, edate):

    start_date = toPyDateTime(sdate)

    end_date = toPyDateTime(edate)

    count = 0

    corres_date = start_date

    while(True):

        corres_date = find_corresponding_date(corres_date)

        if(corres_date > end_date):

            return count

            break

        else:

            count = count + 1


查看完整回答
反对 回复 2022-08-25
?
慕村225694

TA贡献1880条经验 获得超4个赞

dates = [('2019-07-16','2019-08-15'),('2019-08-31','2019-09-30'),

         ('2019-08-15','2020-02-05'),('2019-08-31','2019-11-30'),

         ('2019-08-25','2019-09-10'),('2019-08-25','2019-09-25'),

         ('2019-08-31','2019-12-01'),('2019-08-15' , '2019-12-01'),

         ('2019-08-01', '2019-11-30'),('2019-08-01', '2019-12-01')]

使用熊猫日期时间功能。这依赖于这样一个事实,即如果结果日期不存在,则在时间戳中添加月份将被截断到月底 - 提供一种测试规范的(b)(ii)部分的方法。


import pandas as pd


def f(a,b):

    earlier,later = sorted((a,b))

    rel_months = later.month - earlier.month

    delta_months = rel_months + (later.year - earlier.year) * 12

    period_end = earlier + pd.DateOffset(months=delta_months)


    # sentinals for implementing logic of (b)(ii) of the definition

    period_end_isEOM = period_end + pd.tseries.offsets.MonthEnd(0)

    later_isEOM = later == later + pd.tseries.offsets.MonthEnd(0)

    next_month = period_end + pd.tseries.offsets.MonthBegin(0)


    # begin with the delta - period_end == later - then adjust

    months = delta_months

    # this is straightforward

    if period_end > later:

        months -= 1


    # did period_end get truncated to the end of a month

    if period_end_isEOM and (period_end.day < earlier.day):

        # actual end of period would be beginning of next month

        if later < next_month:    # probably also means later_isEOM or later == period_end

            months -= 1

    return months 


for a,b in dates:

   a, b = map(pd.Timestamp, (a,b))

   c = f(a,b)

   print(f'{a.date()} - {b.date()} --> {c}')


>>>

2019-07-16 - 2019-08-15 --> 0

2019-08-31 - 2019-09-30 --> 0

2019-08-15 - 2020-02-05 --> 5

2019-08-31 - 2019-11-30 --> 2

2019-08-25 - 2019-09-10 --> 0

2019-08-25 - 2019-09-25 --> 1

2019-08-31 - 2019-12-01 --> 3

2019-08-15 - 2019-12-01 --> 3

2019-08-01 - 2019-11-30 --> 3

2019-08-01 - 2019-12-01 --> 4

>>> 

pd.时间戳是datetime.datetime


这似乎有效 - 只有OP可以判断 - 但我忍不住想到我仍然没有利用一些内置功能。应该能够子类熊猫。日期偏移并自定义它以使计算更容易。


使用 Pandas.DateOffset 子类的解决方案。


from pandas import DateOffset, Timestamp

from pandas.tseries.offsets import MonthBegin


class LegislativeMonth(DateOffset):

    def __init__(self, n=1, normalize=False, months=1):

        # restricted to months

        kwds = {'months':months}

        super().__init__(n=1, normalize=False, **kwds)

    def apply(self,other):

        end_date = super().apply(other)

        if end_date.day < other.day:

            # truncated to month end

            end_date = end_date + MonthBegin(1)

        return end_date


for a,b in dates:

   earlier,later = sorted(map(Timestamp, (a,b)))

   delta_months = later.month - earlier.month

   delta_months += (later.year - earlier.year) * 12

   end_of_period = earlier + LegislativeMonth(months=delta_months)

   if end_of_period > later:

       delta_months -= 1

   print(f'{earlier.date()} - {later.date()} --> {delta_months}')


# another


one_month = LegislativeMonth(months=1)

for a,b in dates:

   earlier,later = sorted(map(Timestamp, (a,b)))

   end_period = earlier

   months = 0

   while later >= end_period + one_month:

       months += 1

       end_period += one_month

   print(f'{earlier.date()} - {later.date()} --> {months}')

最后,如果您确保以较早的日期作为第一项来调用它,它看起来像是会做您想要的 -relativedelta(earlier,later)


from datetime import datetime

from dateutil.relativedelta import relativedelta


for a,b in dates:

##   earlier,later = sorted(map(Timestamp, (a,b)))

    earlier,later = sorted((datetime.strptime(a, '%Y-%m-%d'),

                            datetime.strptime(b, '%Y-%m-%d')))

    rd = relativedelta(earlier,later)

    print(f'{earlier.date()} - {later.date()} --> {abs(rd.months)}')

使用此帖子顶部的日期,所有日期都将打印以下内容:


2019-07-16 - 2019-08-15 --> 0

2019-08-31 - 2019-09-30 --> 0

2019-08-15 - 2020-02-05 --> 5

2019-08-31 - 2019-11-30 --> 2

2019-08-25 - 2019-09-10 --> 0

2019-08-25 - 2019-09-25 --> 1

2019-08-31 - 2019-12-01 --> 3

2019-08-15 - 2019-12-01 --> 3

2019-08-01 - 2019-11-30 --> 3

2019-08-01 - 2019-12-01 --> 4


查看完整回答
反对 回复 2022-08-25
  • 3 回答
  • 0 关注
  • 122 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号