1 ## Copyright (C) 2008 Bill Denney <bill@denney.ws>
3 ## This program is free software; you can redistribute it and/or modify it under
4 ## the terms of the GNU General Public License as published by the Free Software
5 ## Foundation; either version 3 of the License, or (at your option) any later
8 ## This program is distributed in the hope that it will be useful, but WITHOUT
9 ## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
13 ## You should have received a copy of the GNU General Public License along with
14 ## this program; if not, see <http://www.gnu.org/licenses/>.
17 ## @deftypefn {Function File} {last =} nweekdate (n, weekday, year, month, nextday)
19 ## Returns the @var{n}th occurrence of @var{weekday} from the
20 ## @var{month} and @var{year}. If the optional @var{nextday} argument
21 ## is given, then the week must also contain @var{nextday}. If @var{n}
22 ## is greater than the number of occurrences of that day in the month, 0
25 ## @seealso{eomdate, lweekdate, weekday}
28 function t = nweekdate (varargin)
29 if nargin < 4 || nargin > 5
30 error ("4 or 5 input arguments are required")
35 ## special handling so that most of this code will not need to be
36 ## duplicated in lweekdate
37 do_lweekdate = is_lweekdate(varargin{1});
42 scale = cellfun (@numel, varargin);
43 if ~ all (scale == 1 | scale == max(scale));
44 error("All inputs must be either scalars or have the same number of elements");
46 ## make sure that the sizes are the same for any non-scalar inputs
47 ind = find (scale > 1);
49 for i = 2:length (ind)
50 if ndims (varargin{ind(1)}) ~= ndims (varargin{ind(i)})
51 error("Mismatching dimensions on inputs %d and %d", ind(1), ind(i));
52 elseif ~ all (size (varargin{ind(1)}) == size (varargin{ind(i)}))
53 error("The sizes of inputs %d and %d do not match", ind(1), ind(i));
60 t = zeros (size (varargin{ind(1)}));
61 for i = 1:numel (varargin{ind(1)})
64 if isscalar (varargin{j})
65 args{j} = varargin{j};
67 args{j} = varargin{j}(i);
71 args{1} = "lweekdate";
73 t(i) = nweekdate (args{:});
81 nextday = varargin{5};
83 ## Find the day of the week for the last day of the mon.
84 doml = eomday (y, mon);
85 dowl = weekday (datenum (y, mon, doml));
86 ## Make sure that the day is in the weeks for the last then the
88 if (wd < nextday) || (dowl < wd)
89 ## adjust the last day
94 dom = sort((doml - dowl + wd - adjust):-7:1);
95 if nextday && (dom(1) <= wd - nextday)
96 # adjust the first day
100 t = datenum (y, mon, dom(end));
101 elseif n > length(dom)
104 t = datenum (y, mon, dom(n));
110 function v = is_lweekdate(v)
112 if strcmp (v, "lweekdate")
115 error("Invalid input for n")
122 # Tests for all calling options
123 # Find the first Wednesday in Jan 2008
124 %!assert(nweekdate(1, 4, 2008, 1), datenum(2008, 1, 2))
125 # Find the second Wednesday in Jan 2008
126 %!assert(nweekdate(2, 4, 2008, 1), datenum(2008, 1, 9))
127 # Find the third Wednesday in Jan 2008
128 %!assert(nweekdate(3, 4, 2008, 1), datenum(2008, 1, 16))
129 # Find the fourth Wednesday in Jan 2008
130 %!assert(nweekdate(4, 4, 2008, 1), datenum(2008, 1, 23))
131 # Find the fifth Wednesday in Jan 2008
132 %!assert(nweekdate(5, 4, 2008, 1), datenum(2008, 1, 30))
133 # Find the sixth Wednesday in Jan 2008, it doesn't exist, so return 0
134 %!assert(nweekdate(6, 4, 2008, 1), 0)
135 # Find the fifth Friday in Jan 2008, it doesn't exist, so return 0
136 %!assert(nweekdate(5, 6, 2008, 1), 0)
137 # Find the first Wednesday in Jan 2008 in the same week as a Monday
138 # WARNING: it is unclear from the Matlab docs if this should work or if
139 # this should be called the second Wednesday in Jan 2008.
140 %!assert(nweekdate(1, 4, 2008, 1, 2), datenum(2008, 1, 9))
141 # Find the fifth Wednesday in Jan 2008 in the same week as a Friday.
142 # It doesn't exist, so return 0
143 %!assert(nweekdate(5, 4, 2008, 1, 6), 0)
144 # Try vector arguments
145 %!assert(nweekdate(1:6, 4, 2008, 1, 6), [datenum(2008, 1, 2:7:23), 0, 0])
147 # Try the lweekdate operation of this function:
148 # Find the last Wednesday in Jan 2008
149 %!assert(nweekdate('lweekdate', 4, 2008, 1), datenum(2008, 1, 30))
150 # Find the last Wednesday in Jan 2008 with a Friday
151 %!assert(nweekdate('lweekdate', 4, 2008, 1, 6), datenum(2008, 1, 23))