1 /* ======================================================= *
2 * Copyright 1998-2006 Stephen C. Grubb *
3 * http://ploticus.sourceforge.net *
4 * Covered by GPL; see the file ./Copyright for details. *
5 * ======================================================= */
6
7 #include "pl.h"
8
9 /* Given smin, smax, and nearest, determine minval and maxval.
10 Smin and Smax are string reps of numerics, dates, times, etc.
11 Pass identical smin and smax to get a $dategroup() or $numgroup() operation.
12
13 Function returns 1 if the 'nearest' mode was recognized and a nearest range computed.
14 Function returns 0 otherwise.
15 */
16 int
PLP_findnearest(smin,smax,axis,nearest,minval,maxval)17 PLP_findnearest( smin, smax, axis, nearest, minval, maxval )
18 char *smin, *smax, axis, *nearest, *minval, *maxval;
19 {
20 int stat;
21 char datepart[40], timepart[40], unittyp[40];
22
23 Egetunits( axis, unittyp );
24
25 if( strncmp( nearest, "month", 5 )== 0 || strncmp( nearest, "quarter", 7 )==0 || strncmp( nearest, "3month", 6 )==0 ) {
26 /* nearest month boundary / quarter-year boundary.. */
27 int mon, day, yr, newmon;
28 long l;
29 if( !GL_smember( unittyp, "date datetime" ))
30 Eerr( 2892, "autorange 'nearest=month' or 'nearest=quarter' only valid with date or datetime scaletype", unittyp );
31 /* min */
32 stat = DT_jdate( smin, &l );
33 DT_getmdy( &mon, &day, &yr );
34 if( nearest[0] == 'q' || nearest[0] == '3' ) {
35 if( mon >= 10 ) mon = 10;
36 else if( mon >= 7 ) mon = 7;
37 else if( mon >= 4 ) mon = 4;
38 else if( mon >= 1 ) mon = 1;
39 }
40 DT_makedate( yr, mon, 1, "", datepart );
41 if( strcmp( unittyp, "datetime" )==0 ) {
42 DT_maketime( 0, 0, 0.0, timepart );
43 DT_build_dt( datepart, timepart, minval );
44 }
45 else strcpy( minval, datepart );
46
47 /* max */
48 stat = DT_jdate( smax, &l );
49 DT_getmdy( &mon, &day, &yr );
50 if( nearest[0] == 'q' || nearest[0] == '3' ) {
51 if( mon <= 3 ) mon = 4;
52 else if( mon <= 6 ) mon = 7;
53 else if( mon <= 9 ) mon = 10;
54 else if( mon <= 12 ) mon = 13;
55 }
56 else mon ++;
57
58 /* wrap around year.. */
59 newmon = ((mon-1) % 12 ) +1;
60 yr += ((mon-1) / 12);
61 mon = newmon;
62 DT_makedate( yr, mon, 1, "", datepart );
63 if( strcmp( unittyp, "datetime" )==0 ) {
64 DT_maketime( 0, 0, 0.0, timepart );
65 DT_build_dt( datepart, timepart, maxval );
66 }
67 else strcpy( maxval, datepart );
68 return( 1 );
69 }
70
71
72 else if( strncmp( nearest, "year", 4 )== 0 || strncmp( nearest, "2year", 5 )==0 ||
73 strncmp( nearest, "5year", 5 )==0 || strncmp( nearest, "10year", 6 )==0 ) {
74 int mon, day, yr;
75 long l;
76 int yearsblock; /* 0 5 or 10 */
77
78 if( !GL_smember( unittyp, "date datetime" ))
79 Eerr( 2892, "autorange 'nearest=year' only valid with date or datetime scaletype", unittyp );
80
81 if( nearest[0] != 'y' ) { /* this section scg 1/28/05 */
82 yearsblock = nearest[0] - '0';
83 if( yearsblock == 1 ) yearsblock = 10;
84 }
85 else yearsblock = 0;
86
87 /* min */
88 stat = DT_jdate( smin, &l );
89 DT_getmdy( &mon, &day, &yr );
90 if( yearsblock ) yr = (yr / yearsblock) * yearsblock; /* scg 1/28/05 */
91 DT_makedate( yr, 1, 1, "", datepart );
92 if( strcmp( unittyp, "datetime" )==0 ) {
93 DT_maketime( 0, 0, 0.0, timepart );
94 DT_build_dt( datepart, timepart, minval );
95 }
96 else strcpy( minval, datepart );
97
98 /* max */
99 stat = DT_jdate( smax, &l );
100 DT_getmdy( &mon, &day, &yr );
101 if( yearsblock ) yr = ((yr / yearsblock)+1) * yearsblock; /* scg 1/28/05 */
102 else yr++;
103 DT_makedate( yr, 1, 1, "", datepart );
104 if( strcmp( unittyp, "datetime" )==0 ) {
105 DT_maketime( 0, 0, 0.0, timepart );
106 DT_build_dt( datepart, timepart, maxval );
107 }
108 else strcpy( maxval, datepart );
109 return( 1 );
110 }
111
112
113 else if( strncmp( nearest, "day", 3 )== 0 || strcmp( nearest, "monday" )==0 || strcmp( nearest, "sunday" )==0 ) {
114 int mon, day, yr;
115 double days, mins;
116
117 if( !GL_smember( unittyp, "date datetime" ))
118 Eerr( 2892, "autorange 'nearest=day' only valid with date or datetime scaletype", unittyp );
119
120 /* min */
121 if( strcmp( unittyp, "datetime" )==0 ) DT_getdtparts( smin, datepart, timepart );
122 else strcpy( datepart, smin ); /* if and else added scg 8/10/05 */
123
124 if( nearest[0] == 'm' || nearest[0] == 's' ) { /* adjust datepart back to a monday or sunday */
125 int iwk; char rbuf[40];
126 DT_weekday( datepart, rbuf, &iwk ); /* rbuf not used */
127 if( nearest[0] == 'm' ) { if( iwk == 1 ) iwk = 8; DT_dateadd( datepart, 2 - iwk, rbuf ); }
128 else if( nearest[0] == 's' ) DT_dateadd( datepart, 1 - iwk, rbuf );
129 strcpy( datepart, rbuf );
130 }
131
132 /* this is just a way to get the dt parts (?) ...
133 * DT_datetime2days( smin, &days );
134 * DT_getmdy( &mon, &day, &yr );
135 * DT_makedate( yr, mon, day, "", datepart );
136 */
137
138 if( strcmp( unittyp, "date" )==0 )
139 /* check for biz day window.. scg 7/21/04 */
140 mins = 0.0;
141 DT_frame_mins( &mins ); /* adjust to any biz day window.. */
142 DT_frommin( mins, timepart );
143 if( strcmp( unittyp, "date" )==0 ) strcpy( minval, datepart );
144 else DT_build_dt( datepart, timepart, minval );
145
146 /* max */
147 if( strcmp( unittyp, "datetime" )==0 ) DT_getdtparts( smax, datepart, timepart );
148 else strcpy( datepart, smax ); /* if and else added scg 8/10/05 */
149
150 if( nearest[0] == 'm' || nearest[0] == 's' ) { /* adjust datepart to next monday or sunday */
151 int iwk; char rbuf[40];
152 DT_weekday( datepart, rbuf, &iwk ); /* rbuf not used */
153 if( nearest[0] == 'm' ) { if( iwk == 1 ) iwk = 8; DT_dateadd( datepart, 9 - iwk, rbuf ); }
154 else if( nearest[0] == 's' ) DT_dateadd( datepart, 8 - iwk, rbuf );
155 DT_build_dt( rbuf, timepart, smax );
156 }
157
158 DT_datetime2days( smax, &days );
159 if( fabs( days - floor( days )) < 0.0001 ) ; /* avoid spurious extra day when data max is on date boundary added scg 7/21/04 */
160 else days++;
161 DT_days2datetime( days, smax );
162 DT_datetime2days( smax, &days ); /* set next day's date for getmdy below */
163 DT_getmdy( &mon, &day, &yr );
164 DT_makedate( yr, mon, day, "", datepart );
165 DT_maketime( 0, 0, 0.0, timepart );
166 if( strcmp( unittyp, "date" )==0 ) strcpy( maxval, datepart );
167 else DT_build_dt( datepart, timepart, maxval );
168 return( 1 );
169 }
170
171 else if( strncmp( nearest, "hour", 4 )== 0 || strncmp( nearest, "3hour", 5 )==0 ||
172 strncmp( nearest, "6hour", 5 )==0 || strncmp( nearest, "12hour", 6 )==0 ) {
173 int hour, minute;
174 double sec;
175 int hoursblock; /* 0, 3, 6, or 12 */
176 if( !GL_smember( unittyp, "time datetime" ))
177 Eerr( 2892, "autorange 'nearest=hour' is incompatible with scaletype", unittyp );
178
179 if( nearest[0]!= 'h' ) { /* this section scg 1/28/05 */
180 hoursblock = nearest[0] - '0';
181 if( hoursblock == 1 ) hoursblock = 12;
182 }
183 else hoursblock = 0;
184
185 if( strcmp( unittyp, "time" )==0 ) {
186 /* min */
187 DT_tomin( smin, &sec ); /* sec not used */
188 DT_gethms( &hour, &minute, &sec );
189 if( hoursblock ) hour = (hour / hoursblock) * hoursblock; /* scg 1/28/05 */
190 DT_maketime( hour, 0, 0.0, minval );
191 /* max */
192 DT_tomin( smax, &sec ); /* sec not used */
193 DT_gethms( &hour, &minute, &sec );
194 if( hoursblock ) hour = ((hour / hoursblock)+1) * hoursblock; /* scg 1/28/05 */
195 if( minute != 0 || sec != 0.0 ) hour++; /* bug, scg 12/13/05 */
196 DT_maketime( hour, 0, 0.0, maxval );
197 }
198 else if( strcmp( unittyp, "datetime" )==0 ) {
199 double days;
200 int mon, day, yr;
201
202 /* min */
203 DT_datetime2days( smin, &days );
204 /* time part */
205 DT_gethms( &hour, &minute, &sec );
206 if( hoursblock ) hour = (hour / hoursblock) * hoursblock; /* scg 1/28/05 */
207 DT_maketime( hour, 0, 0.0, timepart );
208 /* date part */
209 DT_getmdy( &mon, &day, &yr );
210 DT_makedate( yr, mon, day, "", datepart );
211 DT_build_dt( datepart, timepart, minval );
212
213 /* max */
214 DT_datetime2days( smax, &days );
215 /* time part */
216 DT_gethms( &hour, &minute, &sec );
217 if( hour == 23 ) {
218 DT_days2datetime( days+1.0, smax ); /* set next day's date for getmdy below*/
219 DT_datetime2days( smax, &days ); /* set next day's date for getmdy below*/
220 DT_maketime( 0, 0, 0.0, timepart ); /* ok for any hoursblock */
221 }
222 else {
223 if( hoursblock ) hour = ((hour / hoursblock)+1) * hoursblock; /* scg 1/28/05 */
224 else hour++;
225 DT_maketime( hour, 0, 0.0, timepart );
226 }
227 /* date part */
228 DT_getmdy( &mon, &day, &yr );
229 DT_makedate( yr, mon, day, "", datepart );
230 DT_build_dt( datepart, timepart, maxval );
231 }
232 return( 1 );
233 }
234
235 /* else if( strcmp( nearest, "minute" )==0 || strcmp( nearest, "10minute" )==0 ||
236 * strcmp( nearest, "20minute" )==0 || strcmp( nearest, "30minute" )==0 ) {
237 */
238 else if( stricmp( nearest, "minute" )==0 || /* handles minute or Nminute or NNminute, contributed by Chris Demetriou 1/9/08 */
239 ( nearest[0] >= '0' && nearest[0] <= '9' && stricmp( nearest + 1, "minute" )==0 ) ||
240 ( nearest[0] >= '0' && nearest[0] <= '9' && nearest[1] >= '0' && nearest[1] <= '9' && stricmp( nearest + 2, "minute" )==0 ) ) {
241
242 int hour, minute, minblock;
243 double sec;
244
245 if( strcmp( unittyp, "time" )!= 0 ) Eerr( 2892, "autorange 'nearest=minute' is incompatible with scaletype", unittyp );
246
247 /* if( nearest[0] != 'm' ) minblock = (nearest[0] - '0') * 10; // changed 1/9/08 */
248 if( tolower( (int) nearest[0] ) != 'm' ) {
249 minblock = atoi(nearest);
250 if( minblock < 0 || minblock >= 60 || ( 60 % minblock )!=0 ) {
251 Eerr( 2892 /*???*/, "invalid number of minutes in 'nearest' autorange specification", nearest );
252 }
253 }
254 else minblock = 0;
255
256 /* min */
257 DT_tomin( smin, &sec ); /* sec not used */
258 DT_gethms( &hour, &minute, &sec );
259 if( minblock ) minute = (minute / minblock) * minblock;
260 DT_maketime( hour, minute, 0.0, minval );
261
262 /* max */
263 DT_tomin( smax, &sec ); /* sec not used */
264 DT_gethms( &hour, &minute, &sec );
265 if( minblock ) minute = ((minute / minblock)+1) * minblock;
266 else minute++;
267 if( minute >= 60 ) { minute = minute % 60; hour++; }
268 DT_maketime( hour, minute, 0.0, maxval );
269 return( 1 );
270 }
271
272
273 else if( stricmp( nearest, "second" )==0 || /* handles second or Nsecond or NNsecond, contributed by Chris Demetriou 1/9/08 */
274 ( nearest[0] >= '0' && nearest[0] <= '9' && stricmp( nearest + 1, "second" )==0 ) ||
275 ( nearest[0] >= '0' && nearest[0] <= '9' && nearest[1] >= '0' && nearest[1] <= '9' && stricmp( nearest + 2, "second" )==0 ) ) {
276 int hour, minute, isec, secblock;
277 double sec;
278
279 if( strcmp( unittyp, "time" )!= 0 ) Eerr( 2892, "autorange 'nearest=second' is incompatible with scaletype", unittyp );
280
281 if( tolower( (int) nearest[0] ) != 's' ) {
282 secblock = atoi(nearest);
283 if( secblock < 0 || secblock >= 60 || ( 60 % secblock )!=0 ) {
284 Eerr( 2892 /*???*/, "invalid number of minutes in 'nearest' autorange specification", nearest );
285 }
286 }
287 else secblock = 0;
288
289 /* min */
290 DT_tomin( smin, &sec );
291 DT_gethms( &hour, &minute, &sec );
292 isec = (int)floor(sec);
293 if( secblock ) isec = (isec / secblock) * secblock;
294 DT_maketime( hour, minute, isec * 1.0, minval );
295
296 /* max */
297 DT_tomin( smax, &sec );
298 DT_gethms( &hour, &minute, &sec );
299 isec = (int)ceil(sec); /* will round up to "60" at most. */
300 if( secblock ) isec = ((isec + secblock - 1) / secblock) * secblock;
301 if( isec >= 60 ) { minute++; isec = 0; }
302 if( minute >= 60 ) { minute = minute % 60; hour++; }
303 DT_maketime( hour, minute, isec * 1.0, maxval );
304 return( 1 );
305 }
306
307
308 return( 0 );
309 }
310