Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/posix/time.c

    rd3e3a71 rd4d74dc  
    4545#include "errno.h"
    4646#include "signal.h"
     47#include "assert.h"
    4748
    4849#include "libc/malloc.h"
     
    140141 * Used by some functions in this file.
    141142 *
    142  * @param op1 Dividend.
     143 * @param op1 Divident.
    143144 * @param op2 Divisor.
    144145 * @return Rounded quotient.
     
    157158 * Used by some functions in this file.
    158159 *
    159  * @param op1 Dividend.
     160 * @param op1 Divident.
    160161 * @param op2 Divisor.
    161162 * @return Remainder.
     
    195196
    196197/**
     198 * Seconds since the Epoch. see also _days_since_epoch().
     199 *
     200 * @param tm Normalized broken-down time.
     201 * @return Number of seconds since the epoch, not counting leap seconds.
     202 */
     203static time_t _secs_since_epoch(const struct posix_tm *tm)
     204{
     205        return _days_since_epoch(tm->tm_year, tm->tm_mon, tm->tm_mday) *
     206            SECS_PER_DAY + tm->tm_hour * SECS_PER_HOUR +
     207            tm->tm_min * SECS_PER_MIN + tm->tm_sec;
     208}
     209
     210/**
    197211 * Which day of week the specified date is.
    198212 *
     
    216230 * @return 0 on success, -1 on overflow
    217231 */
    218 static int _normalize_time(struct tm *tm, time_t sec_add)
     232static int _normalize_time(struct posix_tm *tm, time_t sec_add)
    219233{
    220234        // TODO: DST correction
     
    292306}
    293307
     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.
     311 *
     312 * @param Year since 1900.
     313 * @return Offset of week-based year relative to calendar year.
     314 */
     315static 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.
     323 *
     324 * @param tm Normalized broken-down time.
     325 * @return Week-based year.
     326 */
     327static int _wbyear(const struct posix_tm *tm)
     328{
     329        int day = tm->tm_yday - _wbyear_offset(tm->tm_year);
     330        if (day < 0) {
     331                /* Last week of previous year. */
     332                return tm->tm_year - 1;
     333        }
     334        if (day > 364 + _is_leap_year(tm->tm_year)) {
     335                /* First week of next year. */
     336                return tm->tm_year + 1;
     337        }
     338        /* All the other days are in the calendar year. */
     339        return tm->tm_year;
     340}
     341
     342/**
     343 * Week number of the year, assuming weeks start on sunday.
     344 * The first Sunday of January is the first day of week 1;
     345 * days in the new year before this are in week 0.
     346 *
     347 * @param tm Normalized broken-down time.
     348 * @return The week number (0 - 53).
     349 */
     350static int _sun_week_number(const struct posix_tm *tm)
     351{
     352        int first_day = (7 - _day_of_week(tm->tm_year, 0, 1)) % 7;
     353        return (tm->tm_yday - first_day + 7) / 7;
     354}
     355
     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
     361 * of January are always in week 1.
     362 *
     363 * @param tm Normalized broken-down time.
     364 * @return The week number (1 - 53).
     365 */
     366static int _iso_week_number(const struct posix_tm *tm)
     367{
     368        int day = tm->tm_yday - _wbyear_offset(tm->tm_year);
     369        if (day < 0) {
     370                /* Last week of previous year. */
     371                return 53;
     372        }
     373        if (day > 364 + _is_leap_year(tm->tm_year)) {
     374                /* First week of next year. */
     375                return 1;
     376        }
     377        /* All the other days give correct answer. */
     378        return (day / 7 + 1);
     379}
     380
     381/**
     382 * Week number of the year, assuming weeks start on monday.
     383 * The first Monday of January is the first day of week 1;
     384 * days in the new year before this are in week 0.
     385 *
     386 * @param tm Normalized broken-down time.
     387 * @return The week number (0 - 53).
     388 */
     389static int _mon_week_number(const struct posix_tm *tm)
     390{
     391        int first_day = (1 - _day_of_week(tm->tm_year, 0, 1)) % 7;
     392        return (tm->tm_yday - first_day + 7) / 7;
     393}
     394
    294395/******************************************************************************/
    295396
     
    311412
    312413/**
     414 * Calculate the difference between two times, in seconds.
     415 *
     416 * @param time1 First time.
     417 * @param time0 Second time.
     418 * @return Time in seconds.
     419 */
     420double posix_difftime(time_t time1, time_t time0)
     421{
     422        return (double) (time1 - time0);
     423}
     424
     425/**
     426 * This function first normalizes the provided broken-down time
     427 * (moves all values to their proper bounds) and then tries to
     428 * calculate the appropriate time_t representation.
     429 *
     430 * @param tm Broken-down time.
     431 * @return time_t representation of the time, undefined value on overflow.
     432 */
     433time_t posix_mktime(struct posix_tm *tm)
     434{
     435        // TODO: take DST flag into account
     436        // TODO: detect overflow
     437
     438        _normalize_time(tm, 0);
     439        return _secs_since_epoch(tm);
     440}
     441
     442/**
     443 * Converts a time value to a broken-down UTC time.
     444 *
     445 * @param timer Time to convert.
     446 * @return Normalized broken-down time in UTC, NULL on overflow.
     447 */
     448struct posix_tm *posix_gmtime(const time_t *timer)
     449{
     450        assert(timer != NULL);
     451
     452        static struct posix_tm result;
     453        return posix_gmtime_r(timer, &result);
     454}
     455
     456/**
    313457 * Converts a time value to a broken-down UTC time.
    314458 *
     
    317461 * @return Value of result on success, NULL on overflow.
    318462 */
    319 struct tm *posix_gmtime_r(const time_t *restrict timer,
    320     struct tm *restrict result)
     463struct posix_tm *posix_gmtime_r(const time_t *restrict timer,
     464    struct posix_tm *restrict result)
    321465{
    322466        assert(timer != NULL);
     
    341485/**
    342486 * Converts a time value to a broken-down local time.
     487 *
     488 * @param timer Time to convert.
     489 * @return Normalized broken-down time in local timezone, NULL on overflow.
     490 */
     491struct posix_tm *posix_localtime(const time_t *timer)
     492{
     493        static struct posix_tm result;
     494        return posix_localtime_r(timer, &result);
     495}
     496
     497/**
     498 * Converts a time value to a broken-down local time.
    343499 *
    344500 * @param timer Time to convert.
     
    346502 * @return Value of result on success, NULL on overflow.
    347503 */
    348 struct tm *posix_localtime_r(const time_t *restrict timer,
    349     struct tm *restrict result)
     504struct posix_tm *posix_localtime_r(const time_t *restrict timer,
     505    struct posix_tm *restrict result)
    350506{
    351507        // TODO: deal with timezone
    352508        // currently assumes system and all times are in GMT
    353509        return posix_gmtime_r(timer, result);
     510}
     511
     512/**
     513 * Converts broken-down time to a string in format
     514 * "Sun Jan 1 00:00:00 1970\n". (Obsolete)
     515 *
     516 * @param timeptr Broken-down time structure.
     517 * @return Pointer to a statically allocated string.
     518 */
     519char *posix_asctime(const struct posix_tm *timeptr)
     520{
     521        static char buf[ASCTIME_BUF_LEN];
     522        return posix_asctime_r(timeptr, buf);
    354523}
    355524
     
    363532 * @return Value of buf.
    364533 */
    365 char *posix_asctime_r(const struct tm *restrict timeptr,
     534char *posix_asctime_r(const struct posix_tm *restrict timeptr,
    366535    char *restrict buf)
    367536{
     
    388557
    389558/**
     559 * Equivalent to asctime(localtime(clock)).
     560 *
     561 * @param timer Time to convert.
     562 * @return Pointer to a statically allocated string holding the date.
     563 */
     564char *posix_ctime(const time_t *timer)
     565{
     566        struct posix_tm *loctime = posix_localtime(timer);
     567        if (loctime == NULL) {
     568                return NULL;
     569        }
     570        return posix_asctime(loctime);
     571}
     572
     573/**
    390574 * Reentrant variant of ctime().
    391575 *
     
    397581char *posix_ctime_r(const time_t *timer, char *buf)
    398582{
    399         struct tm loctime;
     583        struct posix_tm loctime;
    400584        if (posix_localtime_r(timer, &loctime) == NULL) {
    401585                return NULL;
    402586        }
    403587        return posix_asctime_r(&loctime, buf);
     588}
     589
     590/**
     591 * Convert time and date to a string, based on a specified format and
     592 * current locale.
     593 *
     594 * @param s Buffer to write string to.
     595 * @param maxsize Size of the buffer.
     596 * @param format Format of the output.
     597 * @param tm Broken-down time to format.
     598 * @return Number of bytes written.
     599 */
     600size_t posix_strftime(char *restrict s, size_t maxsize,
     601    const char *restrict format, const struct posix_tm *restrict tm)
     602{
     603        assert(s != NULL);
     604        assert(format != NULL);
     605        assert(tm != NULL);
     606
     607        // TODO: use locale
     608        static const char *wday_abbr[] = {
     609                "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
     610        };
     611        static const char *wday[] = {
     612                "Sunday", "Monday", "Tuesday", "Wednesday",
     613                "Thursday", "Friday", "Saturday"
     614        };
     615        static const char *mon_abbr[] = {
     616                "Jan", "Feb", "Mar", "Apr", "May", "Jun",
     617                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
     618        };
     619        static const char *mon[] = {
     620                "January", "February", "March", "April", "May", "June", "July",
     621                "August", "September", "October", "November", "December"
     622        };
     623       
     624        if (maxsize < 1) {
     625                return 0;
     626        }
     627       
     628        char *ptr = s;
     629        size_t consumed;
     630        size_t remaining = maxsize;
     631       
     632        #define append(...) { \
     633                /* FIXME: this requires POSIX-correct snprintf */ \
     634                /*        otherwise it won't work with non-ascii chars */ \
     635                consumed = snprintf(ptr, remaining, __VA_ARGS__); \
     636                if (consumed >= remaining) { \
     637                        return 0; \
     638                } \
     639                ptr += consumed; \
     640                remaining -= consumed; \
     641        }
     642       
     643        #define recurse(fmt) { \
     644                consumed = posix_strftime(ptr, remaining, fmt, tm); \
     645                if (consumed == 0) { \
     646                        return 0; \
     647                } \
     648                ptr += consumed; \
     649                remaining -= consumed; \
     650        }
     651       
     652        #define TO_12H(hour) (((hour) > 12) ? ((hour) - 12) : \
     653            (((hour) == 0) ? 12 : (hour)))
     654       
     655        while (*format != '\0') {
     656                if (*format != '%') {
     657                        append("%c", *format);
     658                        format++;
     659                        continue;
     660                }
     661               
     662                format++;
     663                if (*format == '0' || *format == '+') {
     664                        // TODO: padding
     665                        format++;
     666                }
     667                while (isdigit(*format)) {
     668                        // TODO: padding
     669                        format++;
     670                }
     671                if (*format == 'O' || *format == 'E') {
     672                        // TODO: locale's alternative format
     673                        format++;
     674                }
     675               
     676                switch (*format) {
     677                case 'a':
     678                        append("%s", wday_abbr[tm->tm_wday]); break;
     679                case 'A':
     680                        append("%s", wday[tm->tm_wday]); break;
     681                case 'b':
     682                        append("%s", mon_abbr[tm->tm_mon]); break;
     683                case 'B':
     684                        append("%s", mon[tm->tm_mon]); break;
     685                case 'c':
     686                        // TODO: locale-specific datetime format
     687                        recurse("%Y-%m-%d %H:%M:%S"); break;
     688                case 'C':
     689                        append("%02d", (1900 + tm->tm_year) / 100); break;
     690                case 'd':
     691                        append("%02d", tm->tm_mday); break;
     692                case 'D':
     693                        recurse("%m/%d/%y"); break;
     694                case 'e':
     695                        append("%2d", tm->tm_mday); break;
     696                case 'F':
     697                        recurse("%+4Y-%m-%d"); break;
     698                case 'g':
     699                        append("%02d", _wbyear(tm) % 100); break;
     700                case 'G':
     701                        append("%d", _wbyear(tm)); break;
     702                case 'h':
     703                        recurse("%b"); break;
     704                case 'H':
     705                        append("%02d", tm->tm_hour); break;
     706                case 'I':
     707                        append("%02d", TO_12H(tm->tm_hour)); break;
     708                case 'j':
     709                        append("%03d", tm->tm_yday); break;
     710                case 'k':
     711                        append("%2d", tm->tm_hour); break;
     712                case 'l':
     713                        append("%2d", TO_12H(tm->tm_hour)); break;
     714                case 'm':
     715                        append("%02d", tm->tm_mon); break;
     716                case 'M':
     717                        append("%02d", tm->tm_min); break;
     718                case 'n':
     719                        append("\n"); break;
     720                case 'p':
     721                        append("%s", tm->tm_hour < 12 ? "AM" : "PM"); break;
     722                case 'P':
     723                        append("%s", tm->tm_hour < 12 ? "am" : "PM"); break;
     724                case 'r':
     725                        recurse("%I:%M:%S %p"); break;
     726                case 'R':
     727                        recurse("%H:%M"); break;
     728                case 's':
     729                        append("%ld", _secs_since_epoch(tm)); break;
     730                case 'S':
     731                        append("%02d", tm->tm_sec); break;
     732                case 't':
     733                        append("\t"); break;
     734                case 'T':
     735                        recurse("%H:%M:%S"); break;
     736                case 'u':
     737                        append("%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday); break;
     738                case 'U':
     739                        append("%02d", _sun_week_number(tm)); break;
     740                case 'V':
     741                        append("%02d", _iso_week_number(tm)); break;
     742                case 'w':
     743                        append("%d", tm->tm_wday); break;
     744                case 'W':
     745                        append("%02d", _mon_week_number(tm)); break;
     746                case 'x':
     747                        // TODO: locale-specific date format
     748                        recurse("%Y-%m-%d"); break;
     749                case 'X':
     750                        // TODO: locale-specific time format
     751                        recurse("%H:%M:%S"); break;
     752                case 'y':
     753                        append("%02d", tm->tm_year % 100); break;
     754                case 'Y':
     755                        append("%d", 1900 + tm->tm_year); break;
     756                case 'z':
     757                        // TODO: timezone
     758                        break;
     759                case 'Z':
     760                        // TODO: timezone
     761                        break;
     762                case '%':
     763                        append("%%");
     764                        break;
     765                default:
     766                        /* Invalid specifier, print verbatim. */
     767                        while (*format != '%') {
     768                                format--;
     769                        }
     770                        append("%%");
     771                        break;
     772                }
     773                format++;
     774        }
     775       
     776        #undef append
     777        #undef recurse
     778       
     779        return maxsize - remaining;
    404780}
    405781
     
    518894        stats_task_t *task_stats = stats_get_task(task_get_id());
    519895        if (task_stats) {
    520                 total_cycles = (posix_clock_t) (task_stats->kcycles +
    521                     task_stats->ucycles);
     896                total_cycles = (posix_clock_t) (task_stats->kcycles + task_stats->ucycles);
    522897                free(task_stats);
    523898                task_stats = 0;
Note: See TracChangeset for help on using the changeset viewer.