Changes in uspace/lib/posix/time.c [3f466c33:9b1503e] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
uspace/lib/posix/time.c
r3f466c33 r9b1503e 36 36 #define LIBPOSIX_INTERNAL 37 37 38 /* Must be first. */39 #include "stdbool.h"40 41 38 #include "internal/common.h" 42 39 #include "time.h" 43 44 #include "ctype.h"45 #include "errno.h"46 #include "signal.h"47 48 #include "libc/malloc.h"49 #include "libc/task.h"50 #include "libc/stats.h"51 #include "libc/sys/time.h"52 53 // TODO: documentation54 // TODO: test everything in this file55 56 /* Helper functions ***********************************************************/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)64 65 static bool _is_leap_year(time_t year)66 {67 year += 1900;68 69 if (year % 400 == 0)70 return true;71 if (year % 100 == 0)72 return false;73 if (year % 4 == 0)74 return true;75 return false;76 }77 78 static int _days_in_month(time_t year, time_t mon)79 {80 assert(mon >= 0 && mon <= 11);81 year += 1900;82 83 static int month_days[] =84 { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };85 86 if (mon == 1) {87 /* february */88 return _is_leap_year(year) ? 29 : 28;89 } else {90 return month_days[mon];91 }92 }93 94 static int _day_of_year(time_t year, time_t mon, time_t mday)95 {96 static int mdays[] =97 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };98 static int leap_mdays[] =99 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 };100 101 return (_is_leap_year(year) ? leap_mdays[mon] : mdays[mon]) + mday - 1;102 }103 104 /* Integer division that rounds to negative infinity.105 */106 static time_t _floor_div(time_t op1, time_t op2)107 {108 if (op1 >= 0 || op1 % op2 == 0) {109 return op1 / op2;110 } else {111 return op1 / op2 - 1;112 }113 }114 115 /* Modulo that rounds to negative infinity.116 */117 static time_t _floor_mod(time_t op1, time_t op2)118 {119 int div = _floor_div(op1, op2);120 121 /* (a / b) * b + a % b == a */122 /* thus, a % b == a - (a / b) * b */123 124 int result = op1 - div * op2;125 126 /* Some paranoid checking to ensure I didn't make a mistake here. */127 assert(result >= 0);128 assert(result < op2);129 assert(div * op2 + result == op1);130 131 return result;132 }133 134 static time_t _days_since_epoch(time_t year, time_t mon, time_t mday)135 {136 return (year - 70) * 365 + _floor_div(year - 69, 4) -137 _floor_div(year - 1, 100) + _floor_div(year + 299, 400) +138 _day_of_year(year, mon, mday);139 }140 141 /* Assumes normalized broken-down time. */142 static time_t _secs_since_epoch(const struct posix_tm *tm)143 {144 return _days_since_epoch(tm->tm_year, tm->tm_mon, tm->tm_mday) *145 SECS_PER_DAY + tm->tm_hour * SECS_PER_HOUR +146 tm->tm_min * SECS_PER_MIN + tm->tm_sec;147 }148 149 static int _day_of_week(time_t year, time_t mon, time_t mday)150 {151 /* 1970-01-01 is Thursday */152 return (_days_since_epoch(year, mon, mday) + 4) % 7;153 }154 155 struct _long_tm {156 time_t tm_sec;157 time_t tm_min;158 time_t tm_hour;159 time_t tm_mday;160 time_t tm_mon;161 time_t tm_year;162 int tm_wday;163 int tm_yday;164 int tm_isdst;165 };166 167 static void _posix_to_long_tm(struct _long_tm *ltm, struct posix_tm *ptm)168 {169 assert(ltm != NULL && ptm != NULL);170 ltm->tm_sec = ptm->tm_sec;171 ltm->tm_min = ptm->tm_min;172 ltm->tm_hour = ptm->tm_hour;173 ltm->tm_mday = ptm->tm_mday;174 ltm->tm_mon = ptm->tm_mon;175 ltm->tm_year = ptm->tm_year;176 ltm->tm_wday = ptm->tm_wday;177 ltm->tm_yday = ptm->tm_yday;178 ltm->tm_isdst = ptm->tm_isdst;179 }180 181 static void _long_to_posix_tm(struct posix_tm *ptm, struct _long_tm *ltm)182 {183 assert(ltm != NULL && ptm != NULL);184 // FIXME: the cast should be unnecessary, libarch/common.h brain-damage185 assert((ltm->tm_year >= (int) INT_MIN) && (ltm->tm_year <= (int) INT_MAX));186 187 ptm->tm_sec = ltm->tm_sec;188 ptm->tm_min = ltm->tm_min;189 ptm->tm_hour = ltm->tm_hour;190 ptm->tm_mday = ltm->tm_mday;191 ptm->tm_mon = ltm->tm_mon;192 ptm->tm_year = ltm->tm_year;193 ptm->tm_wday = ltm->tm_wday;194 ptm->tm_yday = ltm->tm_yday;195 ptm->tm_isdst = ltm->tm_isdst;196 }197 198 static void _normalize_time(struct _long_tm *tm)199 {200 // TODO: DST correction201 202 /* Adjust time. */203 tm->tm_min += _floor_div(tm->tm_sec, SECS_PER_MIN);204 tm->tm_sec = _floor_mod(tm->tm_sec, SECS_PER_MIN);205 tm->tm_hour += _floor_div(tm->tm_min, MINS_PER_HOUR);206 tm->tm_min = _floor_mod(tm->tm_min, MINS_PER_HOUR);207 tm->tm_mday += _floor_div(tm->tm_hour, HOURS_PER_DAY);208 tm->tm_hour = _floor_mod(tm->tm_hour, HOURS_PER_DAY);209 210 /* Adjust month. */211 tm->tm_year += _floor_div(tm->tm_mon, 12);212 tm->tm_mon = _floor_mod(tm->tm_mon, 12);213 214 /* Now the difficult part - days of month. */215 /* Slow, but simple. */216 // FIXME: do this faster217 218 while (tm->tm_mday < 1) {219 tm->tm_mon--;220 if (tm->tm_mon == -1) {221 tm->tm_mon = 11;222 tm->tm_year--;223 }224 225 tm->tm_mday += _days_in_month(tm->tm_year, tm->tm_mon);226 }227 228 while (tm->tm_mday > _days_in_month(tm->tm_year, tm->tm_mon)) {229 tm->tm_mday -= _days_in_month(tm->tm_year, tm->tm_mon);230 231 tm->tm_mon++;232 if (tm->tm_mon == 12) {233 tm->tm_mon = 0;234 tm->tm_year++;235 }236 }237 238 /* Calculate the remaining two fields. */239 tm->tm_yday = _day_of_year(tm->tm_year, tm->tm_mon, tm->tm_mday);240 tm->tm_wday = _day_of_week(tm->tm_year, tm->tm_mon, tm->tm_mday);241 }242 243 /* Which day the week-based year starts on relative to the first calendar day.244 * E.g. if the year starts on December 31st, the return value is -1.245 */246 static int _wbyear_offset(int year)247 {248 int start_wday = _day_of_week(year, 0, 1);249 return _floor_mod(4 - start_wday, 7) - 3;250 }251 252 /* Returns week-based year of the specified time.253 * Assumes normalized broken-down time.254 */255 static int _wbyear(const struct posix_tm *tm)256 {257 int day = tm->tm_yday - _wbyear_offset(tm->tm_year);258 if (day < 0) {259 /* Last week of previous year. */260 return tm->tm_year - 1;261 }262 if (day > 364 + _is_leap_year(tm->tm_year)){263 /* First week of next year. */264 return tm->tm_year + 1;265 }266 /* All the other days are in the calendar year. */267 return tm->tm_year;268 }269 270 /** Week number of the year, assuming weeks start on sunday.271 * The first Sunday of January is the first day of week 1;272 * days in the new year before this are in week 0.273 *274 * @param tm Normalized broken-down time.275 * @return The week number (0 - 53).276 */277 static int _sun_week_number(const struct posix_tm *tm)278 {279 int first_day = (7 - _day_of_week(tm->tm_year, 0, 1)) % 7;280 return (tm->tm_yday - first_day + 7) / 7;281 }282 283 /** Week number of the year, assuming weeks start on monday.284 * If the week containing January 1st has four or more days in the new year,285 * then it is considered week 1. Otherwise, it is the last week of the previous286 * year, and the next week is week 1. Both January 4th and the first Thursday287 * of January are always in week 1.288 *289 * @param tm Normalized broken-down time.290 * @return The week number (1 - 53).291 */292 static int _iso_week_number(const struct posix_tm *tm)293 {294 int day = tm->tm_yday - _wbyear_offset(tm->tm_year);295 if (day < 0) {296 /* Last week of previous year. */297 return 53;298 }299 if (day > 364 + _is_leap_year(tm->tm_year)){300 /* First week of next year. */301 return 1;302 }303 /* All the other days give correct answer. */304 return (day / 7 + 1);305 }306 307 /** Week number of the year, assuming weeks start on monday.308 * The first Monday of January is the first day of week 1;309 * days in the new year before this are in week 0.310 *311 * @param tm Normalized broken-down time.312 * @return The week number (0 - 53).313 */314 static int _mon_week_number(const struct posix_tm *tm)315 {316 int first_day = (1 - _day_of_week(tm->tm_year, 0, 1)) % 7;317 return (tm->tm_yday - first_day + 7) / 7;318 }319 320 /******************************************************************************/321 322 int posix_daylight;323 long posix_timezone;324 char *posix_tzname[2];325 326 void posix_tzset(void)327 {328 // TODO: read environment329 posix_tzname[0] = (char *) "GMT";330 posix_tzname[1] = (char *) "GMT";331 posix_daylight = 0;332 posix_timezone = 0;333 }334 335 double posix_difftime(time_t time1, time_t time0)336 {337 return (double) (time1 - time0);338 }339 340 /** This function first normalizes the provided broken-down time341 * (moves all values to their proper bounds) and then tries to342 * calculate the appropriate time_t representation.343 *344 * @param timeptr Broken-down time.345 * @return time_t representation of the time, undefined value on overflow346 */347 time_t posix_mktime(struct posix_tm *tm)348 {349 // TODO: take DST flag into account350 // TODO: detect overflow351 352 struct _long_tm ltm;353 _posix_to_long_tm(<m, tm);354 _normalize_time(<m);355 _long_to_posix_tm(tm, <m);356 357 return _secs_since_epoch(tm);358 }359 360 struct posix_tm *posix_gmtime(const time_t *timer)361 {362 static struct posix_tm result;363 return posix_gmtime_r(timer, &result);364 }365 366 struct posix_tm *posix_gmtime_r(const time_t *restrict timer,367 struct posix_tm *restrict result)368 {369 assert(timer != NULL);370 assert(result != NULL);371 372 /* Set epoch and seconds to _long_tm struct and normalize to get373 * correct values.374 */375 struct _long_tm ltm = {376 .tm_sec = *timer,377 .tm_min = 0,378 .tm_hour = 0, /* 00:00:xx */379 .tm_mday = 1,380 .tm_mon = 0, /* January 1st */381 .tm_year = 70, /* 1970 */382 };383 _normalize_time(<m);384 385 if (ltm.tm_year < (int) INT_MIN || ltm.tm_year > (int) INT_MAX) {386 errno = EOVERFLOW;387 return NULL;388 }389 390 _long_to_posix_tm(result, <m);391 return result;392 }393 40 394 41 /** … … 397 44 * @return 398 45 */ 399 struct posix_tm *posix_localtime(const time_t *time r)46 struct posix_tm *posix_localtime(const time_t *timep) 400 47 { 401 static struct posix_tm result; 402 return posix_localtime_r(timer, &result); 403 } 404 405 struct posix_tm *posix_localtime_r(const time_t *restrict timer, 406 struct posix_tm *restrict result) 407 { 408 // TODO: deal with timezone 409 // currently assumes system and all times are in GMT 410 return posix_gmtime_r(timer, result); 48 // TODO 49 static struct posix_tm result = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 50 return &result; 411 51 } 412 52 … … 416 56 * @return 417 57 */ 418 char *posix_asctime(const struct posix_tm *t imeptr)58 char *posix_asctime(const struct posix_tm *tm) 419 59 { 420 static char buf[ASCTIME_BUF_LEN]; 421 return posix_asctime_r(timeptr, buf); 422 } 423 424 char *posix_asctime_r(const struct posix_tm *restrict timeptr, 425 char *restrict buf) 426 { 427 assert(timeptr != NULL); 428 assert(buf != NULL); 429 430 static const char *wday[] = { 431 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 432 }; 433 static const char *mon[] = { 434 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 435 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 436 }; 437 438 snprintf(buf, ASCTIME_BUF_LEN, "%s %s %2d %02d:%02d:%02d %d\n", 439 wday[timeptr->tm_wday], 440 mon[timeptr->tm_mon], 441 timeptr->tm_mday, timeptr->tm_hour, 442 timeptr->tm_min, timeptr->tm_sec, 443 1900 + timeptr->tm_year); 444 445 return buf; 60 // TODO 61 static char result[] = "Sun Jan 01 00:00:00 1900\n"; 62 return result; 446 63 } 447 64 … … 451 68 * @return 452 69 */ 453 char *posix_ctime(const time_t *time r)70 char *posix_ctime(const time_t *timep) 454 71 { 455 struct posix_tm *loctime = posix_localtime(timer); 456 if (loctime == NULL) { 457 return NULL; 458 } 459 return posix_asctime(loctime); 460 } 461 462 char *posix_ctime_r(const time_t *timer, char *buf) 463 { 464 struct posix_tm loctime; 465 if (posix_localtime_r(timer, &loctime) == NULL) { 466 return NULL; 467 } 468 return posix_asctime_r(&loctime, buf); 72 return posix_asctime(posix_localtime(timep)); 469 73 } 470 74 … … 477 81 * @return 478 82 */ 479 size_t posix_strftime(char *s, size_t maxsize, 480 const char *format, const struct posix_tm *tm) 481 { 482 // TODO: use locale 483 static const char *wday_abbr[] = { 484 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 485 }; 486 static const char *wday[] = { 487 "Sunday", "Monday", "Tuesday", "Wednesday", 488 "Thursday", "Friday", "Saturday" 489 }; 490 static const char *mon_abbr[] = { 491 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 492 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 493 }; 494 static const char *mon[] = { 495 "January", "February", "March", "April", "May", "June", "July", 496 "August", "September", "October", "November", "December" 497 }; 498 499 if (maxsize < 1) { 500 return 0; 501 } 502 503 char *ptr = s; 504 size_t consumed; 505 size_t remaining = maxsize; 506 507 #define append(...) { \ 508 /* FIXME: this requires POSIX-correct snprintf */ \ 509 /* otherwise it won't work with non-ascii chars */ \ 510 consumed = snprintf(ptr, remaining, __VA_ARGS__); \ 511 if (consumed >= remaining) { \ 512 return 0; \ 513 } \ 514 ptr += consumed; \ 515 remaining -= consumed; \ 516 } 517 518 #define recurse(fmt) { \ 519 consumed = posix_strftime(ptr, remaining, fmt, tm); \ 520 if (consumed == 0) { \ 521 return 0; \ 522 } \ 523 ptr += consumed; \ 524 remaining -= consumed; \ 525 } 526 527 #define TO_12H(hour) (((hour) > 12) ? ((hour) - 12) : \ 528 (((hour) == 0) ? 12 : (hour))) 529 530 while (*format != '\0') { 531 if (*format != '%') { 532 append("%c", *format); 533 format++; 534 continue; 535 } 536 537 format++; 538 if (*format == '0' || *format == '+') { 539 // TODO: padding 540 format++; 541 } 542 while (isdigit(*format)) { 543 // TODO: padding 544 format++; 545 } 546 if (*format == 'O' || *format == 'E') { 547 // TODO: locale's alternative format 548 format++; 549 } 550 551 switch (*format) { 552 case 'a': 553 append("%s", wday_abbr[tm->tm_wday]); break; 554 case 'A': 555 append("%s", wday[tm->tm_wday]); break; 556 case 'b': 557 append("%s", mon_abbr[tm->tm_mon]); break; 558 case 'B': 559 append("%s", mon[tm->tm_mon]); break; 560 case 'c': 561 // TODO: locale-specific datetime format 562 recurse("%Y-%m-%d %H:%M:%S"); break; 563 case 'C': 564 append("%02d", (1900 + tm->tm_year) / 100); break; 565 case 'd': 566 append("%02d", tm->tm_mday); break; 567 case 'D': 568 recurse("%m/%d/%y"); break; 569 case 'e': 570 append("%2d", tm->tm_mday); break; 571 case 'F': 572 recurse("%+4Y-%m-%d"); break; 573 case 'g': 574 append("%02d", _wbyear(tm) % 100); break; 575 case 'G': 576 append("%d", _wbyear(tm)); break; 577 case 'h': 578 recurse("%b"); break; 579 case 'H': 580 append("%02d", tm->tm_hour); break; 581 case 'I': 582 append("%02d", TO_12H(tm->tm_hour)); break; 583 case 'j': 584 append("%03d", tm->tm_yday); break; 585 case 'k': 586 append("%2d", tm->tm_hour); break; 587 case 'l': 588 append("%2d", TO_12H(tm->tm_hour)); break; 589 case 'm': 590 append("%02d", tm->tm_mon); break; 591 case 'M': 592 append("%02d", tm->tm_min); break; 593 case 'n': 594 append("\n"); break; 595 case 'p': 596 append("%s", tm->tm_hour < 12 ? "AM" : "PM"); break; 597 case 'P': 598 append("%s", tm->tm_hour < 12 ? "am" : "PM"); break; 599 case 'r': 600 recurse("%I:%M:%S %p"); break; 601 case 'R': 602 recurse("%H:%M"); break; 603 case 's': 604 append("%ld", _secs_since_epoch(tm)); break; 605 case 'S': 606 append("%02d", tm->tm_sec); break; 607 case 't': 608 append("\t"); break; 609 case 'T': 610 recurse("%H:%M:%S"); break; 611 case 'u': 612 append("%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday); break; 613 case 'U': 614 append("%02d", _sun_week_number(tm)); break; 615 case 'V': 616 append("%02d", _iso_week_number(tm)); break; 617 case 'w': 618 append("%d", tm->tm_wday); break; 619 case 'W': 620 append("%02d", _mon_week_number(tm)); break; 621 case 'x': 622 // TODO: locale-specific date format 623 recurse("%Y-%m-%d"); break; 624 case 'X': 625 // TODO: locale-specific time format 626 recurse("%H:%M:%S"); break; 627 case 'y': 628 append("%02d", tm->tm_year % 100); break; 629 case 'Y': 630 append("%d", 1900 + tm->tm_year); break; 631 case 'z': 632 // TODO: timezone 633 break; 634 case 'Z': 635 // TODO: timezone 636 break; 637 case '%': 638 append("%%"); 639 break; 640 default: 641 /* Invalid specifier, print verbatim. */ 642 while (*format != '%') { 643 format--; 644 } 645 append("%%"); 646 break; 647 } 648 format++; 649 } 650 651 #undef append 652 #undef recurse 653 654 return maxsize - remaining; 655 } 656 657 int posix_clock_getres(posix_clockid_t clock_id, struct posix_timespec *res) 658 { 659 assert(res != NULL); 660 661 switch (clock_id) { 662 case CLOCK_REALTIME: 663 res->tv_sec = 0; 664 res->tv_nsec = 1000; /* Microsecond resolution. */ 665 return 0; 666 default: 667 errno = EINVAL; 668 return -1; 669 } 670 } 671 672 int posix_clock_gettime(posix_clockid_t clock_id, struct posix_timespec *tp) 673 { 674 assert(tp != NULL); 675 676 switch (clock_id) { 677 case CLOCK_REALTIME: 678 ; 679 struct timeval tv; 680 gettimeofday(&tv, NULL); 681 tp->tv_sec = tv.tv_sec; 682 tp->tv_nsec = tv.tv_usec * 1000; 683 return 0; 684 default: 685 errno = EINVAL; 686 return -1; 687 } 688 } 689 690 int posix_clock_settime(posix_clockid_t clock_id, 691 const struct posix_timespec *tp) 692 { 693 assert(tp != NULL); 694 695 switch (clock_id) { 696 case CLOCK_REALTIME: 697 // TODO: setting clock 698 // FIXME: HelenOS doesn't actually support hardware 699 // clock yet 700 errno = EPERM; 701 return -1; 702 default: 703 errno = EINVAL; 704 return -1; 705 } 706 } 707 708 int posix_clock_nanosleep(posix_clockid_t clock_id, int flags, 709 const struct posix_timespec *rqtp, struct posix_timespec *rmtp) 710 { 711 assert(rqtp != NULL); 712 assert(rmtp != NULL); 713 714 switch (clock_id) { 715 case CLOCK_REALTIME: 716 // TODO: interruptible sleep 717 if (rqtp->tv_sec != 0) { 718 sleep(rqtp->tv_sec); 719 } 720 if (rqtp->tv_nsec != 0) { 721 usleep(rqtp->tv_nsec / 1000); 722 } 723 return 0; 724 default: 725 errno = EINVAL; 726 return -1; 727 } 728 } 729 730 #if 0 731 732 struct __posix_timer { 733 posix_clockid_t clockid; 734 struct posix_sigevent evp; 735 }; 736 737 int posix_timer_create(posix_clockid_t clockid, 738 struct posix_sigevent *restrict evp, 739 posix_timer_t *restrict timerid) 83 size_t posix_strftime(char *s, size_t maxsize, const char *format, const struct posix_tm *tm) 740 84 { 741 85 // TODO … … 743 87 } 744 88 745 int posix_timer_delete(posix_timer_t timerid)746 {747 // TODO748 not_implemented();749 }750 751 int posix_timer_getoverrun(posix_timer_t timerid)752 {753 // TODO754 not_implemented();755 }756 757 int posix_timer_gettime(posix_timer_t timerid,758 struct posix_itimerspec *value)759 {760 // TODO761 not_implemented();762 }763 764 int posix_timer_settime(posix_timer_t timerid, int flags,765 const struct posix_itimerspec *restrict value,766 struct posix_itimerspec *restrict ovalue)767 {768 // TODO769 not_implemented();770 }771 772 #endif773 774 /**775 * Get CPU time used since the process invocation.776 *777 * @return Consumed CPU cycles by this process or -1 if not available.778 */779 posix_clock_t posix_clock(void)780 {781 posix_clock_t total_cycles = -1;782 stats_task_t *task_stats = stats_get_task(task_get_id());783 if (task_stats) {784 total_cycles = (posix_clock_t) (task_stats->kcycles + task_stats->ucycles);785 }786 free(task_stats);787 task_stats = 0;788 789 return total_cycles;790 }791 792 89 /** @} 793 90 */
Note:
See TracChangeset
for help on using the changeset viewer.