1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2013 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Authors: Derick Rethans <derick@derickrethans.nl> |
16 +----------------------------------------------------------------------+
17 */
18
19 /* $Id$ */
20
21 #include "timelib.h"
22
23 /* jan feb mrt apr may jun jul aug sep oct nov dec */
24 static int month_tab_leap[12] = { -1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
25 static int month_tab[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
26
27 /* dec jan feb mrt apr may jun jul aug sep oct nov dec */
28 static int days_in_month_leap[13] = { 31, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
29 static int days_in_month[13] = { 31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
30
do_range_limit(timelib_sll start,timelib_sll end,timelib_sll adj,timelib_sll * a,timelib_sll * b)31 static int do_range_limit(timelib_sll start, timelib_sll end, timelib_sll adj, timelib_sll *a, timelib_sll *b)
32 {
33 if (*a < start) {
34 *b -= (start - *a - 1) / adj + 1;
35 *a += adj * ((start - *a - 1) / adj + 1);
36 }
37 if (*a >= end) {
38 *b += *a / adj;
39 *a -= adj * (*a / adj);
40 }
41 return 0;
42 }
43
inc_month(timelib_sll * y,timelib_sll * m)44 static void inc_month(timelib_sll *y, timelib_sll *m)
45 {
46 (*m)++;
47 if (*m > 12) {
48 *m -= 12;
49 (*y)++;
50 }
51 }
52
dec_month(timelib_sll * y,timelib_sll * m)53 static void dec_month(timelib_sll *y, timelib_sll *m)
54 {
55 (*m)--;
56 if (*m < 1) {
57 *m += 12;
58 (*y)--;
59 }
60 }
61
do_range_limit_days_relative(timelib_sll * base_y,timelib_sll * base_m,timelib_sll * y,timelib_sll * m,timelib_sll * d,timelib_sll invert)62 static void do_range_limit_days_relative(timelib_sll *base_y, timelib_sll *base_m, timelib_sll *y, timelib_sll *m, timelib_sll *d, timelib_sll invert)
63 {
64 timelib_sll leapyear;
65 timelib_sll month, year;
66 timelib_sll days;
67
68 do_range_limit(1, 13, 12, base_m, base_y);
69
70 year = *base_y;
71 month = *base_m;
72
73 /*
74 printf( "S: Y%d M%d %d %d %d %d\n", year, month, *y, *m, *d, days);
75 */
76 if (!invert) {
77 while (*d < 0) {
78 dec_month(&year, &month);
79 leapyear = timelib_is_leap(year);
80 days = leapyear ? days_in_month_leap[month] : days_in_month[month];
81
82 /* printf( "I Y%d M%d %d %d %d %d\n", year, month, *y, *m, *d, days); */
83
84 *d += days;
85 (*m)--;
86 }
87 } else {
88 while (*d < 0) {
89 leapyear = timelib_is_leap(year);
90 days = leapyear ? days_in_month_leap[month] : days_in_month[month];
91
92 /* printf( "I Y%d M%d %d %d %d %d\n", year, month, *y, *m, *d, days); */
93
94 *d += days;
95 (*m)--;
96 inc_month(&year, &month);
97 }
98 }
99 /*
100 printf( "E: Y%d M%d %d %d %d %d\n", year, month, *y, *m, *d, days);
101 */
102 }
103
do_range_limit_days(timelib_sll * y,timelib_sll * m,timelib_sll * d)104 static int do_range_limit_days(timelib_sll *y, timelib_sll *m, timelib_sll *d)
105 {
106 timelib_sll leapyear;
107 timelib_sll days_this_month;
108 timelib_sll last_month, last_year;
109 timelib_sll days_last_month;
110
111 /* can jump an entire leap year period quickly */
112 if (*d >= DAYS_PER_LYEAR_PERIOD || *d <= -DAYS_PER_LYEAR_PERIOD) {
113 *y += YEARS_PER_LYEAR_PERIOD * (*d / DAYS_PER_LYEAR_PERIOD);
114 *d -= DAYS_PER_LYEAR_PERIOD * (*d / DAYS_PER_LYEAR_PERIOD);
115 }
116
117 do_range_limit(1, 13, 12, m, y);
118
119 leapyear = timelib_is_leap(*y);
120 days_this_month = leapyear ? days_in_month_leap[*m] : days_in_month[*m];
121 last_month = (*m) - 1;
122
123 if (last_month < 1) {
124 last_month += 12;
125 last_year = (*y) - 1;
126 } else {
127 last_year = (*y);
128 }
129 leapyear = timelib_is_leap(last_year);
130 days_last_month = leapyear ? days_in_month_leap[last_month] : days_in_month[last_month];
131
132 if (*d <= 0) {
133 *d += days_last_month;
134 (*m)--;
135 return 1;
136 }
137 if (*d > days_this_month) {
138 *d -= days_this_month;
139 (*m)++;
140 return 1;
141 }
142 return 0;
143 }
144
do_adjust_for_weekday(timelib_time * time)145 static void do_adjust_for_weekday(timelib_time* time)
146 {
147 timelib_sll current_dow, difference;
148
149 current_dow = timelib_day_of_week(time->y, time->m, time->d);
150 if (time->relative.weekday_behavior == 2)
151 {
152 if (time->relative.weekday == 0) {
153 time->relative.weekday = 7;
154 }
155 time->d -= current_dow;
156 time->d += time->relative.weekday;
157 return;
158 }
159 difference = time->relative.weekday - current_dow;
160 if ((time->relative.d < 0 && difference < 0) || (time->relative.d >= 0 && difference <= -time->relative.weekday_behavior)) {
161 difference += 7;
162 }
163 if (time->relative.weekday >= 0) {
164 time->d += difference;
165 } else {
166 time->d -= (7 - (abs(time->relative.weekday) - current_dow));
167 }
168 time->relative.have_weekday_relative = 0;
169 }
170
timelib_do_rel_normalize(timelib_time * base,timelib_rel_time * rt)171 void timelib_do_rel_normalize(timelib_time *base, timelib_rel_time *rt)
172 {
173 do {} while (do_range_limit(0, 60, 60, &rt->s, &rt->i));
174 do {} while (do_range_limit(0, 60, 60, &rt->i, &rt->h));
175 do {} while (do_range_limit(0, 24, 24, &rt->h, &rt->d));
176 do {} while (do_range_limit(0, 12, 12, &rt->m, &rt->y));
177
178 do_range_limit_days_relative(&base->y, &base->m, &rt->y, &rt->m, &rt->d, rt->invert);
179 do {} while (do_range_limit(0, 12, 12, &rt->m, &rt->y));
180 }
181
timelib_do_normalize(timelib_time * time)182 void timelib_do_normalize(timelib_time* time)
183 {
184 if (time->s != TIMELIB_UNSET) do {} while (do_range_limit(0, 60, 60, &time->s, &time->i));
185 if (time->s != TIMELIB_UNSET) do {} while (do_range_limit(0, 60, 60, &time->i, &time->h));
186 if (time->s != TIMELIB_UNSET) do {} while (do_range_limit(0, 24, 24, &time->h, &time->d));
187 do {} while (do_range_limit(1, 13, 12, &time->m, &time->y));
188
189 do {} while (do_range_limit_days(&time->y, &time->m, &time->d));
190 do {} while (do_range_limit(1, 13, 12, &time->m, &time->y));
191 }
192
do_adjust_relative(timelib_time * time)193 static void do_adjust_relative(timelib_time* time)
194 {
195 if (time->relative.have_weekday_relative) {
196 do_adjust_for_weekday(time);
197 }
198 timelib_do_normalize(time);
199
200 if (time->have_relative) {
201 time->s += time->relative.s;
202 time->i += time->relative.i;
203 time->h += time->relative.h;
204
205 time->d += time->relative.d;
206 time->m += time->relative.m;
207 time->y += time->relative.y;
208 }
209 switch (time->relative.first_last_day_of) {
210 case 1: /* first */
211 time->d = 1;
212 break;
213 case 2: /* last */
214 time->d = 0;
215 time->m++;
216 break;
217 }
218 timelib_do_normalize(time);
219 }
220
do_adjust_special_weekday(timelib_time * time)221 static void do_adjust_special_weekday(timelib_time* time)
222 {
223 timelib_sll current_dow, count;
224
225 count = time->relative.special.amount;
226
227 current_dow = timelib_day_of_week(time->y, time->m, time->d);
228 if (count == 0) {
229 /* skip over saturday and sunday */
230 if (current_dow == 6) {
231 time->d += 2;
232 }
233 /* skip over sunday */
234 if (current_dow == 0) {
235 time->d += 1;
236 }
237 } else if (count > 0) {
238 /* skip over saturday and sunday */
239 if (current_dow == 5) {
240 time->d += 2;
241 }
242 /* skip over sunday */
243 if (current_dow == 6) {
244 time->d += 1;
245 }
246 /* add increments of 5 weekdays as a week */
247 time->d += (count / 5) * 7;
248 /* if current DOW plus the remainder > 5, add two days */
249 current_dow = timelib_day_of_week(time->y, time->m, time->d);
250 time->d += (count % 5);
251 if ((count % 5) + current_dow > 5) {
252 time->d += 2;
253 }
254 } else if (count < 0) {
255 /* skip over sunday and saturday */
256 if (current_dow == 1) {
257 time->d -= 2;
258 }
259 /* skip over satruday */
260 if (current_dow == 0 ) {
261 time->d -= 1;
262 }
263 /* subtract increments of 5 weekdays as a week */
264 time->d += (count / 5) * 7;
265 /* if current DOW minus the remainder < 0, subtract two days */
266 current_dow = timelib_day_of_week(time->y, time->m, time->d);
267 time->d += (count % 5);
268 if ((count % 5) + current_dow < 1) {
269 time->d -= 2;
270 }
271 }
272 }
273
do_adjust_special(timelib_time * time)274 static void do_adjust_special(timelib_time* time)
275 {
276 if (time->relative.have_special_relative) {
277 switch (time->relative.special.type) {
278 case TIMELIB_SPECIAL_WEEKDAY:
279 do_adjust_special_weekday(time);
280 break;
281 }
282 }
283 timelib_do_normalize(time);
284 memset(&(time->relative.special), 0, sizeof(time->relative.special));
285 }
286
do_adjust_special_early(timelib_time * time)287 static void do_adjust_special_early(timelib_time* time)
288 {
289 if (time->relative.have_special_relative) {
290 switch (time->relative.special.type) {
291 case TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH:
292 time->d = 1;
293 time->m += time->relative.m;
294 time->relative.m = 0;
295 break;
296 case TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH:
297 time->d = 1;
298 time->m += time->relative.m + 1;
299 time->relative.m = 0;
300 break;
301 }
302 }
303 timelib_do_normalize(time);
304 }
305
do_years(timelib_sll year)306 static timelib_sll do_years(timelib_sll year)
307 {
308 timelib_sll i;
309 timelib_sll res = 0;
310 timelib_sll eras;
311
312 eras = (year - 1970) / 40000;
313 if (eras != 0) {
314 year = year - (eras * 40000);
315 res += (SECS_PER_ERA * eras * 100);
316 }
317
318 if (year >= 1970) {
319 for (i = year - 1; i >= 1970; i--) {
320 if (timelib_is_leap(i)) {
321 res += (DAYS_PER_LYEAR * SECS_PER_DAY);
322 } else {
323 res += (DAYS_PER_YEAR * SECS_PER_DAY);
324 }
325 }
326 } else {
327 for (i = 1969; i >= year; i--) {
328 if (timelib_is_leap(i)) {
329 res -= (DAYS_PER_LYEAR * SECS_PER_DAY);
330 } else {
331 res -= (DAYS_PER_YEAR * SECS_PER_DAY);
332 }
333 }
334 }
335 return res;
336 }
337
do_months(timelib_ull month,timelib_ull year)338 static timelib_sll do_months(timelib_ull month, timelib_ull year)
339 {
340 if (timelib_is_leap(year)) {
341 return ((month_tab_leap[month - 1] + 1) * SECS_PER_DAY);
342 } else {
343 return ((month_tab[month - 1]) * SECS_PER_DAY);
344 }
345 }
346
do_days(timelib_ull day)347 static timelib_sll do_days(timelib_ull day)
348 {
349 return ((day - 1) * SECS_PER_DAY);
350 }
351
do_time(timelib_ull hour,timelib_ull minute,timelib_ull second)352 static timelib_sll do_time(timelib_ull hour, timelib_ull minute, timelib_ull second)
353 {
354 timelib_sll res = 0;
355
356 res += hour * 3600;
357 res += minute * 60;
358 res += second;
359 return res;
360 }
361
do_adjust_timezone(timelib_time * tz,timelib_tzinfo * tzi)362 static timelib_sll do_adjust_timezone(timelib_time *tz, timelib_tzinfo *tzi)
363 {
364 switch (tz->zone_type) {
365 case TIMELIB_ZONETYPE_OFFSET:
366
367 tz->is_localtime = 1;
368 return tz->z * 60;
369 break;
370
371 case TIMELIB_ZONETYPE_ABBR: {
372 timelib_sll tmp;
373
374 tz->is_localtime = 1;
375 tmp = tz->z;
376 tmp -= tz->dst * 60;
377 tmp *= 60;
378 return tmp;
379 }
380 break;
381
382 case TIMELIB_ZONETYPE_ID:
383 tzi = tz->tz_info;
384 /* Break intentionally missing */
385
386 default:
387 /* No timezone in struct, fallback to reference if possible */
388 if (tzi) {
389 timelib_time_offset *before, *after;
390 timelib_sll tmp;
391 int in_transistion;
392
393 tz->is_localtime = 1;
394 before = timelib_get_time_zone_info(tz->sse, tzi);
395 after = timelib_get_time_zone_info(tz->sse - before->offset, tzi);
396 timelib_set_timezone(tz, tzi);
397
398 in_transistion = (
399 ((tz->sse - after->offset) >= (after->transistion_time + (before->offset - after->offset))) &&
400 ((tz->sse - after->offset) < after->transistion_time)
401 );
402
403 if ((before->offset != after->offset) && !in_transistion) {
404 tmp = -after->offset;
405 } else {
406 tmp = -tz->z;
407 }
408 timelib_time_offset_dtor(before);
409 timelib_time_offset_dtor(after);
410
411 {
412 timelib_time_offset *gmt_offset;
413
414 gmt_offset = timelib_get_time_zone_info(tz->sse + tmp, tzi);
415 tz->z = gmt_offset->offset;
416
417 tz->dst = gmt_offset->is_dst;
418 if (tz->tz_abbr) {
419 free(tz->tz_abbr);
420 }
421 tz->tz_abbr = strdup(gmt_offset->abbr);
422 timelib_time_offset_dtor(gmt_offset);
423 }
424 return tmp;
425 }
426 }
427 return 0;
428 }
429
timelib_update_ts(timelib_time * time,timelib_tzinfo * tzi)430 void timelib_update_ts(timelib_time* time, timelib_tzinfo* tzi)
431 {
432 timelib_sll res = 0;
433
434 do_adjust_special_early(time);
435 do_adjust_relative(time);
436 do_adjust_special(time);
437 res += do_years(time->y);
438 res += do_months(time->m, time->y);
439 res += do_days(time->d);
440 res += do_time(time->h, time->i, time->s);
441 time->sse = res;
442
443 res += do_adjust_timezone(time, tzi);
444 time->sse = res;
445
446 time->sse_uptodate = 1;
447 time->have_relative = time->relative.have_weekday_relative = time->relative.have_special_relative = 0;
448 }
449
450 #if 0
451 int main(void)
452 {
453 timelib_sll res;
454 timelib_time time;
455
456 time = timelib_strtotime("10 Feb 2005 06:07:03 PM CET"); /* 1108055223 */
457 printf ("%04d-%02d-%02d %02d:%02d:%02d.%-5d %+04d %1d",
458 time.y, time.m, time.d, time.h, time.i, time.s, time.f, time.z, time.dst);
459 if (time.have_relative) {
460 printf ("%3dY %3dM %3dD / %3dH %3dM %3dS",
461 time.relative.y, time.relative.m, time.relative.d, time.relative.h, time.relative.i, time.relative.s);
462 }
463 if (time.have_weekday_relative) {
464 printf (" / %d", time.relative.weekday);
465 }
466 res = time2unixtime(&time);
467 printf("%Ld\n", res);
468
469 return 0;
470 }
471 #endif
472