Converting MATLAB and R date and time values

For some unknown reason, MATLAB codes its date/time values as the number of elapsed days starting from January 1 in the year 0000. R uses the equally arbitrary, but much more widespread POSIX/Unix epoch as a reference for time keeping, so that R’s POSIX time values are stored internally as the elapsed seconds since 00:00 January 1, 1970. Converting back and forth between the two values requires just a bit of doing.

Inside R, converting from the MATLAB ‘datenum‘ value into the R POSIXt standard is fairly straightforward: Subtract 719529 from your MATLAB datenum to get the number of days since 1970-1-1, and then multiply by 86,400 to convert days to seconds. The primary complication arises when your MATLAB time values are in a time zone other than UTC. The function matlab2POS shown below does that operation, and through some machinations spits out your time values in the user-specified time zone.

Converting from a R POSIXct or POSIXlt object back to a MATLAB datenum can be slightly more involved. MATLAB datenum values don’t carry any sort of time zone information with them (since they are simple numeric values), and it is left to the user to make time zone adjustments as necessary. On the other hand, R tries to adjust time values for your local time zone, so the POSIXct and POSIXlt classes often have a time zone value attached to them. If you live anywhere besides the GMT/UTC time zone, a simple as.numeric(x) on a POSIXt object will probably spit out a different number of seconds than you expect, as the operation returns the seconds value for the UTC time zone, not your local time zone.

There are two routes to take when converting POSIXt objects to MATLAB datenum values. You could either strip off the time zone information and return the MATLAB datenum value for the current time zone, or you could convert your POSIXt values to the GMT/UTC time zone, and then convert them to MATLAB datenum values. Two functions are provided below to do either operation.

  1. The function POSIXt2matlab strips off the time zone information and returns the MATLAB datenum value for the current time zone. This might be useful if you want to refer to the local time in your MATLAB script. This function will also take a numeric object that contains the raw ‘seconds-since-1970-1-1’ and convert it to MATLAB format.
  2. The function POSIXt2matlabUTC first converts all POSIXct and POSIXlt objects to the UTC time zone, and then converts the values to MATLAB datenums.

If the MATLAB datenum ends in some repeating value (XX.666667 or XX.33333 for example), you will find slight differences in reported time for MATLAB vs. R. MATLAB will round off the repeating values and display the “correct” time, while using the <code>matlab2POS</code> function on such a value will have R dropping a second from the reported time it prints at the command line.

Of course, the safest route would be to convert your time stamps into text strings that can be read by either program (using datestr in MATLAB for datenum values, and strptime in R for POSIXt values) and that you can check for any time zone weirdness. But if you’re trying to create a simple numeric matrix to supply to MATLAB to process, you obviously can’t mix the text date/time strings into your matrix.

Link to the R script on GitHub: matlab_time.R

# Filename: matlab_time.R
# Convert between MATLAB datenum values and R POSIXt time values.
# 
# Author: Luke Miller   Feb 20, 2011
###############################################################################
 
#Convert a numeric  MATLAB datenum (days since 0000-1-1 00:00) to seconds in 
#the Unix epoch (seconds since 1970-1-1 00:00). Specify a time zone if the 
#input datenum is anything other than the GMT/UTC time zone. 
matlab2POS = function(x, timez = "UTC") {
	days = x - 719529 	# 719529 = days from 1-1-0000 to 1-1-1970
	secs = days * 86400 # 86400 seconds in a day
	# This next string of functions is a complete disaster, but it works.
	# It tries to outsmart R by converting the secs value to a POSIXct value
	# in the UTC time zone, then converts that to a time/date string that 
	# should lose the time zone, and then it performs a second as.POSIXct()
	# conversion on the time/date string to get a POSIXct value in the user's 
	# specified timezone. Time zones are a goddamned nightmare.
	return(as.POSIXct(strftime(as.POSIXct(secs, origin = '1970-1-1', 
			tz = 'UTC'), format = '%Y-%m-%d %H:%M', 
			tz = 'UTC', usetz = FALSE), tz = timez))
}
 
 
###############################################################################
#Convert POSIXct, POSIXlt or 'seconds since 1970-1-1' to MATLAB datenum value.
#The conversion drops any time zone associated with the POSIXt value. It is the
#user's responsibility to keep track of time zones in MATLAB datenums.
#The constant 719529 in the function is the days from 0000-1-1 to 1970-1-1.
POSIXt2matlab = function(x) {
	if (class(x)[1] == "POSIXlt"){
		days = as.numeric(as.Date(x)) #extract days since 1970-1-1
		frac.day = (((x$hour)*3600) + ((x$min)*60) + x$sec)/86400
		datenum = 719529 + days + frac.day 
		datenum = 719529 + days + frac.day		
	} else if (class(x)[1] == "POSIXct"){
		x = as.POSIXlt(x) #convert to POSIXlt class
		days = as.numeric(as.Date(x)) #extract days since 1970-1-1
		frac.day = (((x$hour)*3600) + ((x$min)*60) + x$sec)/86400
		datenum = 719529 + days + frac.day
	} else if (class(x)[1] == "numeric"){
		days = x / 86400 #convert seconds to days
		datenum = days + 719529 
	} else {
		stop("Input cannot be coerced to POSIXlt or numeric value")
	}
	return(datenum)
}
#The output is a numeric vector of 'days since 0000-1-1 00:00'. 
 
 
###############################################################################
#Convert POSIXct or POSIXlt objects to MATLAB datenum, in UTC time zone. 
#All time stamps with non-GMT/UTC time zones will be first converted to the 
#GMT/UTC time zone, then converted to MATLAB datenum value. 
POSIXt2matlabUTC = function(x) {
	if (class(x)[1] == "POSIXct") {
		x = as.POSIXlt(x, tz = "UTC") #convert to UTC time zone
		days = as.numeric(x) / 86400 #convert to days
		datenum = days + 719529 #convert to MATLAB datenum
	} else if (class(x)[1] == "POSIXlt") {
		x = as.POSIXlt(x, tz = "UTC") #convert to UTC time zone
		days = as.numeric(x) / 86400  #convert to days
		datenum = days + 719529 #convert to MATLAB datenum
	} else {stop("POSIXct or POSIXlt object required for input")}
	return(datenum)
}
#The output is a numeric vector of 'days since 0000-1-1 00:00', adjusted to UTC

Created by Pretty R at inside-R.org

 

Be aware the the default print for R will probably truncate the displayed values for your MATLAB datenum produced by the functions above. If you want to show the full value of the datenum, use print(x, digits = 15) to show the extra digits.

If you’re wondering, it appears that neither MATLAB’s datenum nor R’s POSIXt objects account for leap seconds. They do both account for leap years and skip over the 400-year leap year (i.e. years not evenly divisible by 400 are not leap years).