root/ext/date/lib/tm2unixtime.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. do_range_limit
  2. inc_month
  3. dec_month
  4. do_range_limit_days_relative
  5. do_range_limit_days
  6. do_adjust_for_weekday
  7. timelib_do_rel_normalize
  8. timelib_do_normalize
  9. do_adjust_relative
  10. do_adjust_special_weekday
  11. do_adjust_special
  12. do_adjust_special_early
  13. do_years
  14. do_months
  15. do_days
  16. do_time
  17. do_adjust_timezone
  18. timelib_update_ts
  19. main

   1 /*
   2  * The MIT License (MIT)
   3  *
   4  * Copyright (c) 2015 Derick Rethans
   5  *
   6  * Permission is hereby granted, free of charge, to any person obtaining a copy
   7  * of this software and associated documentation files (the "Software"), to deal
   8  * in the Software without restriction, including without limitation the rights
   9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10  * copies of the Software, and to permit persons to whom the Software is
  11  * furnished to do so, subject to the following conditions:
  12  *
  13  * The above copyright notice and this permission notice shall be included in
  14  * all copies or substantial portions of the Software.
  15  *
  16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22  * THE SOFTWARE.
  23  */
  24 
  25 #include "timelib.h"
  26 
  27 /*                                    jan  feb  mrt  apr  may  jun  jul  aug  sep  oct  nov  dec */
  28 static int month_tab_leap[12]     = {  -1,  30,  59,  90, 120, 151, 181, 212, 243, 273, 304, 334 };
  29 static int month_tab[12]          = {   0,  31,  59,  90, 120, 151, 181, 212, 243, 273, 304, 334 };
  30 
  31 /*                                    dec  jan  feb  mrt  apr  may  jun  jul  aug  sep  oct  nov  dec */
  32 static int days_in_month_leap[13] = {  31,  31,  29,  31,  30,  31,  30,  31,  31,  30,  31,  30,  31 };
  33 static int days_in_month[13]      = {  31,  31,  28,  31,  30,  31,  30,  31,  31,  30,  31,  30,  31 };
  34 
  35 static void do_range_limit(timelib_sll start, timelib_sll end, timelib_sll adj, timelib_sll *a, timelib_sll *b)
  36 {
  37         if (*a < start) {
  38                 *b -= (start - *a - 1) / adj + 1;
  39                 *a += adj * ((start - *a - 1) / adj + 1);
  40         }
  41         if (*a >= end) {
  42                 *b += *a / adj;
  43                 *a -= adj * (*a / adj);
  44         }
  45 }
  46 
  47 static void inc_month(timelib_sll *y, timelib_sll *m)
  48 {
  49         (*m)++;
  50         if (*m > 12) {
  51                 *m -= 12;
  52                 (*y)++;
  53         }
  54 }
  55 
  56 static void dec_month(timelib_sll *y, timelib_sll *m)
  57 {
  58         (*m)--;
  59         if (*m < 1) {
  60                 *m += 12;
  61                 (*y)--;
  62         }
  63 }
  64 
  65 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)
  66 {
  67         timelib_sll leapyear;
  68         timelib_sll month, year;
  69         timelib_sll days;
  70 
  71         do_range_limit(1, 13, 12, base_m, base_y);
  72 
  73         year = *base_y;
  74         month = *base_m;
  75 
  76 /*
  77         printf( "S: Y%d M%d   %d %d %d   %d\n", year, month, *y, *m, *d, days);
  78 */
  79         if (!invert) {
  80                 while (*d < 0) {
  81                         dec_month(&year, &month);
  82                         leapyear = timelib_is_leap(year);
  83                         days = leapyear ? days_in_month_leap[month] : days_in_month[month];
  84 
  85                         /* printf( "I  Y%d M%d   %d %d %d   %d\n", year, month, *y, *m, *d, days); */
  86 
  87                         *d += days;
  88                         (*m)--;
  89                 }
  90         } else {
  91                 while (*d < 0) {
  92                         leapyear = timelib_is_leap(year);
  93                         days = leapyear ? days_in_month_leap[month] : days_in_month[month];
  94 
  95                         /* printf( "I  Y%d M%d   %d %d %d   %d\n", year, month, *y, *m, *d, days); */
  96 
  97                         *d += days;
  98                         (*m)--;
  99                         inc_month(&year, &month);
 100                 }
 101         }
 102         /*
 103         printf( "E: Y%d M%d   %d %d %d   %d\n", year, month, *y, *m, *d, days);
 104         */
 105 }
 106 
 107 static int do_range_limit_days(timelib_sll *y, timelib_sll *m, timelib_sll *d)
 108 {
 109         timelib_sll leapyear;
 110         timelib_sll days_this_month;
 111         timelib_sll last_month, last_year;
 112         timelib_sll days_last_month;
 113 
 114         /* can jump an entire leap year period quickly */
 115         if (*d >= DAYS_PER_LYEAR_PERIOD || *d <= -DAYS_PER_LYEAR_PERIOD) {
 116                 *y += YEARS_PER_LYEAR_PERIOD * (*d / DAYS_PER_LYEAR_PERIOD);
 117                 *d -= DAYS_PER_LYEAR_PERIOD * (*d / DAYS_PER_LYEAR_PERIOD);
 118         }
 119 
 120         do_range_limit(1, 13, 12, m, y);
 121 
 122         leapyear = timelib_is_leap(*y);
 123         days_this_month = leapyear ? days_in_month_leap[*m] : days_in_month[*m];
 124         last_month = (*m) - 1;
 125 
 126         if (last_month < 1) {
 127                 last_month += 12;
 128                 last_year = (*y) - 1;
 129         } else {
 130                 last_year = (*y);
 131         }
 132         leapyear = timelib_is_leap(last_year);
 133         days_last_month = leapyear ? days_in_month_leap[last_month] : days_in_month[last_month];
 134 
 135         if (*d <= 0) {
 136                 *d += days_last_month;
 137                 (*m)--;
 138                 return 1;
 139         }
 140         if (*d > days_this_month) {
 141                 *d -= days_this_month;
 142                 (*m)++;
 143                 return 1;
 144         }
 145         return 0;
 146 }
 147 
 148 static void do_adjust_for_weekday(timelib_time* time)
 149 {
 150         timelib_sll current_dow, difference;
 151 
 152         current_dow = timelib_day_of_week(time->y, time->m, time->d);
 153         if (time->relative.weekday_behavior == 2)
 154         {
 155                 if (time->relative.weekday == 0) {
 156                         time->relative.weekday = 7;
 157                 }
 158                 time->d -= current_dow;
 159                 time->d += time->relative.weekday;
 160                 return;
 161         }
 162         difference = time->relative.weekday - current_dow;
 163         if ((time->relative.d < 0 && difference < 0) || (time->relative.d >= 0 && difference <= -time->relative.weekday_behavior)) {
 164                 difference += 7;
 165         }
 166         if (time->relative.weekday >= 0) {
 167                 time->d += difference;
 168         } else {
 169                 time->d -= (7 - (abs(time->relative.weekday) - current_dow));
 170         }
 171         time->relative.have_weekday_relative = 0;
 172 }
 173 
 174 void timelib_do_rel_normalize(timelib_time *base, timelib_rel_time *rt)
 175 {
 176         do_range_limit(0, 60, 60, &rt->s, &rt->i);
 177         do_range_limit(0, 60, 60, &rt->i, &rt->h);
 178         do_range_limit(0, 24, 24, &rt->h, &rt->d);
 179         do_range_limit(0, 12, 12, &rt->m, &rt->y);
 180 
 181         do_range_limit_days_relative(&base->y, &base->m, &rt->y, &rt->m, &rt->d, rt->invert);
 182         do_range_limit(0, 12, 12, &rt->m, &rt->y);
 183 }
 184 
 185 void timelib_do_normalize(timelib_time* time)
 186 {
 187         if (time->s != TIMELIB_UNSET) do_range_limit(0, 60, 60, &time->s, &time->i);
 188         if (time->s != TIMELIB_UNSET) do_range_limit(0, 60, 60, &time->i, &time->h);
 189         if (time->s != TIMELIB_UNSET) do_range_limit(0, 24, 24, &time->h, &time->d);
 190         do_range_limit(1, 13, 12, &time->m, &time->y);
 191 
 192         do {} while (do_range_limit_days(&time->y, &time->m, &time->d));
 193         do_range_limit(1, 13, 12, &time->m, &time->y);
 194 }
 195 
 196 static void do_adjust_relative(timelib_time* time)
 197 {
 198         if (time->relative.have_weekday_relative) {
 199                 do_adjust_for_weekday(time);
 200         }
 201         timelib_do_normalize(time);
 202 
 203         if (time->have_relative) {
 204                 time->s += time->relative.s;
 205                 time->i += time->relative.i;
 206                 time->h += time->relative.h;
 207 
 208                 time->d += time->relative.d;
 209                 time->m += time->relative.m;
 210                 time->y += time->relative.y;
 211         }
 212 
 213         switch (time->relative.first_last_day_of) {
 214                 case TIMELIB_SPECIAL_FIRST_DAY_OF_MONTH: /* first */
 215                         time->d = 1;
 216                         break;
 217                 case TIMELIB_SPECIAL_LAST_DAY_OF_MONTH: /* last */
 218                         time->d = 0;
 219                         time->m++;
 220                         break;
 221         }
 222 
 223         timelib_do_normalize(time);
 224 }
 225 
 226 static void do_adjust_special_weekday(timelib_time* time)
 227 {
 228         timelib_sll count, dow, rem;
 229 
 230         count = time->relative.special.amount;
 231         dow = timelib_day_of_week(time->y, time->m, time->d);
 232 
 233         /* Add increments of 5 weekdays as a week, leaving the DOW unchanged. */
 234         time->d += (count / 5) * 7;
 235 
 236         /* Deal with the remainder. */
 237         rem = (count % 5);
 238 
 239         if (count > 0) {
 240                 if (rem == 0) {
 241                         /* Head back to Friday if we stop on the weekend. */
 242                         if (dow == 0) {
 243                                 time->d -= 2;
 244                         } else if (dow == 6) {
 245                                 time->d -= 1;
 246                         }
 247                 } else if (dow == 6) {
 248                         /* We ended up on Saturday, but there's still work to do, so move
 249                          * to Sunday and continue from there. */
 250                         time->d += 1;
 251                 } else if (dow + rem > 5) {
 252                         /* We're on a weekday, but we're going past Friday, so skip right
 253                          * over the weekend. */
 254                         time->d += 2;
 255                 }
 256         } else {
 257                 /* Completely mirror the forward direction. This also covers the 0
 258                  * case, since if we start on the weekend, we want to move forward as
 259                  * if we stopped there while going backwards. */
 260                 if (rem == 0) {
 261                         if (dow == 6) {
 262                                 time->d += 2;
 263                         } else if (dow == 0) {
 264                                 time->d += 1;
 265                         }
 266                 } else if (dow == 0) {
 267                         time->d -= 1;
 268                 } else if (dow + rem < 1) {
 269                         time->d -= 2;
 270                 }
 271         }
 272 
 273         time->d += rem;
 274 }
 275 
 276 static void do_adjust_special(timelib_time* time)
 277 {
 278         if (time->relative.have_special_relative) {
 279                 switch (time->relative.special.type) {
 280                         case TIMELIB_SPECIAL_WEEKDAY:
 281                                 do_adjust_special_weekday(time);
 282                                 break;
 283                 }
 284         }
 285         timelib_do_normalize(time);
 286         memset(&(time->relative.special), 0, sizeof(time->relative.special));
 287 }
 288 
 289 static void do_adjust_special_early(timelib_time* time)
 290 {
 291         if (time->relative.have_special_relative) {
 292                 switch (time->relative.special.type) {
 293                         case TIMELIB_SPECIAL_DAY_OF_WEEK_IN_MONTH:
 294                                 time->d = 1;
 295                                 time->m += time->relative.m;
 296                                 time->relative.m = 0;
 297                                 break;
 298                         case TIMELIB_SPECIAL_LAST_DAY_OF_WEEK_IN_MONTH:
 299                                 time->d = 1;
 300                                 time->m += time->relative.m + 1;
 301                                 time->relative.m = 0;
 302                                 break;
 303                 }
 304         }
 305         switch (time->relative.first_last_day_of) {
 306                 case TIMELIB_SPECIAL_FIRST_DAY_OF_MONTH: /* first */
 307                         time->d = 1;
 308                         break;
 309                 case TIMELIB_SPECIAL_LAST_DAY_OF_MONTH: /* last */
 310                         time->d = 0;
 311                         time->m++;
 312                         break;
 313         }
 314         timelib_do_normalize(time);
 315 }
 316 
 317 static timelib_sll do_years(timelib_sll year)
 318 {
 319         timelib_sll i;
 320         timelib_sll res = 0;
 321         timelib_sll eras;
 322 
 323         eras = (year - 1970) / 40000;
 324         if (eras != 0) {
 325                 year = year - (eras * 40000);
 326                 res += (SECS_PER_ERA * eras * 100);
 327         }
 328 
 329         if (year >= 1970) {
 330                 for (i = year - 1; i >= 1970; i--) {
 331                         if (timelib_is_leap(i)) {
 332                                 res += (DAYS_PER_LYEAR * SECS_PER_DAY);
 333                         } else {
 334                                 res += (DAYS_PER_YEAR * SECS_PER_DAY);
 335                         }
 336                 }
 337         } else {
 338                 for (i = 1969; i >= year; i--) {
 339                         if (timelib_is_leap(i)) {
 340                                 res -= (DAYS_PER_LYEAR * SECS_PER_DAY);
 341                         } else {
 342                                 res -= (DAYS_PER_YEAR * SECS_PER_DAY);
 343                         }
 344                 }
 345         }
 346         return res;
 347 }
 348 
 349 static timelib_sll do_months(timelib_ull month, timelib_ull year)
 350 {
 351         if (timelib_is_leap(year)) {
 352                 return ((month_tab_leap[month - 1] + 1) * SECS_PER_DAY);
 353         } else {
 354                 return ((month_tab[month - 1]) * SECS_PER_DAY);
 355         }
 356 }
 357 
 358 static timelib_sll do_days(timelib_ull day)
 359 {
 360         return ((day - 1) * SECS_PER_DAY);
 361 }
 362 
 363 static timelib_sll do_time(timelib_ull hour, timelib_ull minute, timelib_ull second)
 364 {
 365         timelib_sll res = 0;
 366 
 367         res += hour * 3600;
 368         res += minute * 60;
 369         res += second;
 370         return res;
 371 }
 372 
 373 static timelib_sll do_adjust_timezone(timelib_time *tz, timelib_tzinfo *tzi)
 374 {
 375         switch (tz->zone_type) {
 376                 case TIMELIB_ZONETYPE_OFFSET:
 377 
 378                         tz->is_localtime = 1;
 379                         return tz->z * 60;
 380                         break;
 381 
 382                 case TIMELIB_ZONETYPE_ABBR: {
 383                         timelib_sll tmp;
 384 
 385                         tz->is_localtime = 1;
 386                         tmp = tz->z;
 387                         tmp -= tz->dst * 60;
 388                         tmp *= 60;
 389                         return tmp;
 390                         }
 391                         break;
 392 
 393                 case TIMELIB_ZONETYPE_ID:
 394                         tzi = tz->tz_info;
 395                         /* Break intentionally missing */
 396 
 397                 default:
 398                         /* No timezone in struct, fallback to reference if possible */
 399                         if (tzi) {
 400                                 timelib_time_offset *before, *after;
 401                                 timelib_sll          tmp;
 402                                 int                  in_transistion;
 403 
 404                                 tz->is_localtime = 1;
 405                                 before = timelib_get_time_zone_info(tz->sse, tzi);
 406                                 after = timelib_get_time_zone_info(tz->sse - before->offset, tzi);
 407                                 timelib_set_timezone(tz, tzi);
 408 
 409                                 in_transistion = (
 410                                         ((tz->sse - after->offset) >= (after->transistion_time + (before->offset - after->offset))) &&
 411                                         ((tz->sse - after->offset) < after->transistion_time)
 412                                 );
 413 
 414                                 if ((before->offset != after->offset) && !in_transistion) {
 415                                         tmp = -after->offset;
 416                                 } else {
 417                                         tmp = -tz->z;
 418                                 }
 419                                 timelib_time_offset_dtor(before);
 420                                 timelib_time_offset_dtor(after);
 421 
 422                                 {
 423                                         timelib_time_offset *gmt_offset;
 424 
 425                                         gmt_offset = timelib_get_time_zone_info(tz->sse + tmp, tzi);
 426                                         tz->z = gmt_offset->offset;
 427 
 428                                         tz->dst = gmt_offset->is_dst;
 429                                         if (tz->tz_abbr) {
 430                                                 timelib_free(tz->tz_abbr);
 431                                         }
 432                                         tz->tz_abbr = timelib_strdup(gmt_offset->abbr);
 433                                         timelib_time_offset_dtor(gmt_offset);
 434                                 }
 435                                 return tmp;
 436                         }
 437         }
 438         return 0;
 439 }
 440 
 441 void timelib_update_ts(timelib_time* time, timelib_tzinfo* tzi)
 442 {
 443         timelib_sll res = 0;
 444 
 445         do_adjust_special_early(time);
 446         do_adjust_relative(time);
 447         do_adjust_special(time);
 448         res += do_years(time->y);
 449         res += do_months(time->m, time->y);
 450         res += do_days(time->d);
 451         res += do_time(time->h, time->i, time->s);
 452         time->sse = res;
 453 
 454         res += do_adjust_timezone(time, tzi);
 455         time->sse = res;
 456 
 457         time->sse_uptodate = 1;
 458         time->have_relative = time->relative.have_weekday_relative = time->relative.have_special_relative = 0;
 459 }
 460 
 461 #if 0
 462 int main(void)
 463 {
 464         timelib_sll res;
 465         timelib_time time;
 466 
 467         time = timelib_strtotime("10 Feb 2005 06:07:03 PM CET"); /* 1108055223 */
 468         printf ("%04d-%02d-%02d %02d:%02d:%02d.%-5d %+04d %1d",
 469                 time.y, time.m, time.d, time.h, time.i, time.s, time.f, time.z, time.dst);
 470         if (time.have_relative) {
 471                 printf ("%3dY %3dM %3dD / %3dH %3dM %3dS",
 472                         time.relative.y, time.relative.m, time.relative.d, time.relative.h, time.relative.i, time.relative.s);
 473         }
 474         if (time.have_weekday_relative) {
 475                 printf (" / %d", time.relative.weekday);
 476         }
 477         res = time2unixtime(&time);
 478         printf("%Ld\n", res);
 479 
 480         return 0;
 481 }
 482 #endif

/* [<][>][^][v][top][bottom][index][help] */