代码之家  ›  专栏  ›  技术社区  ›  Alexander Schillemans

每月分析一个日期并打印下一个日期

  •  3
  • Alexander Schillemans  · 技术社区  · 6 年前

    对于我目前正在开发的程序的功能,我需要能够分析日期,然后每月返回所有即将到来的日期。

    e、 g。 插入日期:2018年4月13日-->这是四月的第二个星期五。 回报:接下来几个月的所有第二个星期五(2018年5月11日、2018年6月8日、2018年7月13日……)

    另一个限制是,如果接下来的一个月的第一天是星期六或星期日,则不计算为第一周。第一周从第一周算起,日期比周末多。 示例:4月1日是星期日,因此4月的第一周从2日开始到8日。或者6月1日是星期五,所以6月的第一周是从5月28日到6月3日。

    我试过做一些事情,但似乎没有返回正确的日期。

    startdate = input("Enter a startdate: ")
    startdate = datetime.strptime(startdate, "%Y-%m-%d").date()
    dayOfTheWeek = startdate.weekday()
    first_day = startdate.replace(day=1)
    cal = calendar.Calendar()
    weeks = cal.monthdayscalendar(startdate.year, startdate.month)
    if(first_day.isoweekday() in (6,7)): adjustRange = 1
    else: adjustRange = 0
    for x in range(len(weeks)):
        if startdate.day in weeks[x]:
            weekNumb = x-adjustRange
    
    for x in range(0, 5):
        tempWeek = weekNumb
        print("Date: ", startdate)
        if(startdate.month + 1 > 12): added_months = 1
        else: added_months = startdate.month + 1
        weeks = cal.monthdayscalendar(startdate.year, added_months)
    
        first_day = startdate.replace(day=1)
        if(first_day.isoweekday() in (6,7)): tempWeek += 1
    
        plannedDay = weeks[tempWeek][dayOfTheWeek]
    
        if(added_months < 10): added_months = str(added_months).zfill(2)
        datestr = "{}-{}-{}".format(startdate.year, added_months, plannedDay)
        startdate = datetime.strptime(datestr, "%Y-%m-%d").date()
    

    输出:

    Enter a startdate: 2018-04-13
    Date:  2018-04-13
    Date:  2018-05-18 --> must be 2018-05-11
    Date:  2018-06-08 --> OK 
    Date:  2018-07-06 --> must be 2018-07-13
    Date:  2018-08-17 --> must be 2018-08-10
    

    看起来他们都要休息一周了,要么太多,要么就少了一周。

    基本上,我得到一周中的某一天(0-6),然后询问当月的第一天。然后,使用日历模块,我询问该月的所有周及其日期,并检查插入日期所在的周。如果一个月的第一天是星期六或星期天(使用.isoweekday()为6或7),那么我们必须从weeknumber中减去1才能得到实际的weeknumber。

    然后,我分析所有进一步的日期,如果他们的第一天也是星期六或星期天,我必须将一天添加到tempWeek(其中包含原始的weekNumb),因为我试图从日历中收到的week元组中提取日期。

    我希望这种方式有意义,我的代码也有一定的可读性。

    3 回复  |  直到 6 年前
        1
  •  2
  •   Chris Wesseling    6 年前

    如果你 pip install python-dateutil

    from dateutil.rrule import rrule, MONTHLY, weekdays
    from dateutil.parser import parse
    import math
    
    def month_week(date):
        return math.ceil((date - date.replace(day=1)).days / 7 ) or 1
    
    start_date = parse(input("Enter a startdate: "))
    for day in rrule(freq=MONTHLY,
                     count=5,
                     dtstart=start_date,
                     byweekday=weekdays[start_date.weekday()](
                         month_week(start_date)),
                     ):
        print("Date: ", day.date())
    

    将在2018年4月13日实现您的期望。

    根据您的问题,我不确定您对2018-09-01(9月1日星期六)或2018-01-29(1月5日星期五)的预期产量。因此,您可能需要更改month\u week函数,以返回您期望的月份的周数。

        2
  •  1
  •   Scott Mermelstein    6 年前

    你最大的问题是 for 在输出接下来4天的兴趣时循环。你有一句台词, first_day = startdate.replace(day=1) ,然后根据结果的等周设置tempWeek。问题是,您没有更新月份以反映添加的月份。这会把一切都抛到九霄云外。因此,将行更改为

    first_day = startdate.replace(day=1, month=added_months)
    

    但是你还需要跟踪年份——将月份从12滚动到1是很好的,但是当你这样做时,你也需要更新年份。这不会影响您的即时测试,但您在年底前尝试的任何其他测试都会受到影响。

    for x in range(0, 5):
        tempWeek = weekNumb
        print("Date: ", startdate)
        if(startdate.month + 1 > 12): 
            added_months = 1
            added_years = startdate.year + 1
        else: 
            added_months = startdate.month + 1
            added_years = startdate.year
        weeks = cal.monthdayscalendar(added_years, added_months)
    
        first_day = startdate.replace(day=1, month=added_months, year=added_years)
        if(first_day.isoweekday() in (6,7)): tempWeek += 1
    
        plannedDay = weeks[tempWeek][dayOfTheWeek]
    
        if(added_months < 10): added_months = str(added_months).zfill(2)
        datestr = "{}-{}-{}".format(startdate.year, added_months, plannedDay)
        startdate = datetime.strptime(datestr, "%Y-%m-%d").date()
    

    这样做应该可以解决你的问题。

    额外提示:

    • 我只需在交互式python shell中键入每一行就可以解决这个问题,并且每当我设置一个变量时,我都会查看它。当您在这方面做得更好时,您可能会学习开始使用调试器,这将为您提供相同的效果,但更简单。

    • 我建议你不要跺脚 startdate .你所做的是完全正确的,但奇怪的是你保留了一些变量,却践踏了其他变量。为了与您拥有的变量名保持一致,我建议 added_date ,但在下面的代码中,我将其更改为 first_date

    • 覆盖也很奇怪(但允许) added_months (整数)及其字符串表示形式。同样,我会使用不同的变量,比如 str_months .让一个变量做一件事通常会使读取更加顺畅。在下面的示例代码中,我利用 format

    • 一般来说,不把结果放在 if 如果 .现在,空白真的很便宜,而且它使阅读更容易。

    • 如果你的最终结果 对于 循环在末尾,而不是中途。

    • 此外,您应该渴望在内部与您的姓名保持一致,可以是camelCase或下划线的\u名称,但最好不要两者都是。

    • 尽可能避免使用for循环是pythonic,通常如果使用“for x in len(…”,有一种更有效的方法。正如注释中提到的,enumerate可以做到这一点。

    • python中的一个好标准是使用 _ 对于您不关心的变量,例如在fixed for循环中。

    以上代码是使代码正常工作的最低限度的更改。下面是根据我刚才提到的提示,我将进行的修改。此外,我还包括了import语句,因为没有它们,代码就不可执行。我们总是试图在堆栈溢出时在此发布可执行代码。

    from datetime import datetime
    import calendar
    
    start_date = input("Enter a startdate: ")
    
    start_date = datetime.strptime(start_date, "%Y-%m-%d").date()
    day_of_the_week = start_date.weekday()
    first_day = start_date.replace(day=1)
    cal = calendar.Calendar()
    weeks = cal.monthdayscalendar(start_date.year, start_date.month)
    # This if statement could be a single line: 
    # adjust_range = 1 if first_day.isoweekday() in (6,7) else 0
    if(first_day.isoweekday() in (6,7)):
        adjust_range = 1
    else: 
        adjust_range = 0
    
    for x, week in enumerate(weeks):
        if start_date.day in week:
            week_numb = x - adjust_range
            # This will happen only once, so you can break out of the loop
            break
    
    print("Date: ", start_date)
    
    first_date = start_date.replace(day=1)
    for _ in range(4):
        temp_week = week_numb
        if(first_date.month + 1 > 12): 
            new_months = 1
            new_years = first_date.year + 1
        else: 
            new_months = first_date.month + 1
            new_years = first_date.year
    
        weeks = cal.monthdayscalendar(new_years, new_months)
        first_date = first_date.replace(day=1, month=new_months, year=new_years)
        if(first_date.isoweekday() in (6,7)):
            temp_week += 1
    
        planned_day = weeks[temp_week][day_of_the_week]
    
        # The if wasn't needed; let format do the work for you.
        print("{}-{:02d}-{:02d}".format(new_years, new_months, planned_day))
    
        # You could set something like 
        # interested_date = datetime(added_years, added_months, planned_day), 
        # but you don't seem to use it.
    
        3
  •  1
  •   Alexander Schillemans    6 年前

    我一直在玩这个。给出的答案都很有帮助,帮助我进一步理解了这个问题,谢谢你。您可能会在这里看到我使用过的其他人的一些代码片段。在这种情况下,我决定使用日历模块来帮助我。

    import calendar
    from datetime import datetime
    from datetime import timedelta
    import math
    
    
    cal = calendar.Calendar()
    
    start_date = datetime.strptime(input("Enter a start date: "), "%Y-%m-%d").date()
    end_date = datetime.strptime(input("Enter an end date: "), "%Y-%m-%d").date()
    days_difference = (end_date - start_date).days
    week_number = math.ceil((start_date - start_date.replace(day=1)).days / 7) - 1
    weekday = start_date.weekday()
    
    for _ in range(15):
        weeks = cal.monthdayscalendar(start_date.year, start_date.month)
    
        for x, week in enumerate(weeks):
            if(week[weekday] == 0):
                weeks.pop(x)
                break
    
        if(len(weeks) == 5 and (week_number+1) == 4):
            if(weeks[len(weeks)-1][weekday] != 0 and weeks[0][weekday] != 0):
                weeks.pop(0)
    
        new_weekday = weeks[week_number][weekday]
    
        start_date = datetime(start_date.year, start_date.month, new_weekday).date()
        print(start_date)
    
        for x in range(1, days_difference+1):
            start_date += timedelta(days=1)
            print(start_date)
    
        if(start_date.month + 1 > 12):
            new_year = start_date.year + 1
            new_month = 1
        else:
            new_month = start_date.month + 1
            new_year = start_date.year
    
        start_date = datetime(new_year, new_month, 1).date()
    
        print("")
    

    输出:

    Enter a start date: 2018-04-12
    Enter an end date: 2018-04-13
    2018-04-12
    2018-04-13
    
    2018-05-10
    2018-05-11
    
    2018-06-14
    2018-06-15
    
    2018-07-12
    2018-07-13
    ...
    

    我所做的是要求一个开始日期和一个结束日期(两者紧随其后)。然后,我用日历询问周数和日期。monthdayscalendar()。我检查了它们,如果在其中发现一周中给定日期的工作日不存在,它会将其从列表中删除,因为我们不需要它。

    然后,这就是问题的症结所在:我收集数据的平台将某物的四分之一转换为某物的五分之一,而这一天在当月发生了第五次。 示例:我插入2018-09-25,这是星期二的第四天,但下一个日期将是2018-10-30,这是第五个星期二,因为该月发生了五次。我自己花了一段时间才发现这一点(也是为什么我在开始时说周六或周日开始的几周不算,但这并不完全准确:我道歉)。

    因此,如果一个月有五个星期,而原来的星期数是4,那么我们会弹出第一个星期,因为我们不需要它,星期数的索引可以保持不变,以收集正确的日期。对于结束日期,我只是继续增加一天,直到差额用完。

    再次感谢大家的贡献。我尝试查看dateutil,但由于第四个某物会变为第五个某物,如果当月发生五次,这并不是一个解决方案。