【问题标题】:What's the best way to find the inverse of datetime.isocalendar()?找到 datetime.isocalendar() 的倒数的最佳方法是什么?
【发布时间】:2010-09-23 04:28:33
【问题描述】:

Python datetime.isocalendar() 方法为给定的 datetime 对象返回一个元组 (ISO_year, ISO_week_number, ISO_weekday)。有对应的反函数吗?如果没有,是否有一种简单的方法可以计算给定年份、星期数和星期几的日期?

【问题讨论】:

    标签: python datetime


    【解决方案1】:

    编辑:忽略这一点,边缘情况很痛苦。选择 Ben 的解决方案。

    好的,经过仔细检查,我注意到 strptime 具有 %W 和 %w 参数,因此以下工作有效:

    def fromisocalendar(y,w,d):
       return datetime.strptime( "%04dW%02d-%d"%(y,w-1,d), "%YW%W-%w")
    

    几个问题:ISO 周数从1 开始,而%W 从0 开始。 ISO 工作日从1(星期一)开始,与%w 相同,因此星期日可能必须是0,而不是7...

    【讨论】:

    • 您使用的是哪个版本?,我在 2.4.2 上测试,但对我不起作用,找到 bugs.python.org/issue1045381,但说它已在 2.3 上完成
    • 这看起来像是你回答的。为什么不接受你的回答?
    • @Tom:S.O.确实让您接受自己的答案。请参阅有关元的讨论:meta.stackexchange.com/questions/9933/…
    • @GreenMatt,是的,自从我发表评论以来,该功能是新的。无论如何,这个答案不如 Ben 的好。
    • @Tom:哎呀!没发现这是 08 年的。
    【解决方案2】:

    请注意,%W 是第 # 周 (0-53),它与 ​​ISO 周 (1-53) 不同。将存在 %W 不起作用的边缘情况。

    【讨论】:

    • 我有点注意到,在我上面的回答中,您是否有机会扩展这些边缘情况?
    • @Tom,例如 2009 年和 2013 年。
    【解决方案3】:

    Python 3.8 添加了fromisocalendar() 方法:

    >>> datetime.fromisocalendar(2011, 22, 1)
    datetime.datetime(2011, 5, 30, 0, 0)
    

    Python 3.6 添加了%G, %V and %u directives:

    >>> datetime.strptime('2011 22 1', '%G %V %u')
    datetime.datetime(2011, 5, 30, 0, 0)
    

    原答案

    我最近不得不自己解决这个问题,并想出了这个解决方案:

    import datetime
    
    def iso_year_start(iso_year):
        "The gregorian calendar date of the first day of the given ISO year"
        fourth_jan = datetime.date(iso_year, 1, 4)
        delta = datetime.timedelta(fourth_jan.isoweekday()-1)
        return fourth_jan - delta 
    
    def iso_to_gregorian(iso_year, iso_week, iso_day):
        "Gregorian calendar date for the given ISO year, week and day"
        year_start = iso_year_start(iso_year)
        return year_start + datetime.timedelta(days=iso_day-1, weeks=iso_week-1)
    

    几个测试用例:

    >>> iso = datetime.date(2005, 1, 1).isocalendar()
    >>> iso
    (2004, 53, 6)
    >>> iso_to_gregorian(*iso)
    datetime.date(2005, 1, 1)
    
    >>> iso = datetime.date(2010, 1, 4).isocalendar()    
    >>> iso
    (2010, 1, 1)
    >>> iso_to_gregorian(*iso)
    datetime.date(2010, 1, 4)
    
    >>> iso = datetime.date(2010, 1, 3).isocalendar()
    >>> iso
    (2009, 53, 7)
    >>> iso_to_gregorian(*iso)
    datetime.date(2010, 1, 3)
    

    【讨论】:

    • 你能解释一下 1 月 4 日的意义吗?是否在适当的地方描述了 ISO 日历标准?
    • Tom:根据 ISO 对第 1 周(包含一年中的第一个星期四的那一周)的正式定义,1 月 4 日是第 1 周可以开始的最晚日期。
    • 感谢您的解决方案。但是,fourth_jan 的生成对我不起作用(使用 Python 2.7),我不得不写:fourth_jan = datetime.strptime('{0}-01-04'.format(iso_year), '%Y-%m-%d')。
    • 这是一个很好的答案,但它可以更简单。您无需计算一年的开始日期。看我的回答。
    【解决方案4】:
    import datetime
    
    def iso_to_gregorian(iso_year, iso_week, iso_day):
        "Gregorian calendar date for the given ISO year, week and day"
        fourth_jan = datetime.date(iso_year, 1, 4)
        _, fourth_jan_week, fourth_jan_day = fourth_jan.isocalendar()
        return fourth_jan + datetime.timedelta(days=iso_day-fourth_jan_day, weeks=iso_week-fourth_jan_week)
    

    这是改编自@BenJames 的非常好的答案。你不必知道一年的第一天。您只需要知道肯定在同一 ISO 年份的日期示例,以及该日期的 ISO 日历周和日。

    1 月 4 日只是一个例子,因为正如 Ben 指出的那样,1 月 4 日总是属于同一个 ISO 年和公历年,并且是一年中的第一天。

    由于周长相同,您可以简单地减去所需日期的 ISO 和您在两种形式中都知道的日期的 ISO 之间的天数和周数,然后加上该天数和周数. (这些数字是正数还是负数都没有关系,因此您可以选择其他“固定日期”,例如 12 月 28 日。)

    编辑

    我更正了这一点,因为正如@JoSo 所指出的,公历年的第一天也属于 ISO 年,是 1 月 4 日而不是 1 月 5 日。正如解释所说,选择哪个日期作为参考点并不重要,但选择 1 月 4 日会使这个选择不那么“神奇”。

    【讨论】:

    • 你能告诉我,如果它真的在每种情况下都完美无瑕吗?
    • @GeorgeSolymosi 我使用了假设 Python 测试库来搜索任何有效的 datetime 对象,该函数不是 isocalendar 的逆函数。它没有找到任何/参见github.com/jwg4/qual/blob/master/qual/tests/test_iso.py 当然这不是保证 - 我有兴趣了解任何错误。如果您仔细研究代码的逻辑,您可能会也可能不会相信它做了正确的事情。
    • 这比必要的要复杂。看看 Ben 的回答或 Wikipedia。 1 月 4 日也是如此,而且始终位于其 ISO 年的第一个 ISO 周。
    • @JoSo 这怎么比 Ben 的回答复杂?
    • 不在其一年中的第一个 ISO 周。您可以使用 1 月 4 日到 12 月 28 日之间的任何日期,但只有 1 月 4 日始终位于第一周。如果您考虑 Wikipedia,则 1 月 5 日的选择很奇怪,并且您必须使用 1 月 5 日的 ISO 周来计算,而如果您选择 1 月 4 日则不需要。(请参阅我的清理答案)。
    【解决方案5】:

    从 Python 3.6 开始,datetime.strptime() 将支持%G、%V 和%u 指令,因此您可以简单地使用datetime.strptime('2015 1 2', '%G %V %u').date()。见:https://hg.python.org/cpython/rev/acdebfbfbdcf

    【讨论】:

      【解决方案6】:

      从 Python 3.6 开始,您可以使用新的 %G、%u 和 %V 指令。见issue 12006 和updated documentation:

      %G
      ISO 8601 年,世纪代表包含 ISO 周大部分时间的年份 (%V)。

      %u
      ISO 8601 weekday 作为十进制数,其中 1 是星期一。

      %V
      ISO 8601 周为十进制数,星期一为一周的第一天。第 01 周是包含 1 月 4 日的那一周。

      给定一个包含年份、周数和工作日数的字符串,很容易将它们解析为日期:

      from datetime import datetime
      
      datetime.strptime('2002 01 1', '%G %V %u').date()
      

      或作为具有整数输入的函数:

      from datetime import datetime
      
      def date_from_isoweek(iso_year, iso_weeknumber, iso_weekday):
          return datetime.strptime(
              '{:04d} {:02d} {:d}'.format(iso_year, iso_weeknumber, iso_weekday),
              '%G %V %u').date()
      

      【讨论】:

      • 如果收到错误AttributeError: type object 'datetime.date' has no attribute 'strptime',请改用from datetime import datetime。
      • @yl_low 完全正确,不知道为什么我在这里犯了这个错误,或者为什么有人花了这么长时间才注意到!现在全部更正。
      【解决方案7】:

      对于下一个来到这里的人,Ben 的好解决方案的更短的、单分辨率版本:

      def iso_to_gregorian(iso_year, iso_week, iso_day):
          jan4 = datetime.date(iso_year, 1, 4)
          start = jan4 - datetime.timedelta(days=jan4.isoweekday()-1)
          return start + datetime.timedelta(weeks=iso_week-1, days=iso_day-1)
      

      【讨论】:

        【解决方案8】:

        我想出了一个类似于 Ben James 发布的解决方案,但使用了一个函数:

        import datetime
        def getDateFromWeek(year,week,day):
            """Method to retrieve the date from the specified week, year and weekday"""
        
            year_start = datetime.date(year,1,1)
            ys_weekday =  year_start.weekday()
            delta = (week*7)+(day-ys_weekday)
            if ys_weekday<4:
                delta -= 7
        
            return  year_start  + datetime.timedelta(days=delta)
        

        我用 2020 年最后一周和 2021 年第一周等边界值对其进行了测试,结果非常好。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-03-15
          • 2016-05-31
          • 1970-01-01
          • 2013-03-13
          • 2013-08-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多