如何将给定的序数(来自 Excel)转换为日期

Posted

技术标签:

【中文标题】如何将给定的序数(来自 Excel)转换为日期【英文标题】:How to convert a given ordinal number (from Excel) to a date 【发布时间】:2015-06-05 21:13:27 【问题描述】:

我有一个值 38142 我需要使用 python 将其转换为日期格式。 如果在 excel 中使用此数字并在那时右键单击并格式化单元格,则该值将转换为 04/06/2004,我需要使用 python 获得相同的结果。我怎样才能做到这一点

【问题讨论】:

这是一个奇怪的序数;你确定 04/06/2004 是正确的吗?如果值 38142 代表 ,那么这将是 1993/12/25 或 1993/10/27 的偏移量,具体取决于您对月份的解释。 Formula to convert date to number 建议它应该是自 1900/01/01 以来的天数,这就是 date.fromordinal() 所做的。但是那个数字少了一个数字。 我的文件的值我不知道它的序号或我的客户说它的序号并告诉我“如果你想找到实际日期,只需在 Excel 中为给定值格式化单元格那个时候我得到了这个价值”@MartijnPieters 是的,它确实是一个序数,但 Excel 中有一个错误,导致我对我最初的理论不以为然。 相关的老问题:How to convert a python datetime.datetime to excel serial date number 【参考方案1】:

Excel 中的偏移量是自 1900/01/01 以来的天数,1 是 1900 年 1 月的第一天,因此将天数作为时间增量添加到 1899/12/31:

from datetime import datetime, timedelta

def from_excel_ordinal(ordinal, _epoch0=datetime(1899, 12, 31)):
    if ordinal >= 60:
        ordinal -= 1  # Excel leap year bug, 1900 is not a leap year!
    return (_epoch0 + timedelta(days=ordinal)).replace(microsecond=0)

对于 1900/02/28 之后的任何日期,您必须将序数调整一天; Excel 从 Lotus 1-2-3 继承了 leap year bug,并将 1900 年视为闰年。上面的代码为5960 返回datetime(1900, 2, 28, 0, 0) 来纠正这个问题,在[59.0 - 61.0) 范围内的小数值都是00:00:00.0 到23:59:59.999999 之间的时间天。

上面还支持用分数表示时间的序列,但由于 Excel 不支持微秒,所以这些被删除了。

【讨论】:

太完美了,非常感谢@Martijn Pieters @Krish:该漏洞被 Joel Spolsky 推广:My First BillG Review 您确定纪元不是 1899 年 12 月 31 日吗? datetime(1899, 12, 31) + timedelta(ordinal - (ordinal > 59)) @J.F.Sebastian 我在这里坚持使用 Excel 文档;相对于 1900-01-01 减去 1 在这里没什么区别。 @FinanceGuyThatCantCode:_epoch 参数用于将值缓存为 局部变量,仅此而已。这有助于避免必须为每次调用创建它,或者必须查找全局(稍微慢一些)。【参考方案2】:
from datetime import datetime, timedelta

def from_excel_ordinal(ordinal, epoch=datetime(1900, 1, 1)):
    # Adapted from above, thanks to @Martijn Pieters 

    if ordinal > 59:
        ordinal -= 1  # Excel leap year bug, 1900 is not a leap year!
    inDays = int(ordinal)
    frac = ordinal - inDays
    inSecs = int(round(frac * 86400.0))

    return epoch + timedelta(days=inDays - 1, seconds=inSecs) # epoch is day 1

excelDT = 42548.75001           # Float representation of 27/06/2016  6:00:01 PM in Excel format  
pyDT = from_excel_ordinal(excelDT)

上述答案仅适用于日期值,但在这里我将上述解决方案扩展为包括时间并返回日期时间值。

【讨论】:

【参考方案3】:

我会推荐以下内容:

import pandas as pd

def convert_excel_time(excel_time):

    return pd.to_datetime('1900-01-01') + pd.to_timedelta(excel_time,'D')

或者

import datetime

def xldate_to_datetime(xldate):
    temp = datetime.datetime(1900, 1, 1)
    delta = datetime.timedelta(days=xldate)
    return temp+delta

取自 https://gist.github.com/oag335/9959241

【讨论】:

xldate_to_datetime(44000) 给出 2020-06-20 答案是 2020-06-18 @PoornaPrudhvi 是正确的;基准日期应为 1899-12-30。一天的偏移量,因为我们应该添加到 12 月 31 日,并且在接受的答案中提到闰年错误的另一天偏移量 b/c。【参考方案4】:

我在尝试执行上述相同操作时遇到了这个问题,但针对 df.xml 中的整个列。我做了这个功能,它为我做了:

import pandas as pd    
from datetime import datetime, timedelta
import copy as cp

def xlDateConv(df, *cols):      
    tempDt = []
    fin = cp.deepcopy(df)
    for col in [*cols]:
        for i in range(len(fin[col])):
            tempDate = datetime(1900, 1, 1)
            delta = timedelta(float(fin[col][i]))
            tempDt.append(pd.to_datetime(tempDate+delta))

        fin[col] = tempDt
        tempDt = []
    return fin

请注意,您需要输入每一列,引用(作为字符串),作为一个参数,这很可能会得到改进(例如,作为输入的列列表)。此外,它返回原始 df 的副本(不更改原始文件)。

顺便说一句,部分受此启发 (https://gist.github.com/oag335/9959241)。

【讨论】:

非常感谢【参考方案5】:

如果您正在使用 Pandas,这可能会很有用

    import xlrd
    import datetime as dt
    
    def from_excel_datetime(x):
        return dt.datetime(*xlrd.xldate_as_tuple(x, datemode=0))
    
    df['date'] = df.excel_date.map(from_excel_datetime)

如果日期似乎延迟了 4 年,也许您可​​以尝试使用 datemode 1。

:param datemode: 0:基于 1900,1:基于 1904。

【讨论】:

以上是关于如何将给定的序数(来自 Excel)转换为日期的主要内容,如果未能解决你的问题,请参考以下文章

如何实现秒数和日期之间的转换

在EXCEL中如何将日期格式转换,例如把2012/09/01变换成2012-09-01?

在excel中日期格式如何转换成常规格式?

在excel中如何将输入的数字自动转换为日期

如何将 Excel 中的日期转换为 ISO 8601 格式

excel 文本转日期