1## Copyright (C) 2006, 2007, 2008 Paul Kienzle
3## This file is part of Octave.
5## Octave is free software; you can redistribute it and/or modify it
6## under the terms of the GNU General Public License as published by
7## the Free Software Foundation; either version 3 of the License, or (at
8## your option) any later version.
10## Octave is distributed in the hope that it will be useful, but
11## WITHOUT ANY WARRANTY; without even the implied warranty of
12## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13## General Public License for more details.
15## You should have received a copy of the GNU General Public License
16## along with Octave; see the file COPYING. If not, see
17## <http://www.gnu.org/licenses/>.
20## @deftypefn {Function File} {} datenum (@var{year}, @var{month}, @var{day})
21## @deftypefnx {Function File} {} datenum (@var{year}, @var{month}, @var{day}, @var{hour})
22## @deftypefnx {Function File} {} datenum (@var{year}, @var{month}, @var{day}, @var{hour}, @var{minute})
23## @deftypefnx {Function File} {} datenum (@var{year}, @var{month}, @var{day}, @var{hour}, @var{minute}, @var{second})
24## @deftypefnx {Function File} {} datenum (@code{"date"})
25## @deftypefnx {Function File} {} datenum (@code{"date"}, @var{p})
26## Returns the specified local time as a day number, with Jan 1, 0000
27## being day 1. By this reckoning, Jan 1, 1970 is day number 719529.
28## The fractional portion, @var{p}, corresponds to the portion of the
35## Years can be negative and/or fractional.
37## Months below 1 are considered to be January.
39## Days of the month start at 1.
41## Days beyond the end of the month go into subsequent months.
43## Days before the beginning of the month go to the previous month.
45## Days can be fractional.
48## @strong{Warning:} this function does not attempt to handle Julian
49## calendars so dates before Octave 15, 1582 are wrong by as much
50## as eleven days. Also be aware that only Roman Catholic countries
51## adopted the calendar in 1582. It took until 1924 for it to be
52## adopted everywhere. See the Wikipedia entry on the Gregorian
53## calendar for more details.
55## @strong{Warning:} leap seconds are ignored. A table of leap seconds
56## is available on the Wikipedia entry for leap seconds.
57## @seealso{date, clock, now, datestr, datevec, calendar, weekday}
60## Algorithm: Peter Baum (http://vsg.cape.com/~pbaum/date/date0.htm)
61## Author: pkienzle <pkienzle@users.sf.net>
63function [days, secs] = datenum (Y, M, D, h, m, s)
65 ## Days until start of month assuming year starts March 1.
66 persistent monthstart = [306; 337; 0; 31; 61; 92; 122; 153; 184; 214; 245; 275];
68 if (nargin == 0 || (nargin > 2 && ischar (Y)) || nargin > 6)
75 [Y, M, D, h, m, s] = datevec (Y, M);
77 if (nargin < 6) s = 0; endif
78 if (nargin < 5) m = 0; endif
79 if (nargin < 4) h = 0; endif
83 error ("expected date vector containing [Y, M, D, h, m, s]");
86 if (nc >= 6) s = Y(:,6); endif
87 if (nc >= 5) m = Y(:,5); endif
88 if (nc >= 4) h = Y(:,4); endif
95 M(M<1) = 1; ## For compatibility. Otherwise allow negative months.
97 ## Set start of year to March by moving Jan. and Feb. to previous year.
98 ## Correct for months > 12 by moving to subsequent years.
101 ## Lookup number of days since start of the current year.
102 if (numel (M) == 1 || numel (D) == 1)
103 ## Allow M or D to be scalar while other values may be vectors or
105 D += monthstart (mod (M-1,12) + 1) + 60;
107 D = reshape (D, size (M));
110 D += reshape (monthstart (mod (M-1,12) + 1), size (D)) + 60;
113 ## Add number of days to the start of the current year. Correct
114 ## for leap year every 4 years except centuries not divisible by 400.
115 D += 365*Y + floor (Y/4) - floor (Y/100) + floor (Y/400);
117 ## Add fraction representing current second of the day.
118 days = D + (h+(m+s/60)/60)/24;
120 ## Output seconds if asked so that etime can be more accurate
121 secs = 86400*D + h*3600 + m*60 + s;
126%! part = 0.514623842592593;
127%!assert(datenum(2001,5,19), 730990)
128%!assert(datenum([1417,6,12]), 517712)
129%!assert(datenum([2001,5,19;1417,6,12]), [730990;517712])
130%!assert(datenum(2001,5,19,12,21,3.5), 730990+part, eps)
131%!assert(datenum([1417,6,12,12,21,3.5]), 517712+part, eps)
134%! t = [2001,5,19,12,21,3.5; 1417,6,12,12,21,3.5];
135%! n = [730990; 517712] + part;
136%! assert(datenum(t), n, 2*eps);
137## Make sure that the vectors can have either orientation
139%! t = [2001,5,19,12,21,3.5; 1417,6,12,12,21,3.5]';
140%! n = [730990 517712] + part;
141%! assert(datenum(t(1,:), t(2,:), t(3,:), t(4,:), t(5,:), t(6,:)), n, 2*eps);
143## Test mixed vectors and scalars
144%!assert (datenum([2008;2009], 1, 1), [datenum(2008, 1, 1);datenum(2009, 1, 1)]);
145%!assert (datenum(2008, [1;2], 1), [datenum(2008, 1, 1);datenum(2008, 2, 1)]);
146%!assert (datenum(2008, 1, [1;2]), [datenum(2008, 1, 1);datenum(2008, 1, 2)]);
147%!assert (datenum([2008;2009], [1;2], 1), [datenum(2008, 1, 1);datenum(2009, 2, 1)]);
148%!assert (datenum([2008;2009], 1, [1;2]), [datenum(2008, 1, 1);datenum(2009, 1, 2)]);
149%!assert (datenum(2008, [1;2], [1;2]), [datenum(2008, 1, 1);datenum(2008, 2, 2)]);
150## And the other orientation
151%!assert (datenum([2008 2009], 1, 1), [datenum(2008, 1, 1) datenum(2009, 1, 1)]);
152%!assert (datenum(2008, [1 2], 1), [datenum(2008, 1, 1) datenum(2008, 2, 1)]);
153%!assert (datenum(2008, 1, [1 2]), [datenum(2008, 1, 1) datenum(2008, 1, 2)]);
154%!assert (datenum([2008 2009], [1 2], 1), [datenum(2008, 1, 1) datenum(2009, 2, 1)]);
155%!assert (datenum([2008 2009], 1, [1 2]), [datenum(2008, 1, 1) datenum(2009, 1, 2)]);
156%!assert (datenum(2008, [1 2], [1 2]), [datenum(2008, 1, 1) datenum(2008, 2, 2)]);