【问题标题】:Convert Year/Month/Day to Day of Year in Python在 Python 中将年/月/日转换为年中的第几天
【发布时间】:2023-02-08 11:51:57
【问题描述】:

我正在使用 datetime 模块,即:

>>> import datetime
>>> today = datetime.datetime.now()
>>> print(today)
2009-03-06 13:24:58.857946

我想计算一年中考虑闰年的那一天。例如今天(2009年3月6日)是2009年的第65天。

我看到两个选项:

  1. 创建一个 number_of_days_in_month = [31, 28, ...] 数组,判断是否是闰年并手动计算天数。

  2. 使用 datetime.timedelta 进行猜测,然后二分查找一年中的正确日期:

    >>> import datetime
    >>> YEAR = 2009
    >>> DAY_OF_YEAR = 62
    >>> d = datetime.date(YEAR, 1, 1) + datetime.timedelta(DAY_OF_YEAR - 1)
    

    这些都感觉很笨重而且我有一种直觉,有一种更“Pythonic”的方式来计算一年中的一天。有什么想法/建议吗?

【问题讨论】:

    标签: python datetime


    【解决方案1】:

    使用datetime.timetuple() 将您的datetime 对象转换为time.struct_time 对象,然后获取其tm_yday 属性:

    from datetime import datetime
    day_of_year = datetime.now().timetuple().tm_yday  # returns 1 for January 1st
    

    【讨论】:

    • 一个非常小的并且可以说是迂腐的添加,但是使用 date.today() 而不是 datetime.now() 也可以工作并且更加强调操作的性质。
    • 笔记:这从 1 开始计数
    • 如果我想反过来怎么办,我的数字可以说是“2020 年的第 26 天”,我想将其转换为日期“26-01-2020”?
    • @Sankar 然后你用谷歌搜索这个问题,如果你没有找到任何东西,就问你自己的 Stack Overflow 问题。
    【解决方案2】:

    您可以将 strftime%j format string 一起使用:

    >>> import datetime
    >>> today = datetime.datetime.now()
    >>> today.strftime('%j')
    '065'
    

    但是如果你想用这个数字做比较或计算,你必须把它转换成int(),因为strftime()返回一个字符串。如果是这样,您最好使用DzinX's 回答。

    【讨论】:

    • 使用 strftime 是间接的,因为它从一个数字生成一个字符串:timetuple.tm_yday 成员。阅读源代码。在进行任何计算/比较之前,应将生成的字符串转换为数字,所以何必呢?
    • 如果需要在字符串中使用一年中的某一天,比如在文件名中使用,那么 strftime 是一个更简洁的解决方案。
    【解决方案3】:

    DZinX's answer 是这个问题的一个很好的答案。我发现了这个问题并在寻找反函数时使用了DZinX's answer:将日期与儒略年日期转换为日期时间。

    我发现这个工作:

    import datetime
    datetime.datetime.strptime('1936-077T13:14:15','%Y-%jT%H:%M:%S')
    
    >>>> datetime.datetime(1936, 3, 17, 13, 14, 15)
    
    datetime.datetime.strptime('1936-077T13:14:15','%Y-%jT%H:%M:%S').timetuple().tm_yday
    
    >>>> 77
    

    或数字:

    import datetime
    year,julian = [1936,77]
    datetime.datetime(year, 1, 1)+datetime.timedelta(days=julian -1)
    
    >>>> datetime.datetime(1936, 3, 17, 0, 0)
    

    或者使用在某些领域流行的基于小数 1 的 jdates:

    jdate_frac = (datetime.datetime(1936, 3, 17, 13, 14, 15)-datetime.datetime(1936, 1, 1)).total_seconds()/86400+1
    display(jdate_frac)
    
    >>>> 77.5515625
    
    year,julian = [1936,jdate_frac]
    display(datetime.datetime(year, 1, 1)+datetime.timedelta(days=julian -1))
    
    >>>> datetime.datetime(1936, 3, 17, 13, 14, 15)
    

    我不确定这里的礼节,但我认为指向反向功能的指针可能对像我这样的其他人有用。

    【讨论】:

      【解决方案4】:

      如果您有理由避免使用 datetime 模块,那么这些功能将起作用。

      def is_leap_year(year):
          """ if year is a leap year return True
              else return False """
          if year % 100 == 0:
              return year % 400 == 0
          return year % 4 == 0
      
      def doy(Y,M,D):
          """ given year, month, day return day of year
              Astronomical Algorithms, Jean Meeus, 2d ed, 1998, chap 7 """
          if is_leap_year(Y):
              K = 1
          else:
              K = 2
          N = int((275 * M) / 9.0) - K * int((M + 9) / 12.0) + D - 30
          return N
      
      def ymd(Y,N):
          """ given year = Y and day of year = N, return year, month, day
              Astronomical Algorithms, Jean Meeus, 2d ed, 1998, chap 7 """    
          if is_leap_year(Y):
              K = 1
          else:
              K = 2
          M = int((9 * (K + N)) / 275.0 + 0.98)
          if N < 32:
              M = 1
          D = N - int((275 * M) / 9.0) + K * int((M + 9) / 12.0) + 30
          return Y, M, D
      

      【讨论】:

      • 当 d、m、y 分别存在时很有用!
      【解决方案5】:

      我想展示不同方法在 Python 3.4、Linux x64 上的性能。线路分析器摘录:

            Line #      Hits         Time  Per Hit   % Time  Line Contents
            ==============================================================
               (...)
               823      1508        11334      7.5     41.6          yday = int(period_end.strftime('%j'))
               824      1508         2492      1.7      9.1          yday = period_end.toordinal() - date(period_end.year, 1, 1).toordinal() + 1
               825      1508         1852      1.2      6.8          yday = (period_end - date(period_end.year, 1, 1)).days + 1
               826      1508         5078      3.4     18.6          yday = period_end.timetuple().tm_yday
               (...)
      

      所以最有效的是

      yday = (period_end - date(period_end.year, 1, 1)).days + 1
      

      【讨论】:

      • 你是如何计算性能的?我尝试使用 time.time,这些是我的结果: def f(): (today - datetime.datetime(today.year, 1, 1)).days + 1 start = time.time(); F(); print('Lapsed {}'.format(time.time() - 开始));失效 5.221366882324219e-05 def f(): int(today.strftime('%j'));开始=时间.时间(); F(); print('Lapsed {}'.format(time.time() - 开始));失效 7.462501525878906e-05
      • 最后一行需要一个+1。应该是yday = (period_end - date(period_end.year, 1, 1)).days + 1
      【解决方案6】:

      只需从日期中减去 1 月 1 日:

      import datetime
      today = datetime.datetime.now()
      day_of_year = (today - datetime.datetime(today.year, 1, 1)).days + 1
      

      【讨论】:

      • d.toordinal() - date(d.year, 1, 1).toordinal() + 1 根据文档更标准。它相当于接受的答案而不产生整个时间元组。
      【解决方案7】:

      您可以简单地使用“pandas”提供的 dayofyear 属性,这反过来会为您提供特定年份的日期。 例如

      data["day_of_year"] = data.Datetime.apply(lambda x:x.dayofyear)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-04-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多