Ever need to create a vector of dates using some sort of pattern? Perhaps these will be used as the edges argument for a histogram, with each a month.
What's the best way to create a datetime array where each element is the first of the month? And what does "best" even mean? - fewest keystrokes, fewest function calls, most readable, most flexible, most maintainable,etc. Suppose I want to produce something like this:
2018-Jan-01, 2018-Feb-01, 2018-Mar-01, ...
In fact, there are lots of suitable ways. Here are a few.
Just use datetime and specify the month vectors. This works
mydates1 = datetime(2018,1:12,1)
mydates1 = 1×12 datetime array Columns 1 through 5 01-Jan-2018 01-Feb-2018 01-Mar-2018 01-Apr-2018 01-May-2018 Columns 6 through 10 01-Jun-2018 01-Jul-2018 01-Aug-2018 01-Sep-2018 01-Oct-2018 Columns 11 through 12 01-Nov-2018 01-Dec-2018
While I'm at it, I can find the number of days in each month I have, using eomday.
numDays = eomday(year(mydates1),month(mydates1)); bar(mydates1,numDays)
If the months in question span years, you can do this fairly compactly using calmonths.
mydates2 = datetime(2017,1,1):calmonths(1):datetime(2018,7,1)
mydates2 = 1×19 datetime array Columns 1 through 5 01-Jan-2017 01-Feb-2017 01-Mar-2017 01-Apr-2017 01-May-2017 Columns 6 through 10 01-Jun-2017 01-Jul-2017 01-Aug-2017 01-Sep-2017 01-Oct-2017 Columns 11 through 15 01-Nov-2017 01-Dec-2017 01-Jan-2018 01-Feb-2018 01-Mar-2018 Columns 16 through 19 01-Apr-2018 01-May-2018 01-Jun-2018 01-Jul-2018
In addition, you can go beyond 12 months and datetime still works as expected, without needing calmonths.
mydates3 = datetime(2018,1:24,1);
mydates4 = datetime(2017,7:18,1)
mydates4 = 1×12 datetime array Columns 1 through 5 01-Jul-2017 01-Aug-2017 01-Sep-2017 01-Oct-2017 01-Nov-2017 Columns 6 through 10 01-Dec-2017 01-Jan-2018 01-Feb-2018 01-Mar-2018 01-Apr-2018 Columns 11 through 12 01-May-2018 01-Jun-2018
However keeping track of the relative month shifts to start has its own mental overhead for me.
Try this instead. Find the right start date, and then add on the correct number of calmonths from there.
mydates5 = datetime(2017,7,1) + calmonths(0:11)
mydates5 = 1×12 datetime array Columns 1 through 5 01-Jul-2017 01-Aug-2017 01-Sep-2017 01-Oct-2017 01-Nov-2017 Columns 6 through 10 01-Dec-2017 01-Jan-2018 01-Feb-2018 01-Mar-2018 01-Apr-2018 Columns 11 through 12 01-May-2018 01-Jun-2018
Do you have another way you like to produce lists of dates? What's your preference for these sorts of situations? Let me know here.
Get the MATLAB code
Published with MATLAB® R2018b
6 CommentsOldest to Newest
datenum(2018,1,50:6:121) or datetime(2018,1,50:121)Great. Now we hit a difference. Say I want the same date range, but every hour. an hour is 1/24th of a day, so in datenum I can do datenum(2018,1,50:(1/24):121). This works. However, this is NOT allowed in datetime, since all input values must be integers (which is not the case with datenum), so the datetime solution would be: datetime(2018,1,1,(50*24):1:(121*24),0,0). You can do similar tricks with minutes and seconds too.
Personally - I still use datenum for nearly everything time related. I have to deeply care about leap-seconds for my work, and although Matlab has had a valiant attempt at dealing with leap seconds, I don't have an easy way to know how up to date it's leap-second list is which ultimately makes it useless to me. Since I share code with others, I don't know what version of Matlab they are using, so it's difficult to code in a way to check how many leap seconds their version of Matlab is aware of. Mathworks - this needs to be addressed if you want use to trust Matlab time with leap second correction.
However, one big advantage for me of datetime is converting UTC strings to datetime (and then datenum). My UTC strings are often of the form 2019-050T00:00:00:00.000 (day-of year format) or 2019-02-19T00:00:00.000 (year-month-day format). Converting that to a datenum value was computationally expensive when I've thousands of them in a char array (n x 21 for the day-of-year former version). e.g.
%TIME = char array of n by 21 for UTC time in day of year (ddd) TIME(:,[5 9 12 15])=' '; % Remove the - T : from yyyy-dddTHH:MM:SS.sss TIME = str2num(TIME); T = datenum(TIME(:,1)-1,12,31+TIME(:,2),TIME(:,3),TIME(:,4),TIME(:,5)); % note that the 6th argument (Seconds) can be decimal to allow for milliseconds.However, with datetime it's much quicker! MUCH MUCH quicker. Weirdly converting a UTC time string to a datenum was my biggest time-sink in my code. Anyway - my quick way was:
T = datenum(datetime(TIME,'Format','uuuu-DDD''T''HH:mm:ss.SSS')); e.g. T = datenum(datetime('2019-034T01:23:45.678','Format','uuuu-DDD''T''HH:mm:ss.SSS'));If there's a quicker way to do this I'd love to know it!
My two time questions for the room: 1) Is there an easy (1-line) way to find out the last leap-second that Matlab version datetime is aware of? 2) Is there a way to find out what time-zone the machine is set to without using datetime?
For the latter, I can use datetime to figure this out, but if someone I've shared my code with has an older version of Matlab that does not have datetime yet (and there are a few who can't afford to upgrade), is there anyway I can figure out their timezone from Matlab using datenum or now or such?
import java.lang.String import java.util.* java.awt.* import java.util.Enumeration myTime = java.util.GregorianCalendar(); get(myTime.getTimeZone())Cheers, Paul MatlabInvesting.com
datetime(2018,1,50):hours(1):datetime(2018,1,122)Fractional days have no meaning in most time zones. Perhaps you don't care about time zones, but that's why the datetime function will not accept fractional days.
import java.lang.String import java.util.* java.awt.* import java.util.Enumeration myTime = java.util.GregorianCalendar(); get(myTime.getTimeZone());