python如何将日期时间日期转换为十进制年份

时间:2011-06-23 09:03:34

标签: python date

我正在寻找一种将datetime对象转换为十进制年的方法。以下是一个例子

>>> obj = SomeObjet()
>>> obj.DATE_OBS
datetime.datetime(2007, 4, 14, 11, 42, 50)

如何将datetime.datetime(2007,4,14,11,42,50)转换为十进制年份?从这种格式dd / mm / yyyy到这种格式yyyy.yyyy

8 个答案:

答案 0 :(得分:21)

from datetime import datetime as dt
import time

def toYearFraction(date):
    def sinceEpoch(date): # returns seconds since epoch
        return time.mktime(date.timetuple())
    s = sinceEpoch

    year = date.year
    startOfThisYear = dt(year=year, month=1, day=1)
    startOfNextYear = dt(year=year+1, month=1, day=1)

    yearElapsed = s(date) - s(startOfThisYear)
    yearDuration = s(startOfNextYear) - s(startOfThisYear)
    fraction = yearElapsed/yearDuration

    return date.year + fraction

演示:

>>> toYearFraction(dt.today())
2011.47447514

这种方法可能精确到秒(或如果夏令时或其他奇怪的区域事物有效)。它在leapyears期间也能正常工作。如果您需要极高的分辨率(例如由于地球自转的变化),您最好不要查询网络服务。

答案 1 :(得分:2)

我假设您使用它来比较日期时间值。为此,请使用timedelta对象,而不是重新启动方向盘。

示例:

>>> from datetime import timedelta
>>> from datetime import datetime as dt
>>> d = dt.now()
>>> year = timedelta(days=365)
>>> tomorrow = d + timedelta(days=1)
>>> tomorrow + year > d + year
True

如果由于某种原因你确实需要十进制年,datetime对象方法strftime()可以给出一年中某一天的整数表示如果要求%j - 如果这是您正在寻找的,请参阅下面的简单样本(仅限1天的分辨率):

>>> from datetime import datetime
>>> d = datetime(2007, 4, 14, 11, 42, 50)
>>> (float(d.strftime("%j"))-1) / 366 + float(d.strftime("%Y"))
2007.2814207650274

答案 2 :(得分:1)

在实施接受的解决方案后,我得知这个现代熊猫版本是相同的,更简单:

dat['decimal_date']=dat.index.year+ (dat.index.dayofyear -1)/365

必须在日期时间索引Pandas数据帧上使用。添加为此解决方案的帖子出现在谷歌搜索此问题的顶部。

答案 3 :(得分:1)

简答

日期到十进制年份转换的定义含糊不清,超出了 0.002 年(~1 天)的精度。对于小数精度不重要的情况,这将起作用:

# No library needed, one-liner that's probably good enough                                                                                                                  
def decyear4(year, month, day, h=0, m=0, s=0) :                                                                                                                             
    return year + ((30.4375*(month-1) + day-1)*24+h)*3600/31557600.0 

如果您需要精度优于 0.005 年(约 2 天),您应该使用其他东西(例如,自纪元以来的秒数,或类似的)。如果您被迫(或只是真的、真的想这样做)使用十进制年份,请继续阅读。

长答案

与之前发布的一些答案和评论相反,“小数年”日期/时间戳不是一个明确定义的数量。当您考虑十进制年份的概念时,您可能希望有两个属性为真:

  1. 年初和年末的完美插值:
    2020 年 1 月 1 日上午 12:00:00 对应 2020.000
    2020, Dec 31 11:59:59.999... pm 将对应于 2020.999...

  2. 常数单位(即线性映射):
    2020.03-2020.02 == 2021.03-2021.02

不幸的是,您不能同时满足这两个条件,因为闰年​​和非闰年的 1 年时间长度不同。第一个要求是大多数以前的答案都试图满足的要求。但在许多(大多数?)情况下,实际上可能会使用十进制年份(例如,它将用于某种回归或模型中),那么第二个属性同样(如果不是更重要)同样重要。

这里有一些选项。我为 numpy 以矢量化形式做了这些,所以如果不需要 numpy,其中一些可以简化一些。

import numpy as np 
# Datetime based 
# Non-linear time mapping! (Bad for regressions, models, etc.
# e.g. 2020.2-2020.1 != 2021.2-2021.1) 
def decyear1(year, month, day, h=0, m=0, s=0) :
    import datetime
    year_seconds = (datetime.datetime(year,12,31,23,59,59,999999)-datetime.datetime(year,1,1,0,0,0)).total_seconds()
    second_of_year = (datetime.datetime(year,month,day,h,m,s) - datetime.datetime(year,1,1,0,0,0)).total_seconds()
    return year + second_of_year / year_seconds

# Basically the same as decyear1 but without datetime library
def decyear2(year, month, day, h=0, m=0, s=0) :
    leapyr = ((np.r_[year]%4==0) * (np.r_[year]%100!=0) + (np.r_[year]%400==0)).astype(int)
    day_of_year = np.r_[0,31,28,31,30,31,30,31,31,30,31,30,31].cumsum()
    year_seconds = ( (day_of_year[-1]+leapyr )*24*3600)
    extraday = np.r_[month>2].astype(int)*leapyr 
    second_of_year = (((( day_of_year[month-1]+extraday + day-1)*24 + h)*60+m)*60+s)
    return year + second_of_year / year_seconds   

# No library needed
# Linear mapping, some deviation from some conceptual expectations 
# e.g. 2019.0000 != exactly midnight, January 1, 2019
def decyear3(year, month, day, h=0, m=0, s=0) :
    refyear = 2015
    leapyr = ((np.r_[year]%4==0) * (np.r_[year]%100!=0) + (np.r_[year]%400==0)).astype(int)
    day_of_year = np.r_[0,31,28,31,30,31,30,31,31,30,31,30,31].cumsum()
    extraday = np.r_[month>2].astype(int)*leapyr 
    year_seconds = 31557600.0 # Weighted average of leap and non-leap years
    seconds_from_ref = ((year-refyear)*year_seconds + (((( day_of_year[month-1]+extraday + day-1)*24+h)*60 + m)*60 +s))
    return refyear + seconds_from_ref/year_seconds

# No library needed, one-liner that's probably good enough
def decyear4(year, month, day, h=0, m=0, s=0) :
    return year + ((30.4375*(month-1) + day-1)*24+h)*3600/31557600.0

# Just for fun - empirically determined one-liner (e.g. with a linear fit)
def decyear5(year, month, day, h=0, m=0, s=0) :
    return -8.789580e-02 + year + 8.331180e-02*month + 2.737750e-03*day + 1.142047e-04*hr + 2.079919e-06*mn + -1.731524e-07*sec

#
# Code to compare conversions
#
N = 500000
year = np.random.randint(1600,2050,(N))
month = np.random.randint(1,12,(N))
day = np.random.randint(1,28,(N))
hr = np.random.randint(0,23,(N))
mn = np.random.randint(0,59,(N))
sec = np.random.randint(0,59,(N))
s = ('decyear1','decyear2','decyear3','decyear4','decyear5')
decyears = np.zeros((N,len(s)))
for f, i in zip( (np.vectorize(decyear1), decyear2, decyear3, decyear4, decyear5), range(len(s)) ) : 
    decyears[:,i] = f(year,month,day,hr,mn,sec)

avg, std, mx = np.zeros((len(s),len(s)), 'float64'),np.zeros((len(s),len(s)), 'float64'),np.zeros((len(s),len(s)), 'float64')
for i in range(len(s)) : 
    for j in range(len(s)) :
        avg[i,j] = np.abs(decyears[:,i]-decyears[:,j]).mean()*365*24
        std[i,j] = (decyears[:,i]-decyears[:,j]).std()*365*24
        mx[i,j] = np.abs(decyears[:,i]-decyears[:,j]).max()*365*24

import pandas as pd 
unit = " (hours, 1 hour ~= .0001 year)"
for a,b in zip((avg, std, mx),("Average difference"+unit, "Standard dev.", "Max difference")) :
    print(b+unit)
    print(pd.DataFrame(a, columns=s, index=s).round(3))
    print()

并且听听他们如何在伪随机日期集合上进行比较:

Average magnitude of difference (hours, 1 hour ~= .0001 year) 
          decyear1  decyear2  decyear3  decyear4  decyear5
decyear1     0.000     0.000     4.035    19.258    14.051
decyear2     0.000     0.000     4.035    19.258    14.051
decyear3     4.035     4.035     0.000    20.609    15.872
decyear4    19.258    19.258    20.609     0.000    16.631
decyear5    14.051    14.051    15.872    16.631     0.000

Standard dev of difference (hours, 1 hour ~= .0001 year)
          decyear1  decyear2  decyear3  decyear4  decyear5
decyear1     0.000     0.000     5.402    16.550    16.537
decyear2     0.000     0.000     5.402    16.550    16.537
decyear3     5.402     5.402     0.000    18.382    18.369
decyear4    16.550    16.550    18.382     0.000     0.673
decyear5    16.537    16.537    18.369     0.673     0.000

Max difference (hours, 1 hour ~= .0001 year)
          decyear1  decyear2  decyear3  decyear4  decyear5
decyear1     0.000     0.000    16.315    43.998    30.911
decyear2     0.000     0.000    16.315    43.998    30.911
decyear3    16.315    16.315     0.000    44.969    33.171
decyear4    43.998    43.998    44.969     0.000    18.166
decyear5    30.911    30.911    33.171    18.166     0.000

请注意,这些都不一定比其他的更“正确”。这取决于您的定义和用例。但是decyear1decyear2可能是大多数人的想法,尽管(如上所述)它们可能由于非线性问题,在可能使用十进制年份的情况下使用。虽然所有版本都在百分之一内保持一致,但在很多情况下任何人都会这样做(例如我的情况,我需要它作为 World Magnetic Model 2020 的输入)。

问题:

希望现在很明显,精度优于一小时可能并不是真正必要的,但如果是,则可能需要针对时区和夏令时补偿您的数据。 编辑:如果您在整理出小时数后还需要另外 3 位精度,请不要忘记闰秒。

关于精度的说明:

上面给出的所有变体都表现良好且可逆 - 这意味着映射本身具有无限的精度。另一方面,准确性假设有一个特定的标准。例如,如果您在没有解释的情况下获得十进制年份,那么您所做的反向映射的准确性只能保证在半天左右的时间内。

答案 4 :(得分:0)

这比其他解决方案更简单:

import datetime
def year_fraction(date):
    start = datetime.date(date.year, 1, 1).toordinal()
    year_length = datetime.date(date.year+1, 1, 1).toordinal() - start
    return date.year + float(date.toordinal() - start) / year_length

>>> print year_fraction(datetime.datetime.today())
2016.32513661

请注意,这会根据当天的开始计算分数,因此12月31日将为0.997,而不是1.0。

答案 5 :(得分:0)

很惊讶没有人提到过这个......但是减去datetime.timedelta个对象产生的datetime.datetime个对象有一个除法方法。所以,你可以使用简单的函数

from datetime import datetime
def datetime2year(dt): 
    year_part = dt - datetime(year=dt.year, month=1, day=1)
    year_length = datetime(year=dt.year+1, month=1, day=1) - datetime(year=dt.year, month=1, day=1)
    return dt.year + year_part/year_length

其中除法位于datetime.timedelta个对象之间。

答案 6 :(得分:0)

可以使用Pandas的朱利安日期和以下公式计算小数日期。

如果您的pandas数据帧的索引是日期时间:

JD=dat.index.to_julian_date() #create julian date
L= JD+68569
N= 4*L/146097
L= L-(146097*N+3)/4
I= 4000*(L+1)/1461001
L= L-1461*I/4+31
J= 80*L/2447
K= L-2447*J/80
L= J/11
J= J+2-12*L
decimal_date= 100*(N-49)+I+L

decimal_date是一系列日期(与数据帧索引在同一个TZ中),形式类似于2007.123452。

Adapted from this post.

答案 7 :(得分:-1)

如果要包括分钟和秒 使用这个:

YearF=[(x.timetuple().tm_yday-1+x.timetuple().tm_hour/24+x.timetuple().tm_min/(60*24)+x.timetuple().tm_sec/(24*3600))/(365+((x.timetuple().tm_year//4)==(x.timetuple().tm_year/4)))+x.timetuple().tm_year for x in DateArray]