如何将给定的序数(来自 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 年视为闰年。上面的代码为59
和60
返回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)转换为日期的主要内容,如果未能解决你的问题,请参考以下文章