Дублирование в Excel представления дат в Юлии - PullRequest
4 голосов
/ 23 сентября 2019

В Джулии мне нужно преобразовать числа в DateTime таким же образом, как в Microsoft Excel.

В Excel сегодняшняя дата 23 сентября 2019 года представлена ​​43731, а 6 вечера сегодня днем ​​- 43731,75.Я могу игнорировать тот факт, что Excel неправильно полагает, что 1900 год - високосный год , поскольку все мои данные находятся в безопасности за пределами этой точки.Достаточно миллисекундной точности.

Кажется, что приведенный ниже код работает, но есть ли лучший способ?

function exceldatetodate(exceldate::Integer)
    Dates.Date(1899, 12, 30) + Dates.Day(exceldate)
end
function exceldatetodate(exceldate::Real)
    t,d = modf(exceldate)
    Dates.Date(1899, 12, 30) + Dates.Day(d) + Dates.Millisecond(floor(t * 86400000))
end

julia> exceldatetodate(43731)
2019-09-23

julia> exceldatetodate(43731.75)
2019-09-23T18:00:00

1 Ответ

1 голос
/ 23 сентября 2019

вы можете перегрузить методы convert и создать пользовательский тип, содержащий значение.

using Dates

struct ExcelDate{T<:Real}  
   val::T
end

function exceldatetodate(exceldate::Integer)
   Dates.DateTime(1899, 12, 30) + Dates.Day(exceldate)
end
function exceldatetodate(exceldate::Real)
   t,d = modf(exceldate)
   return Dates.DateTime(1899, 12, 30) + Dates.Day(d) + Dates.Millisecond((floor(t * 86400000)))
end

function exceldatetodate(exceldate::ExcelDate)
   exceldatetodate(exceldate.val)
end

function exceldatetodate(exceldate::ExcelDate)
   exceldatetodate(exceldate.val)
end

function toexceldate(date::Date)
   datetime = Dates.value(DateTime(date) - Dates.DateTime(1899, 12, 30))
   datetime = round(datetime/86400000,digits = 3)
   return ExcelDate(datetime)
end

function toexceldate(date::DateTime)
   datetime = Dates.value(date - Dates.DateTime(1899, 12, 30))
   datetime = round(datetime/86400000,digits = 3)
   return ExcelDate(datetime)
end


Base.convert(d::Type{Dates.DateTime},n::ExcelDate) = exceldatetodate(n) 
Base.convert(d::Type{Dates.Date},n::ExcelDate) = convert(Date,exceldatetodate(n))
Base.convert(d::Type{T},n::ExcelDate) where T<: Real = convert(d,n.val)
Base.convert(d::Type{ExcelDate},n::Dates.DateTime) = toexceldate(n) 
Base.convert(d::Type{ExcelDate},n::Dates.Date) = toexceldate(n) 

затем вы можете поиграть со значениями:

original_numbers = 40000.01:41000.01 #test numbers
excel_dates = convert.(ExcelDate,original_numbers)
dates = convert.(Date,excel_dates) #just days
datetimes = convert.(DateTime,excel_dates) #days and miliseconds
orig2 = convert.(ExcelDate,datetimes) #this preserves the original number
orig3 = convert.(ExcelDate,dates) #this does not preserve the original number

IsОчень важно отметить, что в Excel все числа обрабатываются как float64, а в Julia Dates - это совершенно другой тип.На мой взгляд, если вы хотите, чтобы определенный диапазон чисел вел себя как дата, лучше создать тип, отражающий это поведение.Одной из важных характеристик даты Excel является то, что вы можете оперировать датами, например числами, но результат этой операции не форматируется как дата.Это является результатом решения Excel об использовании Float64 для представления дат.Определенный тип имеет больше ограничений, чем число, и если вы хотите работать с датами в виде чисел, вы можете сначала преобразовать ExcelDate в числа, но имеет смысл просто использовать тип julia Date,у него есть лучшие и более эффективные методы для использования с датами.
Оффтопик, но даты - это нерешенная проблема программирования с разными стандартами для всех языков программирования.

...