1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
|
########################################################################
##
## Copyright (C) 2006-2024 The Octave Project Developers
##
## See the file COPYRIGHT.md in the top-level directory of this
## distribution or <https://octave.org/copyright/>.
##
## This file is part of Octave.
##
## Octave is free software: you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## Octave is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Octave; see the file COPYING. If not, see
## <https://www.gnu.org/licenses/>.
##
########################################################################
## -*- texinfo -*-
## @deftypefn {} {@var{days} =} datenum (@var{datevec})
## @deftypefnx {} {@var{days} =} datenum (@var{year}, @var{month}, @var{day})
## @deftypefnx {} {@var{days} =} datenum (@var{year}, @var{month}, @var{day}, @var{hour})
## @deftypefnx {} {@var{days} =} datenum (@var{year}, @var{month}, @var{day}, @var{hour}, @var{minute})
## @deftypefnx {} {@var{days} =} datenum (@var{year}, @var{month}, @var{day}, @var{hour}, @var{minute}, @var{second})
## @deftypefnx {} {@var{days} =} datenum ("datestr")
## @deftypefnx {} {@var{days} =} datenum ("datestr", @var{f})
## @deftypefnx {} {@var{days} =} datenum ("datestr", @var{p})
## @deftypefnx {} {[@var{days}, @var{secs}] =} datenum (@dots{})
## Return the date/time input as a serial day number, with Jan 1, 0000
## defined as day 1.
##
## The integer part, @code{floor (@var{days})} counts the number of
## complete days in the date input.
##
## The fractional part, @code{rem (@var{days}, 1)} corresponds to the time
## on the given day.
##
## The input may be a date vector (@pxref{XREFdatevec,,@code{datevec}}),
## date string (@pxref{XREFdatestr,,@code{datestr}}), or directly specified
## as input.
##
## When processing input datestrings, @var{f} is the format string used to
## interpret date strings (@pxref{XREFdatestr,,@code{datestr}}). If no
## format @var{f} is specified, then a relatively slow search is performed
## through various formats. It is always preferable to specify the format
## string @var{f} if it is known. Formats which do not specify a particular
## time component will have the value set to zero. Formats which do not
## specify a date will default to January 1st of the current year.
##
## When passing separate @var{year}, @var{month}, @var{day}, etc.@: arguments,
## each may be a scalar or nonscalar array. Nonscalar inputs must all be of
## the same size. Scalar inputs will be expanded to be the size of the
## nonscalar inputs.
##
## @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:
##
## @itemize
## @item
## Years can be negative and/or fractional.
##
## @item
## Months below 1 are considered to be January.
##
## @item
## Days of the month start at 1.
##
## @item
## Days beyond the end of the month go into subsequent months.
##
## @item
## Days before the beginning of the month go to the previous month.
##
## @item
## Days can be fractional.
## @end itemize
##
## Examples:
##
## @example
## @group
## Convert from datestrs:
## d = datenum ("1966-06-14")
## @result{} d = 718232
## @end group
##
## @group
## d = datenum (@{"1966-06-14", "1966-06-15", "1966-06-16"@})
## @result{} d =
## 718232
## 718233
## 718234
## @end group
##
## @group
## Convert from datevec:
## d = datenum ([1966 06 14])
## @result{} d = 718232
## @end group
##
## @group
## d = datenum ([1966 06 14 23 59 59])
## @result{} d = 718232.9999884259
## @end group
##
## @group
## Specify date components separately:
## d = datenum (1966, 6, 14)
## @result{} d = 718232
## @end group
##
## @group
## d = datenum (1966, magic(3), 1)
## @result{} d =
##
## 718280 718068 718219
## 718127 718188 718249
## 718158 718311 718099
## @end group
## @end example
##
## @strong{Caution:} datenums represent a specific time for the Earth as a
## whole. They do not take in to account time zones (shifts in time based
## on location), nor seasonal changes due to Daylight Savings Time (shifts in
## time based on local regulation). Be aware that it is possible to create
## datenums that, when interpreted by a function which accounts for time zone
## and DST shifts such as @code{datestr}, are nonexistent or ambiguous.
##
## @strong{Caution:} this function does not attempt to handle Julian calendars
## so dates before October 15, 1582 are wrong by as much 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{datestr, datevec, now, clock, date}
## @end deftypefn
##
## Algorithm: Peter Baum (http://vsg.cape.com/~pbaum/date/date0.htm)
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];
persistent monthlength = [31; 28; 31; 30; 31; 30; 31; 31; 30; 31; 30; 31];
if (nargin == 0 || (nargin > 2 && (ischar (year) || iscellstr (year))))
print_usage ();
endif
do_reshape = false;
if (ischar (year) || iscellstr (year))
[year, month, day, hour, minute, second] = datevec (year, month);
else
if (nargin == 1)
nc = columns (year);
if (nc > 6 || nc < 3)
error ("datenum: expected date vector containing [YEAR, MONTH, DAY, HOUR, MINUTE, SECOND]");
endif
if (nc >= 6) second = year(:,6); endif
if (nc >= 5) minute = year(:,5); endif
if (nc >= 4) hour = year(:,4); endif
day = year(:,3);
month = year(:,2);
year = year(:,1);
else
[err, year, month, day] = common_size (year, month, day);
if (err)
error ("datenum: incompatible sizes for YEAR, MONTH, DAY");
endif
## Preserve shape if necessary, and work with column vectors
## for the remainder of the function.
do_reshape = ! iscolumn (day);
if (do_reshape)
sz_reshape = size (day);
year = year(:);
month = month(:);
day = day(:);
endif
endif
endif
if (! (isa (year, "double") && isa (month, "double")
&& isa (day, "double") && isa (hour, "double")
&& isa (minute, "double") && isa (second, "double")))
error ("datenum: all inputs must be of class double");
endif
## For Matlab compatibility. Otherwise, could allow negative months.
month(month < 1) = 1;
## Treat fractional months, by converting the fraction to days
if (any (month != fix (month)))
fracmonth = month - floor (month);
month = floor (month);
## Separate regular months from leap months
idx = mod (month-1,12) + 1 != 2 | ! is_leap_year (floor (year));
day(idx) += fracmonth(idx) .* monthlength(mod (month(idx)-1,12) + 1);
day(! idx) += fracmonth(! idx) * 29;
endif
## Set start of year to March by moving Jan. and Feb. to previous year.
## Correct for months > 12 by moving to subsequent years.
year += ceil ((month-14)/12);
## Lookup number of days since start of the current year.
day += monthstart(mod (month-1,12) + 1) + 60;
## Treat fractional years, by converting the fraction to days
if (any (year != fix (year)))
fracyear = year - floor (year);
year = floor (year);
day += fracyear .* (365 + is_leap_year (year+1));
endif
## Add number of days to the start of the current year. Correct
## for leap year every 4 years except centuries not divisible by 400.
day += 365*year + floor (year/4) - floor (year/100) + floor (year/400);
if (do_reshape)
day = reshape (day, sz_reshape);
endif
## Add fraction representing current second of the day.
days = day + (hour + (minute + second/60)/60)/24;
## Output seconds if asked so that etime can be more accurate
if (nargout > 1)
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)
## 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);
%! ## 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);
%!assert (size (datenum (2000, 1, ones(1, 1, 3))), [1 1 3])
## Test fractional years including leap years
%!assert (fix (datenum ([2001.999 1 1; 2001.999 2 1])), [731216; 731247])
%!assert (fix (datenum ([2004.999 1 1; 2004.999 2 1])), [732312; 732343])
## Test fractional months including leap months
%!assert (fix (datenum ([2001 1.999 1; 2001 2.999 1])), [730882; 730910])
%!assert (fix (datenum ([2004 1.999 1; 2004 2.999 1])), [731977; 732006])
## Test mixed vectors and scalars
%!assert (datenum ([2008;2009],1,1), [datenum(2008,1,1);datenum(2009,1,1)])
%!assert (datenum (2008, [1;2], 1), [datenum(2008,1,1);datenum(2008,2,1)])
%!assert (datenum (2008, 1, [1;2]), [datenum(2008,1,1);datenum(2008,1,2)])
%!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)])
## And the other orientation
%!assert (datenum ([2008 2009], 1, 1), [datenum(2008,1,1) datenum(2009,1,1)])
%!assert (datenum (2008, [1 2], 1), [datenum(2008,1,1) datenum(2008,2,1)])
%!assert (datenum (2008, 1, [1 2]), [datenum(2008,1,1) datenum(2008,1,2)])
%!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 string input with format string
%!assert (datenum ("5-19, 2001", "mm-dd, yyyy"), 730990)
## Test input validation
%!error <Invalid call> datenum ()
%!error <expected date vector containing> datenum ([1, 2])
%!error <expected date vector containing> datenum ([1,2,3,4,5,6,7])
%!error <all inputs must be of class double> datenum (int32 (2000), int32 (1), int32 (1))
|