00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00034 #include "shared/allocator.h"
00035 #include "shared/duration.h"
00036 #include "shared/log.h"
00037
00038 #include <stdio.h>
00039 #include <stdlib.h>
00040 #include <string.h>
00041 #include <time.h>
00042
00043 static const char* duration_str = "duration";
00044
00045
00050 duration_type*
00051 duration_create(void)
00052 {
00053 duration_type* duration;
00054 allocator_type* allocator = allocator_create(malloc, free);
00055 if (!allocator) {
00056 ods_log_error("[%s] cannot create: no allocator available",
00057 duration_str);
00058 return NULL;
00059 }
00060
00061 duration = (duration_type*) allocator_alloc(allocator,
00062 sizeof(duration_type));
00063 if (!duration) {
00064 ods_log_error("[%s] cannot create: allocator failed", duration_str);
00065 allocator_cleanup(allocator);
00066 return NULL;
00067 }
00068 duration->allocator = allocator;
00069 duration->years = 0;
00070 duration->months = 0;
00071 duration->weeks = 0;
00072 duration->days = 0;
00073 duration->hours = 0;
00074 duration->minutes = 0;
00075 duration->seconds = 0;
00076 return duration;
00077 }
00078
00079
00084 int
00085 duration_compare(duration_type* d1, duration_type* d2)
00086 {
00087 if (!d1 && !d2) {
00088 return 0;
00089 }
00090 if (!d1 || !d2) {
00091 return d1?-1:1;
00092 }
00093
00094 if (d1->years != d2->years) {
00095 return d1->years - d2->years;
00096 }
00097 if (d1->months != d2->months) {
00098 return d1->months - d2->months;
00099 }
00100 if (d1->weeks != d2->weeks) {
00101 return d1->weeks - d2->weeks;
00102 }
00103 if (d1->days != d2->days) {
00104 return d1->days - d2->days;
00105 }
00106 if (d1->hours != d2->hours) {
00107 return d1->hours - d2->hours;
00108 }
00109 if (d1->minutes != d2->minutes) {
00110 return d1->minutes - d2->minutes;
00111 }
00112 if (d1->seconds != d2->seconds) {
00113 return d1->seconds - d2->seconds;
00114 }
00115
00116 return 0;
00117 }
00118
00119
00124 duration_type*
00125 duration_create_from_string(const char* str)
00126 {
00127 duration_type* duration = duration_create();
00128 char* P, *X, *T, *W;
00129 int not_weeks = 0;
00130
00131 if (!duration) {
00132 ods_log_error("[%s] cannot create from string %s: create failed",
00133 duration_str, str);
00134 return NULL;
00135 }
00136 if (!str) {
00137 return duration;
00138 }
00139
00140 P = strchr(str, 'P');
00141 if (!P) {
00142 ods_log_error("[%s] cannot create from string %s: P not found",
00143 duration_str, str);
00144 duration_cleanup(duration);
00145 return NULL;
00146 }
00147
00148 T = strchr(str, 'T');
00149 X = strchr(str, 'Y');
00150 if (X) {
00151 duration->years = atoi(str+1);
00152 str = X;
00153 not_weeks = 1;
00154 }
00155 X = strchr(str, 'M');
00156 if (X && (!T || (size_t) (X-P) < (size_t) (T-P))) {
00157 duration->months = atoi(str+1);
00158 str = X;
00159 not_weeks = 1;
00160 }
00161 X = strchr(str, 'D');
00162 if (X) {
00163 duration->days = atoi(str+1);
00164 str = X;
00165 not_weeks = 1;
00166 }
00167 if (T) {
00168 str = T;
00169 not_weeks = 1;
00170 }
00171 X = strchr(str, 'H');
00172 if (X && T) {
00173 duration->hours = atoi(str+1);
00174 str = X;
00175 not_weeks = 1;
00176 }
00177 X = strrchr(str, 'M');
00178 if (X && T && (size_t) (X-P) > (size_t) (T-P)) {
00179 duration->minutes = atoi(str+1);
00180 str = X;
00181 not_weeks = 1;
00182 }
00183 X = strchr(str, 'S');
00184 if (X && T) {
00185 duration->seconds = atoi(str+1);
00186 str = X;
00187 not_weeks = 1;
00188 }
00189
00190 W = strchr(str, 'W');
00191 if (W) {
00192 if (not_weeks) {
00193 ods_log_error("[%s] cannot create from string: parse error",
00194 duration_str, P);
00195 duration_cleanup(duration);
00196 return NULL;
00197 } else {
00198 duration->weeks = atoi(str+1);
00199 str = W;
00200 }
00201 }
00202 return duration;
00203 }
00204
00205
00210 static size_t
00211 digits_in_number(time_t duration)
00212 {
00213 uint32_t period = (uint32_t) duration;
00214 size_t count = 0;
00215
00216 while (period > 0) {
00217 count++;
00218 period /= 10;
00219 }
00220 return count;
00221 }
00222
00223
00228 char*
00229 duration2string(duration_type* duration)
00230 {
00231 char* str = NULL, *num = NULL;
00232 size_t count = 2;
00233 int T = 0;
00234
00235 if (!duration) {
00236 return NULL;
00237 }
00238
00239 if (duration->years > 0) {
00240 count = count + 1 + digits_in_number(duration->years);
00241 }
00242 if (duration->months > 0) {
00243 count = count + 1 + digits_in_number(duration->months);
00244 }
00245 if (duration->weeks > 0) {
00246 count = count + 1 + digits_in_number(duration->weeks);
00247 }
00248 if (duration->days > 0) {
00249 count = count + 1 + digits_in_number(duration->days);
00250 }
00251 if (duration->hours > 0) {
00252 count = count + 1 + digits_in_number(duration->hours);
00253 T = 1;
00254 }
00255 if (duration->minutes > 0) {
00256 count = count + 1 + digits_in_number(duration->minutes);
00257 T = 1;
00258 }
00259 if (duration->seconds > 0) {
00260 count = count + 1 + digits_in_number(duration->seconds);
00261 T = 1;
00262 }
00263 if (T) {
00264 count++;
00265 }
00266
00267 str = (char*) calloc(count, sizeof(char));
00268 str[0] = 'P';
00269 str[1] = '\0';
00270
00271 if (duration->years > 0) {
00272 count = digits_in_number(duration->years);
00273 num = (char*) calloc(count+2, sizeof(char));
00274 snprintf(num, count+2, "%uY", (uint32_t) duration->years);
00275 str = strncat(str, num, count+2);
00276 free((void*) num);
00277 }
00278 if (duration->months > 0) {
00279 count = digits_in_number(duration->months);
00280 num = (char*) calloc(count+2, sizeof(char));
00281 snprintf(num, count+2, "%uM", (uint32_t) duration->months);
00282 str = strncat(str, num, count+2);
00283 free((void*) num);
00284 }
00285 if (duration->weeks > 0) {
00286 count = digits_in_number(duration->weeks);
00287 num = (char*) calloc(count+2, sizeof(char));
00288 snprintf(num, count+2, "%uW", (uint32_t) duration->weeks);
00289 str = strncat(str, num, count+2);
00290 free((void*) num);
00291 }
00292 if (duration->days > 0) {
00293 count = digits_in_number(duration->days);
00294 num = (char*) calloc(count+2, sizeof(char));
00295 snprintf(num, count+2, "%uD", (uint32_t) duration->days);
00296 str = strncat(str, num, count+2);
00297 free((void*) num);
00298 }
00299 if (T) {
00300 str = strncat(str, "T", 1);
00301 }
00302 if (duration->hours > 0) {
00303 count = digits_in_number(duration->hours);
00304 num = (char*) calloc(count+2, sizeof(char));
00305 snprintf(num, count+2, "%uH", (uint32_t) duration->hours);
00306 str = strncat(str, num, count+2);
00307 free((void*) num);
00308 }
00309 if (duration->minutes > 0) {
00310 count = digits_in_number(duration->minutes);
00311 num = (char*) calloc(count+2, sizeof(char));
00312 snprintf(num, count+2, "%uM", (uint32_t) duration->minutes);
00313 str = strncat(str, num, count+2);
00314 free((void*) num);
00315 }
00316 if (duration->seconds > 0) {
00317 count = digits_in_number(duration->seconds);
00318 num = (char*) calloc(count+2, sizeof(char));
00319 snprintf(num, count+2, "%uS", (uint32_t) duration->seconds);
00320 str = strncat(str, num, count+2);
00321 free((void*) num);
00322 }
00323 return str;
00324 }
00325
00326
00331 time_t
00332 duration2time(duration_type* duration)
00333 {
00334 time_t period = 0;
00335 char* dstr = NULL;
00336
00337 if (duration) {
00338 period += (duration->seconds);
00339 period += (duration->minutes)*60;
00340 period += (duration->hours)*3600;
00341 period += (duration->days)*86400;
00342 period += (duration->weeks)*86400*7;
00343 period += (duration->months)*86400*31;
00344 period += (duration->years)*86400*365;
00345
00346 if (duration->months || duration->years) {
00347
00348 dstr = duration2string(duration);
00349 ods_log_warning("[%s] converting duration %s to approximate value",
00350 duration_str, dstr?dstr:"(null)");
00351 free((void*) dstr);
00352 }
00353 }
00354 return period;
00355 }
00356
00361 time_t
00362 time_minimum(time_t a, time_t b)
00363 {
00364 return (a < b ? a : b);
00365 }
00366
00371 time_t
00372 time_maximum(time_t a, time_t b)
00373 {
00374 return (a > b ? a : b);
00375 }
00376
00377
00382 time_t
00383 ods_rand(time_t mod)
00384 {
00385 #ifdef HAVE_ARC4RANDOM_UNIFORM
00386 return (time_t) (arc4random_uniform((uint32_t) mod+1));
00387 #elif HAVE_ARC4RANDOM
00388 return (time_t) (arc4random() % (unsigned) mod+1);
00389 #else
00390 return (time_t) (random() % (unsigned) mod+1);
00391 #endif
00392 }
00393
00394
00395
00396 static const int mdays[] = {
00397 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
00398 };
00399
00400
00401 static int
00402 is_leap_year(int year)
00403 {
00404 return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
00405 }
00406
00407
00408 static int
00409 leap_days(int y1, int y2)
00410 {
00411 --y1;
00412 --y2;
00413 return (y2/4 - y1/4) - (y2/100 - y1/100) + (y2/400 - y1/400);
00414 }
00415
00416
00417
00418
00419
00420
00421 static time_t
00422 mktime_from_utc(const struct tm *tm)
00423 {
00424 int year = 1900 + tm->tm_year;
00425 time_t days = 365 * (year - 1970) + leap_days(1970, year);
00426 time_t hours;
00427 time_t minutes;
00428 time_t seconds;
00429 int i;
00430
00431 for (i = 0; i < tm->tm_mon; ++i) {
00432 days += mdays[i];
00433 }
00434 if (tm->tm_mon > 1 && is_leap_year(year)) {
00435 ++days;
00436 }
00437 days += tm->tm_mday - 1;
00438
00439 hours = days * 24 + tm->tm_hour;
00440 minutes = hours * 60 + tm->tm_min;
00441 seconds = minutes * 60 + tm->tm_sec;
00442
00443 return seconds;
00444 }
00445
00446
00451 static time_t
00452 timeshift2time(const char *time)
00453 {
00454
00455 struct tm tm;
00456 time_t timeshift = 0;
00457
00458
00459 if (strptime(time, "%Y%m%d%H%M%S", &tm)) {
00460 timeshift = mktime_from_utc(&tm);
00461 }
00462 return timeshift;
00463 }
00464
00465
00470 time_t
00471 time_now(void)
00472 {
00473 #ifdef ENFORCER_TIMESHIFT
00474 const char* env = getenv("ENFORCER_TIMESHIFT");
00475 if (env) {
00476 return timeshift2time(env);
00477 } else
00478 #endif
00479
00480 return time(NULL);
00481 }
00482
00483
00488 uint32_t
00489 time_datestamp(time_t tt, const char* format, char** str)
00490 {
00491 time_t t;
00492 struct tm *tmp;
00493 uint32_t ut = 0;
00494 char outstr[32];
00495
00496 if (tt) {
00497 t = tt;
00498 } else {
00499 t = time_now();
00500 }
00501
00502 tmp = localtime(&t);
00503 if (tmp == NULL) {
00504 ods_log_error("[%s] time_datestamp: localtime() failed", duration_str);
00505 return 0;
00506 }
00507
00508 if (strftime(outstr, sizeof(outstr), format, tmp) == 0) {
00509 ods_log_error("[%s] time_datestamp: strftime() failed", duration_str);
00510 return 0;
00511 }
00512
00513 ut = (uint32_t) strtoul(outstr, NULL, 10);
00514 if (str) {
00515 *str = strdup(outstr);
00516 }
00517 return ut;
00518 }
00519
00520 static void
00521 time_itoa_reverse(char* s)
00522 {
00523 int i, j;
00524 char c;
00525
00526 for (i = 0, j = strlen(s)-1; i<j; i++, j--) {
00527 c = s[i];
00528 s[i] = s[j];
00529 s[j] = c;
00530 }
00531 return;
00532 }
00533
00534
00539 void
00540 time_itoa(time_t n, char* s)
00541 {
00542 int i = 0;
00543
00544 do {
00545 s[i++] = n % 10 + '0';
00546 } while ((n /= 10) > 0);
00547 s[i] = '\0';
00548 time_itoa_reverse(s);
00549 return;
00550 }
00551
00552
00557 void
00558 duration_cleanup(duration_type* duration)
00559 {
00560 allocator_type* allocator;
00561
00562 if (!duration) {
00563 return;
00564 }
00565 allocator = duration->allocator;
00566 allocator_deallocate(allocator, (void*) duration);
00567 allocator_cleanup(allocator);
00568 return;
00569 }