# Simple method for converting float array to DateTime (or similar)?

I have a time series with time recorded in a vector of floats in the format yyyy,mm,dd,HH,MM,SS.ssssss; i.e.:

`times = [2017 09 06 00 00 04.911360]`

This comes from the MATLAB `datevec()` function, which outputs a vector of floats with fractional seconds.

When I work with these data in MATLAB, I often store dates as a float using the `datenum()` function. In Julia, I want to convert these time vectors to something like `DateTime`, or split into `Date` and `Time` in order to preserve microsecond accuracy. I could simply convert the date vectors into a float like `datenum()`, but I like the idea of using `Date` and `Time` in order to easily (I hope) bin by time windows (e.g. separate a 24-hour time series into 10-minute bins).

Here’s what I have so far:

``````# sample data
times = [   2017 09 06 00 00 04.911360;
2017 09 06 04 15 55.193727;
2017 09 06 22 55 12.256655]

# attempt to initialize Date and Time arrays
sdate = Date(times[1,1]):Day(1):Date(times[3,1])
stime = Time(times[1,4]):Second(1):Time(times[3,4])

for i = 1:size(times, 1)
years   = times[i,1]
months  = times[i,2]
days    = times[i,3]
hours   = times[i,4]
minutes = times[i,5]
seconds = floor(times[i,6])
millis  = floor((times[i,6] - seconds)*1000)
micros  = floor(((times[i,6] - seconds)*1000 - millis)*1000)

sdate[i] = Date(years, months, days)
stime[i] = Time(hours, minutes, seconds, millis, micros)

end
``````

There are a number of problems here, including:

• I don’t know how to initialize `sdate` and `stime`, so both the initializations and the loop fail.
• Even if I get the initialization and assignment in the loop working, I have to tediously break out each argument of `Date` and `Time`.

It would be great to be able do something like:

``````sdate = Date.(times[:,1:3])
stime = Time.(times[:,4:6])
sdatetime = DateTime.(times)
``````

…but of course `.` operates elementwise, so that doesn’t work. Any ideas?

What is the source of the data?
Possibly a CSV file?
If we can assume that you start with a `String`
DateFormat does a lot of work for you.

The microseconds are a bit trickier though…

``````times=["2017 09 06 00 00 04","2017 09 06 04 15 55"]
dfmt = DateFormat("yyyy mm dd HH MM SS")
dt = DateTime.(times,dfmt)
sdate = Date.(dt)
stime = Time.(dt)
``````

Actually DateFormat does support digits for the second part (up to milliseconds), but your data has more precision than that. Truncating is always an option (albeit not a preferable one).
`Time` has support for nanoseconds, thus the parsing would need to be done separately.

EDIT: second part of the code has a possible solution. I am still assuming that you start with a `String` and not a matrix as in your example.

``````using Dates
dfmt = DateFormat("yyyy mm dd HH MM SS.s")
DateTime("2017 09 06 00 00 04.911",dfmt) #works for me
DateTime("2017 09 06 00 00 04.911360",dfmt) #fails

using Dates
times=["2017 09 06 00 00 04.911360","2017 09 06 04 15 55.193727"]
dfmt = DateFormat("yyyy mm dd HH MM SS")
times_wo_mus = map(x->x[1:findfirst(".",x)-1],times)
times_mus = map(x->x[findfirst(".",x)+1:end],times)

#Parse DateTime (second precision only)
dt = DateTime.(times_wo_mus,dfmt)
sdate = Date.(dt)
stime = Time.(dt)

mus = Microsecond.(parse.(Int,times_mus))
stime .= stime .+ mus
``````
1 Like

The package CompoundPeriods.jl is great for this:

``````using Dates, CompoundPeriods

T = [   2017 09 06 00 00 04.911360;
2017 09 06 04 15 55.193727;
2017 09 06 22 55 12.256655]

tcp = Year.(T[:,1]) + Month.(T[:,2]) + Day.(T[:,3]) + Hour.(T[:,4]) +
+ Minute.(T[:,5]) + Nanosecond.(round.(Int,1e9*T[:,6]))

t = canonical.(tcp)

3-element Vector{CompoundPeriod}:
2017 years, 9 months, 6 days, 4 seconds, 911 milliseconds, 360 microseconds
2017 years, 9 months, 6 days, 4 hours, 15 minutes, 55 seconds, 193 milliseconds, 727 microseconds
2017 years, 9 months, 6 days, 22 hours, 55 minutes, 12 seconds, 256 milliseconds, 655 microseconds
``````
1 Like

@bernhard, this is very helpful. The data comes from .mat files, which I read with MAT.jl. The files are generated daily, and analyses range from single-day events to several weeks, so speed will eventually be somewhat important. The file is imported as an array of `Float64`, but I can convert to `String` with `@sprintf()`. Here’s a version of your solution that does truncate to millisecond precision:

``````using Dates

times = [   2017 09 06 00 00 04.911360;
2017 09 06 04 15 55.193727;
2017 09 06 22 55 12.256655]

years   = times[:,1]
months  = times[:,2]
days    = times[:,3]
hours   = times[:,4]
minutes = times[:,5]
seconds = times[:,6]

dfmt = DateFormat("yyyy mm dd HH MM SS.s")

timestring = fill("",size(times, 1),1)

for i = 1:size(times, 1)

timestring[i] = @sprintf("%.0f %02.0f %02.0f %02.0f %02.0f %06.3f", years[i], months[i], days[i], hours[i], minutes[i], seconds[i])

end

dt = DateTime.(timestring,dfmt)
``````

@rafael.guerra I like this one! Nice that you can easily get millisecond precision with `DateTime.(t)`, or preserve microsecond precision with `Date.(t)` and `Time.(t)`. So the full solution becomes:

``````using Dates, CompoundPeriods

T = [   2017 09 06 00 00 04.911360;
2017 09 06 04 15 55.193727;
2017 09 06 22 55 12.256655]

tcp = Year.(T[:,1]) + Month.(T[:,2]) + Day.(T[:,3]) + Hour.(T[:,4]) +
+ Minute.(T[:,5]) + Nanosecond.(round.(Int,1e9*T[:,6]))

t = canonical.(tcp)

sdt = DateTime.(t)  # millisecond precision
sdate = Date.(t)    # date only
stime = Time.(t)    # time only with microsecond precision
``````

@ToddAnderson, if you like the functionality, consider starring the package repository in github, the author @JeffreySarnoff should appreciate.

2 Likes