[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Octave-forge Date Functions Patch
From: |
John W. Eaton |
Subject: |
Octave-forge Date Functions Patch |
Date: |
Thu, 16 Mar 2006 15:31:45 -0500 |
On 7-Jan-2006, Bill Denney wrote:
| Here is a patch to the functions datesplit, datevec, and datenum to allow
| formats (as in strptime) to be used. It's very slow (at least on my
| testing in cygwin which I know is slow to begin with), so it may be
| worthwhile for someone to vectorize strptime.
|
| Changelog:
|
| 2005 Jan 7: Bill Denney
|
| datesplit.m, datevec.m, datenum.m: allow the specification of a format
| for the date
Sorry, I didn't realize that these were submitted here until after I
committed the changes. Can you please see if there are any changes in
your patch that should be applied to the current CVS sources and send
a revised patch relative to that?
Thanks,
jwe
| Bill
|
| --
| "Hurray for our side." -- Flynn, _Tron_
| Index: datenum.m
| ===================================================================
| RCS file: /cvsroot/octave/octave-forge/main/time/datenum.m,v
| retrieving revision 1.8
| diff -u -r1.8 datenum.m
| --- datenum.m 8 Sep 2005 02:00:18 -0000 1.8
| +++ datenum.m 7 Jan 2006 05:50:36 -0000
| @@ -1,6 +1,6 @@
| ## -*- texinfo -*-
| ## @deftypefn {Function File} {} datenum(Y, M, D [, h , m [, s]])
| -## @deftypefnx {Function File} {} datenum('date' [, P])
| +## @deftypefnx {Function File} {} datenum('date' [, P [, fmt]])
| ## Returns 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, corresponds to the portion of the specified day.
| @@ -12,6 +12,17 @@
| ## Days before the beginning of the month go to the previous month.
| ## Days can be fractional.
| ##
| +## The parameter @var{P} is needed to convert date strings with 2 digit
| +## years into dates with 4 digit years. 2 digit years are assumed to be
| +## between @var{P} and @code{P+99}. If @var{P} is not given then the
| +## current year - 50 is used, so that dates are centered on the present.
| +## For birthdates, you would want @var{P} to be current year - 99. For
| +## appointments, you would want @var{P} to be current year.
| +##
| +## The parameter @var{fmt} is optional and it allows you to specify the
| +## format as in @code{strftime}. See the @code{strftime} help for how
| +## to specify a format string.
| +##
| ## XXX WARNING XXX 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
| @@ -22,7 +33,7 @@
| ## XXX WARNING XXX 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{date,clock,now,datestr,datevec,calendar,weekday,strptime,strftime}
| ## @end deftypefn
|
| ## Algorithm: Peter Baum (http://vsg.cape.com/~pbaum/date/date0.htm)
| @@ -32,12 +43,13 @@
| function n = datenum(Y,M,D,h,m,s)
| persistent monthstart = [306,337,0,31,61,92,122,153,184,214,245,275];
|
| - if nargin == 0 || (nargin > 2 && ischar(Y)) || nargin > 6
| + if nargin == 0 || (nargin > 3 && isstr(Y)) || nargin > 6
| usage("n=datenum('date' [, P]) or n=datenum(Y, M, D [, h, m [, s]])");
| endif
| - if ischar(Y)
| - if nargin < 2, M=[]; endif
| - [Y,M,D,h,m,s] = datevec(Y,M);
| + if (isstr(Y) || iscellstr(Y))
| + if nargin < 2, M=[]; endif ## set P empty
| + if nargin < 3, D=""; endif ## set fmt empty
| + [Y,M,D,h,m,s] = datevec(Y,M,D);
| else
| if nargin < 6, s = 0; endif
| if nargin < 5, m = 0; endif
| Index: datesplit.m
| ===================================================================
| RCS file: /cvsroot/octave/octave-forge/main/time/datesplit.m,v
| retrieving revision 1.6
| diff -u -r1.6 datesplit.m
| --- datesplit.m 2 May 2005 15:30:16 -0000 1.6
| +++ datesplit.m 7 Jan 2006 05:50:37 -0000
| @@ -17,6 +17,7 @@
| ## -*- texinfo -*-
| ## @deftypefn {Function File} {Y =} datesplit(date, P)
| ## @deftypefnx {Function File} {[Y,M,D,h,m,s] =} datesplit(date, P)
| +## @deftypefnx {Function File} {[Y,M,D,h,m,s] =} datesplit(date, P, fmt)
| ## Split a date string into the Year, Month, Day, hour, minute, and
| ## second. This routine tries to be as forgiving as possible to the
| ## date input while requiring that the date is not ambiguous.
| @@ -26,10 +27,10 @@
| ## along the same lines, where possible, commas were allowed with
| ## spaces, and the year/month/day separators were allowed as period (.),
| ## slash (/), and dash (-). Not all format possibilities are shown in
| -## the following table, but a date like @code{dd-mmm-yyyy HH:MM:SS} is
| -## parsed just as well as @code{d/mmm.yyyy, ,H:MM, AM}.
| +## the following table, but a date like @var{dd-mmm-yyyy HH:MM:SS} is
| +## parsed just as well as @var{d/mmm.yyyy, ,H:MM, AM}.
| ##
| -## Supported @code{date} formats include (the same as datestr):
| +## Supported @var{date} formats include (the same as datestr):
| ## @multitable @columnfractions 0.1 0.45 0.45
| ## @item @strong{Code} @tab @strong{Format} @tab @strong{Example}
| ## @item 0 @tab dd-mmm-yyyy HH:MM:SS @tab 07-Sep-2000 15:38:09
| @@ -59,12 +60,16 @@
| ## @item 31 @tab yyyy-mm-dd HH:MM:SS @tab 1047-03-13 13:26:03
| ## @end multitable
| ##
| -## The parameter @code{P} is needed to convert date strings with 2 digit
| +## The parameter @var{P} is needed to convert date strings with 2 digit
| ## years into dates with 4 digit years. 2 digit years are assumed to be
| -## between @code{P} and @code{P+99}. If @code{P} is not given then the
| +## between @var{P} and @code{P+99}. If @var{P} is not given then the
| ## current year - 50 is used, so that dates are centered on the present.
| -## For birthdates, you would want @code{P} to be current year - 99. For
| -## appointments, you would want @code{P} to be current year.
| +## For birthdates, you would want @var{P} to be current year - 99. For
| +## appointments, you would want @var{P} to be current year.
| +##
| +## The parameter @var{fmt} is optional and it allows you to specify the
| +## format as in strptime and strftime. See the strftime help for how to
| +## specify a format string.
| ##
| ## This function makes no strong attempt to verify the accuracy of the
| ## numbers that it returns in that it doesn't (currently) check to see
| @@ -74,12 +79,10 @@
| ## but if there is doubt, datesplit will return an error instead of
| ## trying to guess the wrong value.
| ##
| -## @seealso{date,clock,now,datestr,datenum,calendar,weekday}
| +##
@seealso{date,clock,now,datestr,datenum,calendar,weekday,strftime,strptime}
| ## @end deftypefn
|
| ## TODO:
| -## * Some formats are ambiguous. Allow the user to specify the format
| -## to remove ambiguity.
| ## * Validate the dates.
| ## * Possible bug (after dates are validated): There are times where
| ## the year is assumed, Feb 29 may be a valid date, but with the
| @@ -89,11 +92,17 @@
|
| ## Author: Bill Denney <address@hidden>
|
| -function [y, m, d, h, mi, s] = datesplit(ds, P)
| +function [y, m, d, h, mi, s] = datesplit(ds, P, fmt)
|
| - if nargin < 2
| + if (nargin < 1)
| + usage("datesplit(ds, P, fmt)");
| + endif
| + if (nargin < 2)
| P = [];
| endif
| + if (nargin < 3)
| + fmt = "";
| + endif
|
| today = datevec(now);
|
| @@ -106,17 +115,48 @@
| global __day_names = ["Sun";"Mon";"Tue";"Wed";"Thu";"Fri";"Sat"];
| global __time_names = ["AM";"PM"];
|
| - if (iscellstr(ds))
| + if iscell(ds)
| + if (prod(size(ds)) > 1)
| + error("datesplit: only single element cells are allowed.");
| + endif
| ds = ds{1};
| endif
| +
| ds = tolower(deblank(ds));
|
| - if (nargin < 1)
| - error("datesplit: no input arguments");
| - elseif (nargin == 1)
| - fmt = [];
| + ## it's easy if we have a format to use
| + if (~ isempty(fmt))
| + for i = 1:size(ds,1)
| + datestruct(i) = strptime(ds(i,:), fmt);
| + endfor
| +
| + for i = 1:length(datestruct)
| + y(i) = datestruct.year;
| + m(i) = datestruct.mon;
| + d(i) = datestruct.mday;
| + h(i) = datestruct.hour;
| + mi(i) = datestruct.min;
| + s(i) = datestruct.sec;
| + endfor
| +
| + ## convert the digits to the digits that we use in this function
| + if all(y < 170)
| + ## strftime splits between the current and the last century at
| + ## 1970.
| +
| + ## make them use the P that is specified by our function and
| + ## convert to four digit years
| + y = mod(y,100);
| + y(y <= P) = y(y <= P) + 100;
| + y = y + 1900;
| + endif
| +
| + ## convert the months from zero to one based
| + m = m + 1;
| + return;
| endif
| - %% we have to determine the format, this could be error prone
| +
| + ## we have to determine the format, this could be error prone
|
| ## format 0 dd-mmm-yyyy HH:MM:SS e.g. 07-Sep-2000 15:38:09
| [match, d, m, y, h, mi, s, ap] = \
| Index: datevec.m
| ===================================================================
| RCS file: /cvsroot/octave/octave-forge/main/time/datevec.m,v
| retrieving revision 1.7
| diff -u -r1.7 datevec.m
| --- datevec.m 8 Sep 2005 02:00:18 -0000 1.7
| +++ datevec.m 7 Jan 2006 05:50:37 -0000
| @@ -2,6 +2,7 @@
| ## -*- texinfo -*-
| ## @deftypefn {Function File} {V =} datevec(date)
| ## @deftypefnx {Function File} {[Y,M,D,h,m,s] =} datevec(date, P)
| +## @deftypefnx {Function File} {[Y,M,D,h,m,s] =} datevec(date, P, fmt)
| ## Breaks the number of days since Jan 1, 0000 into a year-month-day
| ## hour-minute-second format. By this reckoning, Jan 1, 1970 is day
| ## number 719529. The fractional portion of @code{date} corresponds to the
| @@ -19,11 +20,14 @@
| ## For birthdates, you would want @code{P} to be current year - 99. For
| ## appointments, you would want @code{P} to be current year.
| ##
| +## The optional arguement @var{fmt} is allowed to indicate a format
| +## (parsable by @code{strptime}) to remove ambiguity about the date format.
| +##
| ## Dates must be represented as an integer date code or in a format
| ## recognisable by datesplit as either a string, string array, cell, or
| ## cell string array.
| ##
| -## @seealso{date,clock,now,datestr,datenum,calendar,weekday,datesplit}
| +##
@seealso{date,clock,now,datestr,datenum,calendar,weekday,datesplit,strptime,strftime}
| ## @end deftypefn
|
| ## Algorithm: Peter Baum (http://vsg.cape.com/~pbaum/date/date0.htm)
| @@ -31,14 +35,15 @@
| ## This program is granted to the public domain.
| ## Modified-by: Bill Denney <address@hidden>
|
| -function [Y,M,D,h,m,s] = datevec(date,P)
| +function [Y,M,D,h,m,s] = datevec(dv,P,fmt)
|
| - if nargin == 0 || nargin > 2
| - usage("V=datevec(n) or [Y,M,D,h,m,s]=datevec(n)");
| + if nargin == 0 || nargin > 3
| + usage("V=datevec(n,P,fmt) or [Y,M,D,h,m,s]=datevec(n,P,fmt)");
| endif
| if nargin < 2, P = []; endif
| + if nargin < 3, fmt = ""; endif
|
| - if (ischar(date) || iscellstr(date))
| + if (isstr(dv) || iscellstr(dv))
| ## handle strings
| if isempty(P)
| tm = localtime(time);
| @@ -49,22 +54,28 @@
| "Jul";"Aug";"Sep";"Oct";"Nov";"Dec"];
| global __time_names = ["AM";"PM"];
|
| - Y = h = m = s = zeros(rows(date),1);
| + if iscellstr(dv)
| + niter = length(dv(:));
| + else
| + niter = size(dv,1);
| + endif
| +
| + Y = h = m = s = zeros(niter,1);
| M = D = ones(size(Y));
|
| - for i = 1:size(date,1)
| - if (iscellstr(date))
| - thisdate = date{i};
| + for i = 1:niter
| + if (iscellstr(dv))
| + thisdv = dv{i};
| else
| - thisdate = date(i,:);
| + thisdv = dv(i,:);
| endif
| - [Y(i) M(i) D(i) h(i) m(i) s(i)] = datesplit(date(i,:), P);
| + [Y(i) M(i) D(i) h(i) m(i) s(i)] = datesplit(thisdv, P, fmt);
| endfor
| else
| ## handle date numbers
|
| ## Move day 0 from midnight -0001-12-31 to midnight 0001-3-1
| - z = floor(date) - 60;
| + z = floor(dv) - 60;
| ## Calculate number of centuries; K1=0.25 is to avoid rounding problems.
| a = floor((z-0.25)/36524.25);
| ## Days within century; K2=0.25 is to avoid rounding problems.
| @@ -81,7 +92,7 @@
| M(M>12)-=12;
|
| ## Convert hour-minute-seconds
| - s = 86400*(date-floor(date));
| + s = 86400*(dv-floor(dv));
| h = floor(s/3600);
| s = s - 3600*h;
| m = floor(s/60);