# HG changeset patch # User Rik # Date 1320878949 28800 # Node ID 40e32fe44aaae3c4f11f60fbae9da8c13c3234b1 # Parent 9fc597693b0b23015cf2f4adda6b844a40d8450c Ugrade time functions to accept millisecond format string FFF (Bug #34586) * datestr.m: Add millisecond FFF format. Change numerical formats 21,22,29 to match Matlab. Remove unused persistent variables. Vectorize some for loops. Use strrep rather than regexp where possible for speed. * datevec.m: Add millisecond FFF format. Use strrep rather than regexp where possible for speed. * datenum.m: Update docstring. Use modern coding style. Only calculate second output argument if requested. Allow cellstr inputs. diff --git a/NEWS b/NEWS --- a/NEWS +++ b/NEWS @@ -65,6 +65,12 @@ Default options are "Qt Qbb Qc Qx" for 4D and higher voronoi : No default arguments + ** Date/Time functions updated. + Millisecond support with FFF format string now supported. + + datestr : Numerical formats 21, 22, 29 changed to match Matlab. + Now accepts cellstr inputs. + ** Octave warning IDs updated "empty-list-elements" : removed "fortran-indexing" : removed diff --git a/scripts/time/datenum.m b/scripts/time/datenum.m --- a/scripts/time/datenum.m +++ b/scripts/time/datenum.m @@ -17,16 +17,25 @@ ## . ## -*- texinfo -*- -## @deftypefn {Function File} {} datenum (@var{year}, @var{month}, @var{day}) -## @deftypefnx {Function File} {} datenum (@var{year}, @var{month}, @var{day}, @var{hour}) -## @deftypefnx {Function File} {} datenum (@var{year}, @var{month}, @var{day}, @var{hour}, @var{minute}) -## @deftypefnx {Function File} {} datenum (@var{year}, @var{month}, @var{day}, @var{hour}, @var{minute}, @var{second}) -## @deftypefnx {Function File} {} datenum ("date") -## @deftypefnx {Function File} {} datenum ("date", @var{p}) -## Return the specified local time as a day number, with Jan 1, 0000 -## being day 1. By this reckoning, Jan 1, 1970 is day number 719529. -## The fractional portion, @var{p}, corresponds to the portion of the -## specified day. +## @deftypefn {Function File} {@var{days} =} datenum (@var{datevec}) +## @deftypefnx {Function File} {@var{days} =} datenum (@var{year}, @var{month}, @var{day}) +## @deftypefnx {Function File} {@var{days} =} datenum (@var{year}, @var{month}, @var{day}, @var{hour}) +## @deftypefnx {Function File} {@var{days} =} datenum (@var{year}, @var{month}, @var{day}, @var{hour}, @var{minute}) +## @deftypefnx {Function File} {@var{days} =} datenum (@var{year}, @var{month}, @var{day}, @var{hour}, @var{minute}, @var{second}) +## @deftypefnx {Function File} {@var{days} =} datenum ("datestr") +## @deftypefnx {Function File} {@var{days} =} datenum ("datestr", @var{p}) +## @deftypefnx {Function File} {[@var{days}, @var{secs}] =} datenum (@dots{}) +## Return the date/time input as a serial day number, with Jan 1, 0000 +## being day 1. The fractional portion of @var{days} corresponds to the time +## on the given day. The input may be a date vector (see @code{datevec}), +## datestr (see @code{datestr}), or directly specified as input. +## +## When processing datestrings, @var{p} is the year at the start of the century +## to which two-digit years will be referenced. If not specified, it defaults +## to the current year minus 50. +## +## The optional output @var{secs} holds the time on the specified day with greater +## precision than @var{days}. ## ## Notes: ## @@ -52,48 +61,43 @@ ## ## @strong{Caution:} this function does not attempt to handle Julian ## calendars so dates before Octave 15, 1582 are wrong by as much -## as eleven days. Also be aware that only Roman Catholic countries +## as eleven days. Also, be aware that only Roman Catholic countries ## adopted the calendar in 1582. It took until 1924 for it to be ## adopted everywhere. See the Wikipedia entry on the Gregorian ## calendar for more details. ## ## @strong{Warning:} leap seconds are ignored. A table of leap seconds ## is available on the Wikipedia entry for leap seconds. -## @seealso{date, clock, now, datestr, datevec, calendar, weekday} +## @seealso{datestr, datevec, date, clock, now, calendar, weekday} ## @end deftypefn ## Algorithm: Peter Baum (http://vsg.cape.com/~pbaum/date/date0.htm) ## Author: pkienzle -function [days, secs] = datenum (year, month, day, hour, minute, second) +function [days, secs] = datenum (year, month = [], day = [], hour = 0, minute = 0, second = 0) ## Days until start of month assuming year starts March 1. persistent monthstart = [306; 337; 0; 31; 61; 92; 122; 153; 184; 214; 245; 275]; - if (nargin == 0 || (nargin > 2 && ischar (year)) || nargin > 6) + if (nargin == 0 || nargin > 6 || + (nargin > 2 && (ischar (year) || iscellstr (year)))) print_usage (); endif - if (ischar (year)) - if (nargin < 2) - month = []; - endif + + if (ischar (year) || iscellstr (year)) [year, month, day, hour, minute, second] = datevec (year, month); else - if (nargin < 6) second = 0; endif - if (nargin < 5) minute = 0; endif - if (nargin < 4) hour = 0; endif if (nargin == 1) nc = columns (year); if (nc > 6 || nc < 3) error ("datenum: expected date vector containing [YEAR, MONTH, DAY, HOUR, MINUTE, SECOND]"); endif - second = minute = hour = 0; if (nc >= 6) second = year(:,6); endif if (nc >= 5) minute = year(:,5); endif - if (nc >= 4) hour = year(:,4); endif - day = year(:,3); + if (nc >= 4) hour = year(:,4); endif + day = year(:,3); month = year(:,2); - year = year(:,1); + year = year(:,1); endif endif @@ -120,30 +124,32 @@ day += 365*year + floor (year/4) - floor (year/100) + floor (year/400); ## Add fraction representing current second of the day. - days = day + (hour+(minute+second/60)/60)/24; + days = day + (hour + (minute + second/60)/60)/24; ## Output seconds if asked so that etime can be more accurate - secs = 86400*day + hour*3600 + minute*60 + second; + if (isargout (2)) + secs = day*86400 + hour*3600 + minute*60 + second; + endif endfunction + %!shared part %! part = 0.514623842592593; -%!assert(datenum(2001,5,19), 730990) -%!assert(datenum([1417,6,12]), 517712) -%!assert(datenum([2001,5,19;1417,6,12]), [730990;517712]) -%!assert(datenum(2001,5,19,12,21,3.5), 730990+part, eps) -%!assert(datenum([1417,6,12,12,21,3.5]), 517712+part, eps) +%!assert (datenum (2001,5,19), 730990) +%!assert (datenum ([1417,6,12]), 517712) +%!assert (datenum ([2001,5,19;1417,6,12]), [730990;517712]) +%!assert (datenum (2001,5,19,12,21,3.5), 730990+part, eps) +%!assert (datenum ([1417,6,12,12,21,3.5]), 517712+part, eps) ## Test vector inputs %!test %! t = [2001,5,19,12,21,3.5; 1417,6,12,12,21,3.5]; %! n = [730990; 517712] + part; -%! assert(datenum(t), n, 2*eps); -## Make sure that the vectors can have either orientation -%!test -%! t = [2001,5,19,12,21,3.5; 1417,6,12,12,21,3.5]'; -%! n = [730990 517712] + part; -%! assert(datenum(t(1,:), t(2,:), t(3,:), t(4,:), t(5,:), t(6,:)), n, 2*eps); +%! assert (datenum (t), n, 2*eps); +%! ## Check that vectors can have either orientation +%! t = t'; +%! n = n'; +%! assert (datenum (t(1,:), t(2,:), t(3,:), t(4,:), t(5,:), t(6,:)), n, 2*eps); ## Test mixed vectors and scalars %!assert (datenum([2008;2009], 1, 1), [datenum(2008, 1, 1);datenum(2009, 1, 1)]); @@ -159,3 +165,14 @@ %!assert (datenum([2008 2009], [1 2], 1), [datenum(2008, 1, 1) datenum(2009, 2, 1)]); %!assert (datenum([2008 2009], 1, [1 2]), [datenum(2008, 1, 1) datenum(2009, 1, 2)]); %!assert (datenum(2008, [1 2], [1 2]), [datenum(2008, 1, 1) datenum(2008, 2, 2)]); +## Test string and cellstr inputs +%!assert (datenum ("5/19/2001"), 730990) +%!assert (datenum ({"5/19/2001"}), 730990) +%!assert (datenum (char ("5/19/2001", "6/6/1944")), [730990; 710189]) +%!assert (datenum ({"5/19/2001", "6/6/1944"}), [730990; 710189]) + +%% Test input validation +%!error datenum () +%!error datenum (1,2,3,4,5,6,7) +%!error datenum ([1, 2]) +%!error datenum ([1,2,3,4,5,6,7]) diff --git a/scripts/time/datestr.m b/scripts/time/datestr.m --- a/scripts/time/datestr.m +++ b/scripts/time/datestr.m @@ -89,6 +89,8 @@ ## @item @tab and not padded with zeros otherwise @tab 9:00 AM ## @item MM @tab Minute of hour (padded with zeros) @tab 10:05 ## @item SS @tab Second of minute (padded with zeros) @tab 10:05:03 +## @item FFF @tab Milliseconds of second (padded with zeros) @tab 10:05:03.012 +## @item AM @tab Use 12-hour time format @tab 11:30 AM ## @item PM @tab Use 12-hour time format @tab 11:30 PM ## @end multitable ## @@ -98,10 +100,10 @@ ## ## If @var{p} is nor specified, it defaults to the current year minus 50. ## -## If a matrix or cell array of dates is given, a vector of date strings is -## returned. +## If a matrix or cell array of dates is given, a column vector of date strings +## is returned. ## -## @seealso{datenum, datevec, date, clock, now, datetick} +## @seealso{datenum, datevec, date, clock, now} ## @end deftypefn ## FIXME: parse arbitrary code strings. @@ -126,22 +128,21 @@ ## Created: 10 October 2001 (CVS) ## Adapted-By: William Poetra Yoga Hadisoeseno -function retval = datestr (date, f, p) +function retval = datestr (date, f = [], p = []) - persistent dateform names_mmmm names_mmm names_m names_dddd names_ddd names_d; + persistent dateform names_mmmm names_m names_d; if (isempty (dateform)) - dateform = cell (32, 1); - dateform{1} = "dd-mmm-yyyy HH:MM:SS"; - dateform{2} = "dd-mmm-yyyy"; - dateform{3} = "mm/dd/yy"; - dateform{4} = "mmm"; - dateform{5} = "m"; - dateform{6} = "mm"; - dateform{7} = "mm/dd"; - dateform{8} = "dd"; - dateform{9} = "ddd"; + dateform{1} = "dd-mmm-yyyy HH:MM:SS"; + dateform{2} = "dd-mmm-yyyy"; + dateform{3} = "mm/dd/yy"; + dateform{4} = "mmm"; + dateform{5} = "m"; + dateform{6} = "mm"; + dateform{7} = "mm/dd"; + dateform{8} = "dd"; + dateform{9} = "ddd"; dateform{10} = "d"; dateform{11} = "yyyy"; dateform{12} = "yy"; @@ -154,68 +155,50 @@ dateform{19} = "QQ"; dateform{20} = "dd/mm"; dateform{21} = "dd/mm/yy"; - dateform{22} = "mmm.dd.yyyy HH:MM:SS"; - dateform{23} = "mmm.dd.yyyy"; + dateform{22} = "mmm.dd,yyyy HH:MM:SS"; + dateform{23} = "mmm.dd,yyyy"; dateform{24} = "mm/dd/yyyy"; dateform{25} = "dd/mm/yyyy"; dateform{26} = "yy/mm/dd"; dateform{27} = "yyyy/mm/dd"; dateform{28} = "QQ-YYYY"; dateform{29} = "mmmyyyy"; - dateform{30} = "yyyymmdd"; + dateform{30} = "yyyy-mm-dd"; dateform{31} = "yyyymmddTHHMMSS"; dateform{32} = "yyyy-mm-dd HH:MM:SS"; - names_m = {"J"; "F"; "M"; "A"; "M"; "J"; "J"; "A"; "S"; "O"; "N"; "D"}; - - names_d = {"S"; "M"; "T"; "W"; "T"; "F"; "S"}; - + names_m = {"J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"}; + names_d = {"S", "M", "T", "W", "T", "F", "S"}; endif if (nargin < 1 || nargin > 3) print_usage (); endif - if (nargin < 2) - f = []; - endif - if (nargin < 3) - p = []; - endif - - if (ischar (date)) - t = date; - date = cell (1); - date{1} = t; - endif - - ## Guess, so we might be wrong. - if (iscell (date) || columns (date) != 6) + ## Guess input type. We might be wrong. + if (ischar (date) || iscellstr (date) || columns (date) != 6) v = datevec (date, p); else v = []; if (columns (date) == 6) ## Make sure that the input really is a datevec. maxdatevec = [Inf, 12, 31, 23, 59, 60]; - for i = 1:numel (maxdatevec) - if (any (date(:,i) > maxdatevec(i)) - || (i != 6 && any (floor (date(:,i)) != date(:,i)))) - v = datevec (date, p); - break; - endif - endfor + if (any (max (date, 1) > maxdatevec) || + any (date(:,1:5) != floor (date(:,1:5)))) + v = datevec (date, p); + endif endif if (isempty (v)) v = date; endif endif - for i = 1:(rows (v)) + retval = []; + for i = 1 : rows (v) - if (isempty (f) || f == -1) + if (isempty (f)) if (v(i,4:6) == 0) f = 1; - ## elseif (v(i,1:3) == [0, 1, 1]) elseif (v(i,1:3) == [-1, 12, 31]) f = 16; else @@ -230,7 +213,8 @@ endif df_orig = df; - df = regexprep (df, '[AP]M', "%p"); + df = strrep (df, 'AM', "%p"); + df = strrep (df, 'PM', "%p"); if (strcmp (df, df_orig)) ## PM not set. df = strrep (df, "HH", "%H"); @@ -266,9 +250,11 @@ df = strrep (df, "MM", "%M"); - df = strrep (df, "SS", "%S"); + df = regexprep (df, '[Ss][Ss]', "%S"); - df = regexprep (df, '[Qq][Qq]', sprintf ("Q%d", fix ((v(i,2) + 2) / 3))); + df = strrep (df, "FFF", sprintf ("%03d", 1000 * (v(i,6) - fix (v(i,6))))); + + df = strrep (df, 'QQ', sprintf ("Q%d", fix ((v(i,2) + 2) / 3))); vi = v(i,:); tm.year = vi(1) - 1900; @@ -278,7 +264,7 @@ tm.min = vi(5); sec = vi(6); tm.sec = fix (sec); - tm.usec = fix (rem (sec, 1) * 1e6); + tm.usec = fix ((sec - tm.sec) * 1e6); tm.wday = wday - 1; ## FIXME -- Do we need YDAY and DST? How should they be computed? ## We don't want to use "localtime (mktime (tm))" because that @@ -288,61 +274,64 @@ str = strftime (df, tm); - if (i == 1) - retval = str; - else - retval = [retval; str]; - endif + retval = [retval; str]; endfor endfunction -# simple tests + +## demos +%!demo +%! ## Current date and time in default format +%! datestr (now ()) +%!demo +%! ## Current date (integer portion of datenum) +%! datestr (fix (now ())) +%!demo +%! ## Current time (fractional portion of datenum) +%! datestr (rem (now (), 1)) + %!shared testtime %! testtime = [2005.0000, 12.0000, 18.0000, 2.0000, 33.0000, 17.3822]; -%!assert(datestr(testtime,0),"18-Dec-2005 02:33:17"); -%!assert(datestr(testtime,1),"18-Dec-2005"); -%!assert(datestr(testtime,2),"12/18/05"); -%!assert(datestr(testtime,3),"Dec"); -%!assert(datestr(testtime,4),"D"); -%!assert(datestr(testtime,5),"12"); -%!assert(datestr(testtime,6),"12/18"); -%!assert(datestr(testtime,7),"18"); -%!assert(datestr(testtime,8),"Sun"); -%!assert(datestr(testtime,9),"S"); -%!assert(datestr(testtime,10),"2005"); -%!assert(datestr(testtime,11),"05"); -%!assert(datestr(testtime,12),"Dec05"); -%!assert(datestr(testtime,13),"02:33:17"); -%!assert(datestr(testtime,14)," 2:33:17 AM"); -%!assert(datestr(testtime,15),"02:33"); -%!assert(datestr(testtime,16)," 2:33 AM"); -%!assert(datestr(testtime,17),"Q4-05"); -%!assert(datestr(testtime,18),"Q4"); -%!assert(datestr(testtime,19),"18/12"); -%!assert(datestr(testtime,20),"18/12/05"); -%!assert(datestr(testtime,21),"Dec.18.2005 02:33:17"); -%!assert(datestr(testtime,22),"Dec.18.2005"); -%!assert(datestr(testtime,23),"12/18/2005"); -%!assert(datestr(testtime,24),"18/12/2005"); -%!assert(datestr(testtime,25),"05/12/18"); -%!assert(datestr(testtime,26),"2005/12/18"); -%!assert(datestr(testtime,27),"Q4-2005"); -%!assert(datestr(testtime,28),"Dec2005"); -%!assert(datestr(testtime,29),"20051218"); -%!assert(datestr(testtime,30),"20051218T023317"); -%!assert(datestr(testtime,31),"2005-12-18 02:33:17"); -%!assert(datestr(testtime+[0 0 3 0 0 0],"dddd"),"Wednesday") -## avoid the bug where someone happens to give a vector of datenums that -## happens to be 6 wide -%!assert(datestr(733452.933:733457.933), ["14-Feb-2008 22:23:31";"15-Feb-2008 22:23:31";"16-Feb-2008 22:23:31";"17-Feb-2008 22:23:31";"18-Feb-2008 22:23:31";"19-Feb-2008 22:23:31"]) -%!assert (datestr ([1944, 6, 6, 6, 30, 0], 0), "06-Jun-1944 06:30:00"); +%!assert (datestr (testtime,0), "18-Dec-2005 02:33:17") +%!assert (datestr (testtime,1), "18-Dec-2005") +%!assert (datestr (testtime,2), "12/18/05") +%!assert (datestr (testtime,3), "Dec") +%!assert (datestr (testtime,4), "D") +%!assert (datestr (testtime,5), "12") +%!assert (datestr (testtime,6), "12/18") +%!assert (datestr (testtime,7), "18") +%!assert (datestr (testtime,8), "Sun") +%!assert (datestr (testtime,9), "S") +%!assert (datestr (testtime,10), "2005") +%!assert (datestr (testtime,11), "05") +%!assert (datestr (testtime,12), "Dec05") +%!assert (datestr (testtime,13), "02:33:17") +%!assert (datestr (testtime,14), " 2:33:17 AM") +%!assert (datestr (testtime,15), "02:33") +%!assert (datestr (testtime,16), " 2:33 AM") +%!assert (datestr (testtime,17), "Q4-05") +%!assert (datestr (testtime,18), "Q4") +%!assert (datestr (testtime,19), "18/12") +%!assert (datestr (testtime,20), "18/12/05") +%!assert (datestr (testtime,21), "Dec.18,2005 02:33:17") +%!assert (datestr (testtime,22), "Dec.18,2005") +%!assert (datestr (testtime,23), "12/18/2005") +%!assert (datestr (testtime,24), "18/12/2005") +%!assert (datestr (testtime,25), "05/12/18") +%!assert (datestr (testtime,26), "2005/12/18") +%!assert (datestr (testtime,27), "Q4-2005") +%!assert (datestr (testtime,28), "Dec2005") +%!assert (datestr (testtime,29), "2005-12-18") +%!assert (datestr (testtime,30), "20051218T023317") +%!assert (datestr (testtime,31), "2005-12-18 02:33:17") +%!assert (datestr (testtime+[0 0 3 0 0 0], "dddd"), "Wednesday") +## Test possible bug where input is a vector of datenums that is exactly 6 wide +%!assert (datestr ([1944, 6, 6, 6, 30, 0], 0), "06-Jun-1944 06:30:00") +## Test fractional millisecond time extension +%!assert (datestr (testtime, "HH:MM:SS:FFF"), "02:33:17:382") -# demos -%!demo -%! datestr (now ()) -%!demo -%! datestr (rem (now (), 1)) -%!demo -%! datestr (floor (now ())) +%% Test input validation +%!error datestr () +%!error datestr (1, 2, 3, 4) diff --git a/scripts/time/datevec.m b/scripts/time/datevec.m --- a/scripts/time/datevec.m +++ b/scripts/time/datevec.m @@ -31,9 +31,9 @@ ## @var{f} is the format string used to interpret date strings ## (see @code{datestr}). ## -## @var{p} is the year at the start of the century in which two-digit years -## are to be interpreted in. If not specified, it defaults to the current -## year minus 50. +## @var{p} is the year at the start of the century to which two-digit years +## will be referenced. If not specified, it defaults to the current year +## minus 50. ## @seealso{datenum, datestr, date, clock, now} ## @end deftypefn @@ -46,13 +46,15 @@ ## The function __date_str2vec__ is based on datesplit by Bill Denney. -function [y, m, d, h, mi, s] = datevec (date, varargin) +function [y, m, d, h, mi, s] = datevec (date, f = [], p = []) persistent std_formats nfmt; if (isempty (std_formats)) std_formats = cell (); nfmt = 0; + ## These formats are specified by Matlab to be parsed + ## The '# XX' refers to the datestr numerical format code std_formats{++nfmt} = "dd-mmm-yyyy HH:MM:SS"; # 0 std_formats{++nfmt} = "dd-mmm-yyyy"; # 1 std_formats{++nfmt} = "mm/dd/yy"; # 2 @@ -62,6 +64,8 @@ std_formats{++nfmt} = "HH:MM"; # 15 std_formats{++nfmt} = "HH:MM PM"; # 16 std_formats{++nfmt} = "mm/dd/yyyy"; # 23 + + ## These are other formats that Octave tries std_formats{++nfmt} = "mmm-dd-yyyy HH:MM:SS"; std_formats{++nfmt} = "mmm-dd-yyyy"; std_formats{++nfmt} = "dd mmm yyyy HH:MM:SS"; @@ -72,8 +76,6 @@ std_formats{++nfmt} = "dd.mmm.yyyy"; std_formats{++nfmt} = "mmm.dd.yyyy HH:MM:SS"; std_formats{++nfmt} = "mmm.dd.yyyy"; - - ## Custom formats. std_formats{++nfmt} = "mmmyy"; # 12 std_formats{++nfmt} = "mm/dd/yyyy HH:MM"; endif @@ -82,22 +84,14 @@ print_usage (); endif - switch (nargin) - case 1 + if (ischar (date)) + date = cellstr (date); + endif + + if (isnumeric (f)) + p = f; f = []; - p = []; - case 2 - if (ischar (varargin{1})) - f = varargin{1}; - p = []; - else - f = []; - p = varargin{1}; - endif - case 3 - f = varargin{1}; - p = varargin{2}; - endswitch + endif if (isempty (f)) f = -1; @@ -107,10 +101,6 @@ p = (localtime (time ())).year + 1900 - 50; endif - if (ischar (date)) - date = cellstr (date); - endif - if (iscell (date)) nd = numel (date); @@ -132,7 +122,7 @@ endif endfor else - ## Decipher the format string just once for sake of speed. + ## Decipher the format string just once for speed. [f, rY, ry, fy, fm, fd, fh, fmi, fs] = __date_vfmt2sfmt__ (f); for k = 1:nd [found y(k) m(k) d(k) h(k) mi(k) s(k)] = __date_str2vec__ (date{k}, p, f, rY, ry, fy, fm, fd, fh, fmi, fs); @@ -142,7 +132,7 @@ endfor endif - else + else # datenum input date = date(:); @@ -187,38 +177,7 @@ function [f, rY, ry, fy, fm, fd, fh, fmi, fs] = __date_vfmt2sfmt__ (f) ## Play safe with percent signs. - f = strrep(f, "%", "%%"); - - ## Dates to lowercase (note: we cannot convert MM to mm). - f = strrep (f, "YYYY", "yyyy"); - f = strrep (f, "YY", "yy"); - f = strrep (f, "QQ", "qq"); - f = strrep (f, "MMMM", "mmmm"); - f = strrep (f, "MMM", "mmm"); - f = strrep (f, "DDDD", "dddd"); - f = strrep (f, "DDD", "ddd"); - f = strrep (f, "DD", "dd"); - ## Times to uppercase (also cannot convert mm to MM). - f = strrep (f, "hh", "HH"); - f = strrep (f, "ss", "SS"); - f = strrep (f, "pm", "PM"); - f = strrep (f, "am", "AM"); - - ## Right now, the format string may only contain these tokens: - ## - ## yyyy 4 digit year - ## yy 2 digit year - ## mmmm month name, full - ## mmm month name, abbreviated - ## mm month number - ## dddd weekday name, full - ## ddd weekday name, abbreviated - ## dd date - ## HH hour - ## MM minutes - ## SS seconds - ## PM AM/PM - ## AM AM/PM + f = strrep (f, "%", "%%"); if (! isempty (strfind (f, "PM")) || ! isempty (strfind (f, "AM"))) ampm = true; @@ -227,14 +186,14 @@ endif ## Date part. - f = strrep (f, "yyyy", "%Y"); - f = strrep (f, "yy", "%y"); + f = regexprep (f, '[Yy][Yy][Yy][Yy]', "%Y"); + f = regexprep (f, '[Yy][Yy]', "%y"); f = strrep (f, "mmmm", "%B"); f = strrep (f, "mmm", "%b"); f = strrep (f, "mm", "%m"); - f = strrep (f, "dddd", "%A"); - f = strrep (f, "ddd", "%a"); - f = strrep (f, "dd", "%d"); + f = regexprep (f, '[Dd][Dd][Dd][Dd]', "%A"); + f = regexprep (f, '[Dd][Dd][Dd]', "%a"); + f = regexprep (f, '[Dd][Dd]', "%d"); ## Time part. if (ampm) @@ -245,7 +204,7 @@ f = strrep (f, "HH", "%H"); endif f = strrep (f, "MM", "%M"); - f = strrep (f, "SS", "%S"); + f = regexprep (f, '[Ss][Ss]', "%S"); rY = rindex (f, "%Y"); ry = rindex (f, "%y"); @@ -263,12 +222,25 @@ function [found, y, m, d, h, mi, s] = __date_str2vec__ (ds, p, f, rY, ry, fy, fm, fd, fh, fmi, fs) - [tm, nc] = strptime (ds, f); - - if (nc == size (ds, 2) + 1) + idx = strfind (f, "FFF"); + if (! isempty (idx)) + ## Kludge to handle FFF millisecond format since strptime does not + f(idx:idx+2) = []; + [~, nc] = strptime (ds, f); + if (nc > 0) + msec = ds(nc:min(nc+2, end)); + f = [f(1:idx-1) msec f(idx:end)]; + [tm, nc] = strptime (ds, f); + tm.usec = 1000 * str2double (msec); + endif + else + [tm, nc] = strptime (ds, f); + endif + + if (nc == columns (ds) + 1) + found = true; y = tm.year + 1900; m = tm.mon + 1; d = tm.mday; h = tm.hour; mi = tm.min; s = tm.sec + tm.usec / 1e6; - found = true; if (rY < ry) if (y > 1999) y -= 2000; @@ -289,7 +261,6 @@ tmp = localtime (time ()); y = tmp.year + 1900; elseif (fy && fm && ! fd) - tmp = localtime (time ()); d = 1; endif if (! fh && ! fmi && ! fs) @@ -304,23 +275,30 @@ endfunction + +%!demo +%! ## Current date and time +%! datevec (now ()) + %!shared nowvec %! nowvec = datevec (now); # Some tests could fail around midnight! -# tests for standard formats: 0, 1, 2, 6, 13, 14, 15, 16, 23 -%!assert(datevec("07-Sep-2000 15:38:09"),[2000,9,7,15,38,9]); -%!assert(datevec("07-Sep-2000"),[2000,9,7,0,0,0]); -%!assert(datevec("09/07/00"),[2000,9,7,0,0,0]); -%!assert(datevec("09/13"),[nowvec(1),9,13,0,0,0]); -%!assert(datevec("15:38:09"),[nowvec(1:3),15,38,9]); -%!assert(datevec("3:38:09 PM"),[nowvec(1:3),15,38,9]); -%!assert(datevec("15:38"),[nowvec(1:3),15,38,0]); -%!assert(datevec("03:38 PM"),[nowvec(1:3),15,38,0]); -%!assert(datevec("03/13/1962"),[1962,3,13,0,0,0]); -# other tests -%!assert(all(datenum(datevec([-1e4:1e4]))==[-1e4:1e4]')) +%!# tests for standard formats: 0, 1, 2, 6, 13, 14, 15, 16, 23 +%!assert (datevec ("07-Sep-2000 15:38:09"), [2000,9,7,15,38,9]) +%!assert (datevec ("07-Sep-2000"), [2000,9,7,0,0,0]) +%!assert (datevec ("09/07/00"), [2000,9,7,0,0,0]) +%!assert (datevec ("09/13"), [nowvec(1),9,13,0,0,0]) +%!assert (datevec ("15:38:09"), [nowvec(1:3),15,38,9]) +%!assert (datevec ("3:38:09 PM"), [nowvec(1:3),15,38,9]) +%!assert (datevec ("15:38"), [nowvec(1:3),15,38,0]) +%!assert (datevec ("03:38 PM"), [nowvec(1:3),15,38,0]) +%!assert (datevec ("03/13/1962"), [1962,3,13,0,0,0]) + +%% Test millisecond format FFF +%!assert (datevec ("15:38:21.25", "HH:MM:SS.FFF"), [nowvec(1:3),15,38,21.025]) + +# Other tests +%!assert (datenum (datevec ([-1e4:1e4])), [-1e4:1e4]') %!test %! t = linspace (-2e5, 2e5, 10993); %! assert (all (abs (datenum (datevec (t)) - t') < 1e-5)); -# demos -%!demo -%! datevec (now ()) +