Changes in uspace/lib/c/generic/time.c [1ab8539:bf9cb2f] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/c/generic/time.c
r1ab8539 rbf9cb2f 54 54 #include <malloc.h> 55 55 56 #define ASCTIME_BUF_LEN 26 57 58 #define HOURS_PER_DAY 24 59 #define MINS_PER_HOUR 60 60 #define SECS_PER_MIN 60 61 #define MINS_PER_DAY (MINS_PER_HOUR * HOURS_PER_DAY) 62 #define SECS_PER_HOUR (SECS_PER_MIN * MINS_PER_HOUR) 63 #define SECS_PER_DAY (SECS_PER_HOUR * HOURS_PER_DAY) 56 #define ASCTIME_BUF_LEN 26 64 57 65 58 /** Pointer to kernel shared variables with time */ … … 70 63 } *ktime = NULL; 71 64 72 static async_sess_t *clock_conn = NULL; 73 74 /** Check whether the year is a leap year. 65 /* Helper functions ***********************************************************/ 66 67 #define HOURS_PER_DAY (24) 68 #define MINS_PER_HOUR (60) 69 #define SECS_PER_MIN (60) 70 #define MINS_PER_DAY (MINS_PER_HOUR * HOURS_PER_DAY) 71 #define SECS_PER_HOUR (SECS_PER_MIN * MINS_PER_HOUR) 72 #define SECS_PER_DAY (SECS_PER_HOUR * HOURS_PER_DAY) 73 74 /** 75 * Checks whether the year is a leap year. 75 76 * 76 77 * @param year Year since 1900 (e.g. for 1970, the value is 70). 77 *78 78 * @return true if year is a leap year, false otherwise 79 * 80 */ 81 static bool is_leap_year(time_t year) 79 */ 80 static bool _is_leap_year(time_t year) 82 81 { 83 82 year += 1900; 84 83 85 84 if (year % 400 == 0) 86 85 return true; 87 88 86 if (year % 100 == 0) 89 87 return false; 90 91 88 if (year % 4 == 0) 92 89 return true; 93 94 90 return false; 95 91 } 96 92 97 /** How many days there are in the given month 98 * 99 * Return how many days there are in the given month of the given year. 93 /** 94 * Returns how many days there are in the given month of the given year. 100 95 * Note that year is only taken into account if month is February. 101 96 * 102 97 * @param year Year since 1900 (can be negative). 103 * @param mon Month of the year. 0 for January, 11 for December. 104 * 98 * @param mon Month of the year. 0 for January, 11 for December. 105 99 * @return Number of days in the specified month. 106 * 107 */ 108 static int days_in_month(time_t year, time_t mon) 109 { 110 assert(mon >= 0); 111 assert(mon <= 11); 112 113 static int month_days[] = { 114 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 115 }; 116 100 */ 101 static int _days_in_month(time_t year, time_t mon) 102 { 103 assert(mon >= 0 && mon <= 11); 104 105 static int month_days[] = 106 { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; 107 117 108 if (mon == 1) { 118 /* February */119 109 year += 1900; 120 return is_leap_year(year) ? 29 : 28;121 }122 123 return month_days[mon];124 }125 126 /** Which day of that year it is. 127 128 * For specified year, month and day of month, return which day of that year110 /* february */ 111 return _is_leap_year(year) ? 29 : 28; 112 } else { 113 return month_days[mon]; 114 } 115 } 116 117 /** 118 * For specified year, month and day of month, returns which day of that year 129 119 * it is. 130 120 * 131 121 * For example, given date 2011-01-03, the corresponding expression is: 132 * day_of_year(111, 0, 3) == 2122 * _day_of_year(111, 0, 3) == 2 133 123 * 134 124 * @param year Year (year 1900 = 0, can be negative). 135 * @param mon 125 * @param mon Month (January = 0). 136 126 * @param mday Day of month (First day is 1). 137 *138 127 * @return Day of year (First day is 0). 139 * 140 */ 141 static int day_of_year(time_t year, time_t mon, time_t mday) 142 { 143 static int mdays[] = { 144 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 145 }; 146 147 static int leap_mdays[] = { 148 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 149 }; 150 151 return (is_leap_year(year) ? leap_mdays[mon] : mdays[mon]) + mday - 1; 152 } 153 154 /** Integer division that rounds to negative infinity. 155 * 156 * Used by some functions in this module. 128 */ 129 static int _day_of_year(time_t year, time_t mon, time_t mday) 130 { 131 static int mdays[] = 132 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; 133 static int leap_mdays[] = 134 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }; 135 136 return (_is_leap_year(year) ? leap_mdays[mon] : mdays[mon]) + mday - 1; 137 } 138 139 /** 140 * Integer division that rounds to negative infinity. 141 * Used by some functions in this file. 157 142 * 158 143 * @param op1 Dividend. 159 144 * @param op2 Divisor. 160 *161 145 * @return Rounded quotient. 162 * 163 */ 164 static time_t floor_div(time_t op1, time_t op2) 165 { 166 if ((op1 >= 0) || (op1 % op2 == 0)) 146 */ 147 static time_t _floor_div(time_t op1, time_t op2) 148 { 149 if (op1 >= 0 || op1 % op2 == 0) { 167 150 return op1 / op2; 168 169 return op1 / op2 - 1; 170 } 171 172 /** Modulo that rounds to negative infinity. 173 * 174 * Used by some functions in this module. 151 } else { 152 return op1 / op2 - 1; 153 } 154 } 155 156 /** 157 * Modulo that rounds to negative infinity. 158 * Used by some functions in this file. 175 159 * 176 160 * @param op1 Dividend. 177 161 * @param op2 Divisor. 178 *179 162 * @return Remainder. 180 * 181 */ 182 static time_t floor_mod(time_t op1, time_t op2) 183 { 184 time_t div = floor_div(op1, op2); 185 186 /* 187 * (a / b) * b + a % b == a 188 * Thus: a % b == a - (a / b) * b 189 */ 190 191 time_t result = op1 - div * op2; 192 193 /* Some paranoid checking to ensure there is mistake here. */ 163 */ 164 static time_t _floor_mod(time_t op1, time_t op2) 165 { 166 int div = _floor_div(op1, op2); 167 168 /* (a / b) * b + a % b == a */ 169 /* thus, a % b == a - (a / b) * b */ 170 171 int result = op1 - div * op2; 172 173 /* Some paranoid checking to ensure I didn't make a mistake here. */ 194 174 assert(result >= 0); 195 175 assert(result < op2); … … 199 179 } 200 180 201 /** Number of days since the Epoch.202 * 181 /** 182 * Number of days since the Epoch. 203 183 * Epoch is 1970-01-01, which is also equal to day 0. 204 184 * 205 185 * @param year Year (year 1900 = 0, may be negative). 206 * @param mon 186 * @param mon Month (January = 0). 207 187 * @param mday Day of month (first day = 1). 208 *209 188 * @return Number of days since the Epoch. 210 * 211 */ 212 static time_t days_since_epoch(time_t year, time_t mon, time_t mday) 213 { 214 return (year - 70) * 365 + floor_div(year - 69, 4) - 215 floor_div(year - 1, 100) + floor_div(year + 299, 400) + 216 day_of_year(year, mon, mday); 217 } 218 219 /** Seconds since the Epoch. 220 * 221 * See also days_since_epoch(). 222 * 189 */ 190 static time_t _days_since_epoch(time_t year, time_t mon, time_t mday) 191 { 192 return (year - 70) * 365 + _floor_div(year - 69, 4) - 193 _floor_div(year - 1, 100) + _floor_div(year + 299, 400) + 194 _day_of_year(year, mon, mday); 195 } 196 197 /** 198 * Seconds since the Epoch. see also _days_since_epoch(). 199 * 223 200 * @param tm Normalized broken-down time. 224 *225 201 * @return Number of seconds since the epoch, not counting leap seconds. 226 * 227 */ 228 static time_t secs_since_epoch(const struct tm *tm) 229 { 230 return days_since_epoch(tm->tm_year, tm->tm_mon, tm->tm_mday) * 202 */ 203 static time_t _secs_since_epoch(const struct tm *tm) 204 { 205 return _days_since_epoch(tm->tm_year, tm->tm_mon, tm->tm_mday) * 231 206 SECS_PER_DAY + tm->tm_hour * SECS_PER_HOUR + 232 207 tm->tm_min * SECS_PER_MIN + tm->tm_sec; 233 208 } 234 209 235 /** Which day of week the specified date is. 236 * 210 /** 211 * Which day of week the specified date is. 212 * 237 213 * @param year Year (year 1900 = 0). 238 * @param mon 214 * @param mon Month (January = 0). 239 215 * @param mday Day of month (first = 1). 240 *241 216 * @return Day of week (Sunday = 0). 242 * 243 */ 244 static time_t day_of_week(time_t year, time_t mon, time_t mday) 217 */ 218 static int _day_of_week(time_t year, time_t mon, time_t mday) 245 219 { 246 220 /* 1970-01-01 is Thursday */ 247 return floor_mod(days_since_epoch(year, mon, mday) + 4, 7);248 } 249 250 /** Normalize the broken-down time.251 * 252 * Optionally add specified amount ofseconds.253 * 254 * @param tm 221 return _floor_mod((_days_since_epoch(year, mon, mday) + 4), 7); 222 } 223 224 /** 225 * Normalizes the broken-down time and optionally adds specified amount of 226 * seconds. 227 * 228 * @param tm Broken-down time to normalize. 255 229 * @param sec_add Seconds to add. 256 *257 230 * @return 0 on success, -1 on overflow 258 * 259 */ 260 static int normalize_time(struct tm *tm, time_t sec_add) 231 */ 232 static int _normalize_time(struct tm *tm, time_t sec_add) 261 233 { 262 234 // TODO: DST correction 263 235 264 236 /* Set initial values. */ 265 237 time_t sec = tm->tm_sec + sec_add; … … 269 241 time_t mon = tm->tm_mon; 270 242 time_t year = tm->tm_year; 271 243 272 244 /* Adjust time. */ 273 min += floor_div(sec, SECS_PER_MIN);274 sec = floor_mod(sec, SECS_PER_MIN);275 hour += floor_div(min, MINS_PER_HOUR);276 min = floor_mod(min, MINS_PER_HOUR);277 day += floor_div(hour, HOURS_PER_DAY);278 hour = floor_mod(hour, HOURS_PER_DAY);279 245 min += _floor_div(sec, SECS_PER_MIN); 246 sec = _floor_mod(sec, SECS_PER_MIN); 247 hour += _floor_div(min, MINS_PER_HOUR); 248 min = _floor_mod(min, MINS_PER_HOUR); 249 day += _floor_div(hour, HOURS_PER_DAY); 250 hour = _floor_mod(hour, HOURS_PER_DAY); 251 280 252 /* Adjust month. */ 281 year += floor_div(mon, 12);282 mon = floor_mod(mon, 12);283 253 year += _floor_div(mon, 12); 254 mon = _floor_mod(mon, 12); 255 284 256 /* Now the difficult part - days of month. */ 285 257 286 258 /* First, deal with whole cycles of 400 years = 146097 days. */ 287 year += floor_div(day, 146097) * 400;288 day = floor_mod(day, 146097);259 year += _floor_div(day, 146097) * 400; 260 day = _floor_mod(day, 146097); 289 261 290 262 /* Then, go in one year steps. */ … … 292 264 /* January and February. */ 293 265 while (day > 365) { 294 day -= is_leap_year(year) ? 366 : 365;266 day -= _is_leap_year(year) ? 366 : 365; 295 267 year++; 296 268 } … … 298 270 /* Rest of the year. */ 299 271 while (day > 365) { 300 day -= is_leap_year(year + 1) ? 366 : 365;272 day -= _is_leap_year(year + 1) ? 366 : 365; 301 273 year++; 302 274 } … … 304 276 305 277 /* Finally, finish it off month per month. */ 306 while (day >= days_in_month(year, mon)) {307 day -= days_in_month(year, mon);278 while (day >= _days_in_month(year, mon)) { 279 day -= _days_in_month(year, mon); 308 280 mon++; 309 310 281 if (mon >= 12) { 311 282 mon -= 12; … … 315 286 316 287 /* Calculate the remaining two fields. */ 317 tm->tm_yday = day_of_year(year, mon, day + 1);318 tm->tm_wday = day_of_week(year, mon, day + 1);288 tm->tm_yday = _day_of_year(year, mon, day + 1); 289 tm->tm_wday = _day_of_week(year, mon, day + 1); 319 290 320 291 /* And put the values back to the struct. */ … … 325 296 tm->tm_mon = (int) mon; 326 297 327 /* Casts to work around POSIXbrain-damage. */328 if (year > ((int) INT_MAX) || year < ((int)INT_MIN)) {329 tm->tm_year = (year < 0) ? ((int) INT_MIN) : ((int)INT_MAX);298 /* Casts to work around libc brain-damage. */ 299 if (year > ((int)INT_MAX) || year < ((int)INT_MIN)) { 300 tm->tm_year = (year < 0) ? ((int)INT_MIN) : ((int)INT_MAX); 330 301 return -1; 331 302 } … … 335 306 } 336 307 337 /** Which day the week-based year starts on. 338 * 339 * Relative to the first calendar day. E.g. if the year starts 340 * on December 31st, the return value is -1. 308 /** 309 * Which day the week-based year starts on, relative to the first calendar day. 310 * E.g. if the year starts on December 31st, the return value is -1. 341 311 * 342 312 * @param Year since 1900. 343 *344 313 * @return Offset of week-based year relative to calendar year. 345 * 346 */ 347 static int wbyear_offset(int year) 348 { 349 int start_wday = day_of_week(year, 0, 1); 350 351 return floor_mod(4 - start_wday, 7) - 3; 352 } 353 354 /** Week-based year of the specified time. 314 */ 315 static int _wbyear_offset(int year) 316 { 317 int start_wday = _day_of_week(year, 0, 1); 318 return _floor_mod(4 - start_wday, 7) - 3; 319 } 320 321 /** 322 * Returns week-based year of the specified time. 355 323 * 356 324 * @param tm Normalized broken-down time. 357 *358 325 * @return Week-based year. 359 * 360 */ 361 static int wbyear(const struct tm *tm) 362 { 363 int day = tm->tm_yday - wbyear_offset(tm->tm_year); 364 326 */ 327 static int _wbyear(const struct tm *tm) 328 { 329 int day = tm->tm_yday - _wbyear_offset(tm->tm_year); 365 330 if (day < 0) { 366 331 /* Last week of previous year. */ 367 332 return tm->tm_year - 1; 368 333 } 369 370 if (day > 364 + is_leap_year(tm->tm_year)) { 334 if (day > 364 + _is_leap_year(tm->tm_year)) { 371 335 /* First week of next year. */ 372 336 return tm->tm_year + 1; 373 337 } 374 375 338 /* All the other days are in the calendar year. */ 376 339 return tm->tm_year; 377 340 } 378 341 379 /** Week number of the year (assuming weeks start on Sunday).380 * 342 /** 343 * Week number of the year, assuming weeks start on sunday. 381 344 * The first Sunday of January is the first day of week 1; 382 345 * days in the new year before this are in week 0. 383 346 * 384 347 * @param tm Normalized broken-down time. 385 *386 348 * @return The week number (0 - 53). 387 * 388 */ 389 static int sun_week_number(const struct tm *tm) 390 { 391 int first_day = (7 - day_of_week(tm->tm_year, 0, 1)) % 7; 392 349 */ 350 static int _sun_week_number(const struct tm *tm) 351 { 352 int first_day = (7 - _day_of_week(tm->tm_year, 0, 1)) % 7; 393 353 return (tm->tm_yday - first_day + 7) / 7; 394 354 } 395 355 396 /** Week number of the year (assuming weeks start on Monday). 397 * 398 * If the week containing January 1st has four or more days 399 * in the new year, then it is considered week 1. Otherwise, 400 * it is the last week of the previous year, and the next week 401 * is week 1. Both January 4th and the first Thursday 356 /** 357 * Week number of the year, assuming weeks start on monday. 358 * If the week containing January 1st has four or more days in the new year, 359 * then it is considered week 1. Otherwise, it is the last week of the previous 360 * year, and the next week is week 1. Both January 4th and the first Thursday 402 361 * of January are always in week 1. 403 362 * 404 363 * @param tm Normalized broken-down time. 405 *406 364 * @return The week number (1 - 53). 407 * 408 */ 409 static int iso_week_number(const struct tm *tm) 410 { 411 int day = tm->tm_yday - wbyear_offset(tm->tm_year); 412 365 */ 366 static int _iso_week_number(const struct tm *tm) 367 { 368 int day = tm->tm_yday - _wbyear_offset(tm->tm_year); 413 369 if (day < 0) { 414 370 /* Last week of previous year. */ 415 371 return 53; 416 372 } 417 418 if (day > 364 + is_leap_year(tm->tm_year)) { 373 if (day > 364 + _is_leap_year(tm->tm_year)) { 419 374 /* First week of next year. */ 420 375 return 1; 421 376 } 422 423 377 /* All the other days give correct answer. */ 424 378 return (day / 7 + 1); 425 379 } 426 380 427 /** Week number of the year (assuming weeks start on Monday).428 * 381 /** 382 * Week number of the year, assuming weeks start on monday. 429 383 * The first Monday of January is the first day of week 1; 430 * days in the new year before this are in week 0. 384 * days in the new year before this are in week 0. 431 385 * 432 386 * @param tm Normalized broken-down time. 433 *434 387 * @return The week number (0 - 53). 435 * 436 */ 437 static int mon_week_number(const struct tm *tm) 438 { 439 int first_day = (1 - day_of_week(tm->tm_year, 0, 1)) % 7; 440 388 */ 389 static int _mon_week_number(const struct tm *tm) 390 { 391 int first_day = (1 - _day_of_week(tm->tm_year, 0, 1)) % 7; 441 392 return (tm->tm_yday - first_day + 7) / 7; 442 393 } 394 395 /******************************************************************************/ 396 443 397 444 398 /** Add microseconds to given timeval. … … 514 468 } 515 469 516 /** Get time of day .470 /** Get time of day 517 471 * 518 472 * The time variables are memory mapped (read-only) from kernel which … … 528 482 * 529 483 */ 530 void gettimeofday(struct timeval *tv, struct timezone *tz) 531 { 484 int gettimeofday(struct timeval *tv, struct timezone *tz) 485 { 486 int rc; 487 struct tm t; 488 category_id_t cat_id; 489 size_t svc_cnt; 490 service_id_t *svc_ids = NULL; 491 service_id_t svc_id; 492 char *svc_name = NULL; 493 494 static async_sess_t *clock_conn = NULL; 495 532 496 if (tz) { 533 497 tz->tz_minuteswest = 0; 534 498 tz->tz_dsttime = DST_NONE; 535 499 } 536 500 537 501 if (clock_conn == NULL) { 538 category_id_t cat_id; 539 int rc = loc_category_get_id("clock", &cat_id, IPC_FLAG_BLOCKING); 502 rc = loc_category_get_id("clock", &cat_id, IPC_FLAG_BLOCKING); 540 503 if (rc != EOK) 541 goto fallback; 542 543 service_id_t *svc_ids; 544 size_t svc_cnt; 504 goto ret_uptime; 505 545 506 rc = loc_category_get_svcs(cat_id, &svc_ids, &svc_cnt); 546 507 if (rc != EOK) 547 goto fallback;548 508 goto ret_uptime; 509 549 510 if (svc_cnt == 0) 550 goto fallback; 551 552 char *svc_name; 511 goto ret_uptime; 512 553 513 rc = loc_service_get_name(svc_ids[0], &svc_name); 554 free(svc_ids);555 514 if (rc != EOK) 556 goto fallback; 557 558 service_id_t svc_id; 515 goto ret_uptime; 516 559 517 rc = loc_service_get_id(svc_name, &svc_id, 0); 560 free(svc_name);561 518 if (rc != EOK) 562 goto fallback;563 519 goto ret_uptime; 520 564 521 clock_conn = loc_service_connect(EXCHANGE_SERIALIZE, 565 522 svc_id, IPC_FLAG_BLOCKING); 566 523 if (!clock_conn) 567 goto fallback; 568 } 569 570 struct tm time; 571 int rc = clock_dev_time_get(clock_conn, &time); 524 goto ret_uptime; 525 } 526 527 rc = clock_dev_time_get(clock_conn, &t); 572 528 if (rc != EOK) 573 goto fallback;574 529 goto ret_uptime; 530 575 531 tv->tv_usec = 0; 576 tv->tv_sec = mktime(&time); 577 578 return; 579 580 fallback: 581 getuptime(tv); 582 } 583 584 void getuptime(struct timeval *tv) 532 tv->tv_sec = mktime(&t); 533 534 free(svc_name); 535 free(svc_ids); 536 537 return EOK; 538 539 ret_uptime: 540 541 free(svc_name); 542 free(svc_ids); 543 544 return getuptime(tv); 545 } 546 547 int getuptime(struct timeval *tv) 585 548 { 586 549 if (ktime == NULL) { … … 589 552 if (rc != EOK) { 590 553 errno = rc; 591 goto fallback;554 return -1; 592 555 } 593 556 … … 598 561 as_area_destroy(addr); 599 562 errno = rc; 600 goto fallback;563 return -1; 601 564 } 602 565 … … 617 580 } else 618 581 tv->tv_sec = s1; 619 620 return; 621 622 fallback: 623 tv->tv_sec = 0; 624 tv->tv_usec = 0; 582 583 return 0; 625 584 } 626 585 … … 628 587 { 629 588 struct timeval tv; 630 gettimeofday(&tv, NULL); 589 if (gettimeofday(&tv, NULL)) 590 return (time_t) -1; 631 591 632 592 if (tloc) … … 671 631 } 672 632 673 /** Get time from broken-down time. 674 * 675 * First normalize the provided broken-down time 676 * (moves all values to their proper bounds) and 677 * then try to calculate the appropriate time_t 678 * representation. 633 /** 634 * This function first normalizes the provided broken-down time 635 * (moves all values to their proper bounds) and then tries to 636 * calculate the appropriate time_t representation. 679 637 * 680 638 * @param tm Broken-down time. 681 * 682 * @return time_t representation of the time. 683 * @return Undefined value on overflow. 684 * 639 * @return time_t representation of the time, undefined value on overflow. 685 640 */ 686 641 time_t mktime(struct tm *tm) … … 688 643 // TODO: take DST flag into account 689 644 // TODO: detect overflow 690 691 normalize_time(tm, 0); 692 return secs_since_epoch(tm); 693 } 694 695 /* 696 * FIXME: This requires POSIX-correct snprintf. 697 * Otherwise it won't work with non-ASCII chars. 698 */ 699 #define APPEND(...) \ 700 { \ 701 consumed = snprintf(ptr, remaining, __VA_ARGS__); \ 702 if (consumed >= remaining) \ 703 return 0; \ 704 \ 705 ptr += consumed; \ 706 remaining -= consumed; \ 707 } 708 709 #define RECURSE(fmt) \ 710 { \ 711 consumed = strftime(ptr, remaining, fmt, tm); \ 712 if (consumed == 0) \ 713 return 0; \ 714 \ 715 ptr += consumed; \ 716 remaining -= consumed; \ 717 } 718 719 #define TO_12H(hour) \ 720 (((hour) > 12) ? ((hour) - 12) : \ 721 (((hour) == 0) ? 12 : (hour))) 722 723 /** Convert time and date to a string. 724 * 725 * @param s Buffer to write string to. 645 646 _normalize_time(tm, 0); 647 return _secs_since_epoch(tm); 648 } 649 650 /** 651 * Convert time and date to a string, based on a specified format and 652 * current locale. 653 * 654 * @param s Buffer to write string to. 726 655 * @param maxsize Size of the buffer. 727 * @param format Format of the output. 728 * @param tm Broken-down time to format. 729 * 656 * @param format Format of the output. 657 * @param tm Broken-down time to format. 730 658 * @return Number of bytes written. 731 *732 659 */ 733 660 size_t strftime(char *restrict s, size_t maxsize, … … 737 664 assert(format != NULL); 738 665 assert(tm != NULL); 739 666 740 667 // TODO: use locale 741 742 668 static const char *wday_abbr[] = { 743 669 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 744 670 }; 745 746 671 static const char *wday[] = { 747 672 "Sunday", "Monday", "Tuesday", "Wednesday", 748 673 "Thursday", "Friday", "Saturday" 749 674 }; 750 751 675 static const char *mon_abbr[] = { 752 676 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 753 677 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 754 678 }; 755 756 679 static const char *mon[] = { 757 680 "January", "February", "March", "April", "May", "June", "July", … … 759 682 }; 760 683 761 if (maxsize < 1) 684 if (maxsize < 1) { 762 685 return 0; 686 } 763 687 764 688 char *ptr = s; … … 766 690 size_t remaining = maxsize; 767 691 692 #define append(...) { \ 693 /* FIXME: this requires POSIX-correct snprintf */ \ 694 /* otherwise it won't work with non-ascii chars */ \ 695 consumed = snprintf(ptr, remaining, __VA_ARGS__); \ 696 if (consumed >= remaining) { \ 697 return 0; \ 698 } \ 699 ptr += consumed; \ 700 remaining -= consumed; \ 701 } 702 703 #define recurse(fmt) { \ 704 consumed = strftime(ptr, remaining, fmt, tm); \ 705 if (consumed == 0) { \ 706 return 0; \ 707 } \ 708 ptr += consumed; \ 709 remaining -= consumed; \ 710 } 711 712 #define TO_12H(hour) (((hour) > 12) ? ((hour) - 12) : \ 713 (((hour) == 0) ? 12 : (hour))) 714 768 715 while (*format != '\0') { 769 716 if (*format != '%') { 770 APPEND("%c", *format);717 append("%c", *format); 771 718 format++; 772 719 continue; … … 774 721 775 722 format++; 776 if ( (*format == '0') || (*format == '+')) {723 if (*format == '0' || *format == '+') { 777 724 // TODO: padding 778 725 format++; 779 726 } 780 781 727 while (isdigit(*format)) { 782 728 // TODO: padding 783 729 format++; 784 730 } 785 786 if ((*format == 'O') || (*format == 'E')) { 731 if (*format == 'O' || *format == 'E') { 787 732 // TODO: locale's alternative format 788 733 format++; … … 791 736 switch (*format) { 792 737 case 'a': 793 APPEND("%s", wday_abbr[tm->tm_wday]); 794 break; 738 append("%s", wday_abbr[tm->tm_wday]); break; 795 739 case 'A': 796 APPEND("%s", wday[tm->tm_wday]); 797 break; 740 append("%s", wday[tm->tm_wday]); break; 798 741 case 'b': 799 APPEND("%s", mon_abbr[tm->tm_mon]); 800 break; 742 append("%s", mon_abbr[tm->tm_mon]); break; 801 743 case 'B': 802 APPEND("%s", mon[tm->tm_mon]); 803 break; 744 append("%s", mon[tm->tm_mon]); break; 804 745 case 'c': 805 746 // TODO: locale-specific datetime format 806 RECURSE("%Y-%m-%d %H:%M:%S"); 807 break; 747 recurse("%Y-%m-%d %H:%M:%S"); break; 808 748 case 'C': 809 APPEND("%02d", (1900 + tm->tm_year) / 100); 810 break; 749 append("%02d", (1900 + tm->tm_year) / 100); break; 811 750 case 'd': 812 APPEND("%02d", tm->tm_mday); 813 break; 751 append("%02d", tm->tm_mday); break; 814 752 case 'D': 815 RECURSE("%m/%d/%y"); 816 break; 753 recurse("%m/%d/%y"); break; 817 754 case 'e': 818 APPEND("%2d", tm->tm_mday); 819 break; 755 append("%2d", tm->tm_mday); break; 820 756 case 'F': 821 RECURSE("%+4Y-%m-%d"); 822 break; 757 recurse("%+4Y-%m-%d"); break; 823 758 case 'g': 824 APPEND("%02d", wbyear(tm) % 100); 825 break; 759 append("%02d", _wbyear(tm) % 100); break; 826 760 case 'G': 827 APPEND("%d", wbyear(tm)); 828 break; 761 append("%d", _wbyear(tm)); break; 829 762 case 'h': 830 RECURSE("%b"); 831 break; 763 recurse("%b"); break; 832 764 case 'H': 833 APPEND("%02d", tm->tm_hour); 834 break; 765 append("%02d", tm->tm_hour); break; 835 766 case 'I': 836 APPEND("%02d", TO_12H(tm->tm_hour)); 837 break; 767 append("%02d", TO_12H(tm->tm_hour)); break; 838 768 case 'j': 839 APPEND("%03d", tm->tm_yday); 840 break; 769 append("%03d", tm->tm_yday); break; 841 770 case 'k': 842 APPEND("%2d", tm->tm_hour); 843 break; 771 append("%2d", tm->tm_hour); break; 844 772 case 'l': 845 APPEND("%2d", TO_12H(tm->tm_hour)); 846 break; 773 append("%2d", TO_12H(tm->tm_hour)); break; 847 774 case 'm': 848 APPEND("%02d", tm->tm_mon); 849 break; 775 append("%02d", tm->tm_mon); break; 850 776 case 'M': 851 APPEND("%02d", tm->tm_min); 852 break; 777 append("%02d", tm->tm_min); break; 853 778 case 'n': 854 APPEND("\n"); 855 break; 779 append("\n"); break; 856 780 case 'p': 857 APPEND("%s", tm->tm_hour < 12 ? "AM" : "PM"); 858 break; 781 append("%s", tm->tm_hour < 12 ? "AM" : "PM"); break; 859 782 case 'P': 860 APPEND("%s", tm->tm_hour < 12 ? "am" : "PM"); 861 break; 783 append("%s", tm->tm_hour < 12 ? "am" : "PM"); break; 862 784 case 'r': 863 RECURSE("%I:%M:%S %p"); 864 break; 785 recurse("%I:%M:%S %p"); break; 865 786 case 'R': 866 RECURSE("%H:%M"); 867 break; 787 recurse("%H:%M"); break; 868 788 case 's': 869 APPEND("%ld", secs_since_epoch(tm)); 870 break; 789 append("%ld", _secs_since_epoch(tm)); break; 871 790 case 'S': 872 APPEND("%02d", tm->tm_sec); 873 break; 791 append("%02d", tm->tm_sec); break; 874 792 case 't': 875 APPEND("\t"); 876 break; 793 append("\t"); break; 877 794 case 'T': 878 RECURSE("%H:%M:%S"); 879 break; 795 recurse("%H:%M:%S"); break; 880 796 case 'u': 881 APPEND("%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);797 append("%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday); 882 798 break; 883 799 case 'U': 884 APPEND("%02d", sun_week_number(tm)); 885 break; 800 append("%02d", _sun_week_number(tm)); break; 886 801 case 'V': 887 APPEND("%02d", iso_week_number(tm)); 888 break; 802 append("%02d", _iso_week_number(tm)); break; 889 803 case 'w': 890 APPEND("%d", tm->tm_wday); 891 break; 804 append("%d", tm->tm_wday); break; 892 805 case 'W': 893 APPEND("%02d", mon_week_number(tm)); 894 break; 806 append("%02d", _mon_week_number(tm)); break; 895 807 case 'x': 896 808 // TODO: locale-specific date format 897 RECURSE("%Y-%m-%d"); 898 break; 809 recurse("%Y-%m-%d"); break; 899 810 case 'X': 900 811 // TODO: locale-specific time format 901 RECURSE("%H:%M:%S"); 902 break; 812 recurse("%H:%M:%S"); break; 903 813 case 'y': 904 APPEND("%02d", tm->tm_year % 100); 905 break; 814 append("%02d", tm->tm_year % 100); break; 906 815 case 'Y': 907 APPEND("%d", 1900 + tm->tm_year); 908 break; 816 append("%d", 1900 + tm->tm_year); break; 909 817 case 'z': 910 818 // TODO: timezone … … 914 822 break; 915 823 case '%': 916 APPEND("%%");824 append("%%"); 917 825 break; 918 826 default: 919 827 /* Invalid specifier, print verbatim. */ 920 while (*format != '%') 828 while (*format != '%') { 921 829 format--; 922 923 APPEND("%%");830 } 831 append("%%"); 924 832 break; 925 833 } 926 927 834 format++; 928 835 } 929 836 837 #undef append 838 #undef recurse 839 930 840 return maxsize - remaining; 931 841 } 932 842 933 /** Convert a time value to a broken-down UTC time/ 934 * 935 * @param time Time to convert936 * @param result Structure to store the result to937 * 938 * @return EOK or a negative error code939 * 843 844 /** Converts a time value to a broken-down UTC time 845 * 846 * @param time Time to convert 847 * @param result Structure to store the result to 848 * 849 * @return EOK or a negative error code 940 850 */ 941 851 int time_utc2tm(const time_t time, struct tm *restrict result) 942 852 { 943 853 assert(result != NULL); 944 854 945 855 /* Set result to epoch. */ 946 856 result->tm_sec = 0; … … 950 860 result->tm_mon = 0; 951 861 result->tm_year = 70; /* 1970 */ 952 953 if ( normalize_time(result, time) == -1)862 863 if (_normalize_time(result, time) == -1) 954 864 return EOVERFLOW; 955 865 956 866 return EOK; 957 867 } 958 868 959 /** Convert a time value to a NULL-terminated string. 960 * 961 * The format is "Wed Jun 30 21:49:08 1993\n" expressed in UTC. 962 * 963 * @param time Time to convert. 964 * @param buf Buffer to store the string to, must be at least 965 * ASCTIME_BUF_LEN bytes long. 966 * 967 * @return EOK or a negative error code. 968 * 869 /** Converts a time value to a null terminated string of the form 870 * "Wed Jun 30 21:49:08 1993\n" expressed in UTC. 871 * 872 * @param time Time to convert. 873 * @param buf Buffer to store the string to, must be at least 874 * ASCTIME_BUF_LEN bytes long. 875 * 876 * @return EOK or a negative error code. 969 877 */ 970 878 int time_utc2str(const time_t time, char *restrict buf) 971 879 { 972 struct tm tm; 973 int ret = time_utc2tm(time, &tm); 974 if (ret != EOK) 975 return ret; 976 977 time_tm2str(&tm, buf); 880 struct tm t; 881 int r; 882 883 if ((r = time_utc2tm(time, &t)) != EOK) 884 return r; 885 886 time_tm2str(&t, buf); 978 887 return EOK; 979 888 } 980 889 981 /** Convert broken-down time to a NULL-terminated string. 982 * 983 * The format is "Sun Jan 1 00:00:00 1970\n". (Obsolete) 890 891 /** 892 * Converts broken-down time to a string in format 893 * "Sun Jan 1 00:00:00 1970\n". (Obsolete) 984 894 * 985 895 * @param timeptr Broken-down time structure. 986 * @param buf Buffer to store string to, must be at least 987 * ASCTIME_BUF_LEN bytes long. 988 * 896 * @param buf Buffer to store string to, must be at least ASCTIME_BUF_LEN 897 * bytes long. 989 898 */ 990 899 void time_tm2str(const struct tm *restrict timeptr, char *restrict buf) … … 992 901 assert(timeptr != NULL); 993 902 assert(buf != NULL); 994 903 995 904 static const char *wday[] = { 996 905 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 997 906 }; 998 999 907 static const char *mon[] = { 1000 908 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 1001 909 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 1002 910 }; 1003 911 1004 912 snprintf(buf, ASCTIME_BUF_LEN, "%s %s %2d %02d:%02d:%02d %d\n", 1005 913 wday[timeptr->tm_wday], … … 1010 918 } 1011 919 1012 /** Converts a time value to a broken-down local time. 1013 * 1014 * Time is expressed relative to the user's specified timezone. 1015 * 1016 * @param timer Time to convert. 1017 * @param result Structure to store the result to. 1018 * 1019 * @return EOK on success or a negative error code. 1020 * 920 /** 921 * Converts a time value to a broken-down local time, expressed relative 922 * to the user's specified timezone. 923 * 924 * @param timer Time to convert. 925 * @param result Structure to store the result to. 926 * 927 * @return EOK on success or a negative error code. 1021 928 */ 1022 929 int time_local2tm(const time_t time, struct tm *restrict result) 1023 930 { 1024 // TODO: Deal with timezones.1025 // Currently assumes system and all times are in UTC1026 931 // TODO: deal with timezone 932 // currently assumes system and all times are in GMT 933 1027 934 /* Set result to epoch. */ 1028 935 result->tm_sec = 0; … … 1032 939 result->tm_mon = 0; 1033 940 result->tm_year = 70; /* 1970 */ 1034 1035 if ( normalize_time(result, time) == -1)941 942 if (_normalize_time(result, time) == -1) 1036 943 return EOVERFLOW; 1037 944 1038 945 return EOK; 1039 946 } 1040 947 1041 /** Convert the calendar time to a NULL-terminated string.1042 * 1043 * The format is"Wed Jun 30 21:49:08 1993\n" expressed relative to the948 /** 949 * Converts the calendar time to a null terminated string 950 * of the form "Wed Jun 30 21:49:08 1993\n" expressed relative to the 1044 951 * user's specified timezone. 1045 * 1046 * @param timer Time to convert. 1047 * @param buf Buffer to store the string to. Must be at least 1048 * ASCTIME_BUF_LEN bytes long. 1049 * 1050 * @return EOK on success or a negative error code. 1051 * 952 * 953 * @param timer Time to convert. 954 * @param buf Buffer to store the string to. Must be at least 955 * ASCTIME_BUF_LEN bytes long. 956 * 957 * @return EOK on success or a negative error code. 1052 958 */ 1053 959 int time_local2str(const time_t time, char *buf) 1054 960 { 1055 961 struct tm loctime; 1056 int ret = time_local2tm(time, &loctime); 1057 if (ret != EOK) 1058 return ret; 1059 962 int r; 963 964 if ((r = time_local2tm(time, &loctime)) != EOK) 965 return r; 966 1060 967 time_tm2str(&loctime, buf); 968 1061 969 return EOK; 1062 970 } 1063 971 1064 /** Calculate the difference between two times, in seconds. 1065 * 972 /** 973 * Calculate the difference between two times, in seconds. 974 * 1066 975 * @param time1 First time. 1067 976 * @param time0 Second time. 1068 * 1069 * @return Time difference in seconds. 1070 * 977 * @return Time in seconds. 1071 978 */ 1072 979 double difftime(time_t time1, time_t time0)
Note:
See TracChangeset
for help on using the changeset viewer.