Ignore:
File:
1 edited

Legend:

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

    ra12f7f1 r55b1efd  
    5151#include "libc/sys/time.h"
    5252
    53 // TODO: documentation
    5453// TODO: test everything in this file
     54
     55/* In some places in this file, phrase "normalized broken-down time" is used.
     56 * This means time broken down to components (year, month, day, hour, min, sec),
     57 * in which every component is in its proper bounds. Non-normalized time could
     58 * e.g. be 2011-54-5 29:13:-5, which would semantically mean start of year 2011
     59 * + 53 months + 4 days + 29 hours + 13 minutes - 5 seconds.
     60 */
     61
     62
    5563
    5664/* Helper functions ***********************************************************/
     
    6472
    6573/**
    66  *
    67  * @param year
    68  * @return
     74 * Checks whether the year is a leap year.
     75 *
     76 * @param year Year since 1900 (e.g. for 1970, the value is 70).
     77 * @return true if year is a leap year, false otherwise
    6978 */
    7079static bool _is_leap_year(time_t year)
     
    8291
    8392/**
    84  *
    85  * @param year
    86  * @param mon
    87  * @return
     93 * Returns how many days there are in the given month of the given year.
     94 * Note that year is only taken into account if month is February.
     95 *
     96 * @param year Year since 1900 (can be negative).
     97 * @param mon Month of the year. 0 for January, 11 for December.
     98 * @return Number of days in the specified month.
    8899 */
    89100static int _days_in_month(time_t year, time_t mon)
    90101{
    91102        assert(mon >= 0 && mon <= 11);
    92         year += 1900;
    93103
    94104        static int month_days[] =
     
    96106
    97107        if (mon == 1) {
     108                year += 1900;
    98109                /* february */
    99110                return _is_leap_year(year) ? 29 : 28;
     
    104115
    105116/**
    106  *
    107  * @param year
    108  * @param mon
    109  * @param mday
    110  * @return
     117 * For specified year, month and day of month, returns which day of that year
     118 * it is.
     119 *
     120 * For example, given date 2011-01-03, the corresponding expression is:
     121 *     _day_of_year(111, 0, 3) == 2
     122 *
     123 * @param year Year (year 1900 = 0, can be negative).
     124 * @param mon Month (January = 0).
     125 * @param mday Day of month (First day is 1).
     126 * @return Day of year (First day is 0).
    111127 */
    112128static int _day_of_year(time_t year, time_t mon, time_t mday)
     
    122138/**
    123139 * Integer division that rounds to negative infinity.
    124  *
    125  * @param op1
    126  * @param op2
    127  * @return
     140 * Used by some functions in this file.
     141 *
     142 * @param op1 Divident.
     143 * @param op2 Divisor.
     144 * @return Rounded quotient.
    128145 */
    129146static time_t _floor_div(time_t op1, time_t op2)
     
    138155/**
    139156 * Modulo that rounds to negative infinity.
    140  *
    141  * @param op1
    142  * @param op2
    143  * @return
     157 * Used by some functions in this file.
     158 *
     159 * @param op1 Divident.
     160 * @param op2 Divisor.
     161 * @return Remainder.
    144162 */
    145163static time_t _floor_mod(time_t op1, time_t op2)
     
    161179
    162180/**
    163  *
    164  * @param year
    165  * @param mon
    166  * @param mday
    167  * @return
     181 * Number of days since the Epoch.
     182 * Epoch is 1970-01-01, which is also equal to day 0.
     183 *
     184 * @param year Year (year 1900 = 0, may be negative).
     185 * @param mon Month (January = 0).
     186 * @param mday Day of month (first day = 1).
     187 * @return Number of days since the Epoch.
    168188 */
    169189static time_t _days_since_epoch(time_t year, time_t mon, time_t mday)
     
    175195
    176196/**
    177  * Assumes normalized broken-down time.
    178  *
    179  * @param tm
    180  * @return
     197 * Seconds since the Epoch. see also _days_since_epoch().
     198 * 
     199 * @param tm Normalized broken-down time.
     200 * @return Number of seconds since the epoch, not counting leap seconds.
    181201 */
    182202static time_t _secs_since_epoch(const struct posix_tm *tm)
     
    188208
    189209/**
    190  *
    191  * @param year
    192  * @param mon
    193  * @param mday
    194  * @return
     210 * Which day of week the specified date is.
     211 *
     212 * @param year Year (year 1900 = 0).
     213 * @param mon Month (January = 0).
     214 * @param mday Day of month (first = 1).
     215 * @return Day of week (Sunday = 0).
    195216 */
    196217static int _day_of_week(time_t year, time_t mon, time_t mday)
    197218{
    198219        /* 1970-01-01 is Thursday */
    199         return (_days_since_epoch(year, mon, mday) + 4) % 7;
    200 }
    201 
    202 struct _long_tm {
    203         time_t tm_sec;
    204         time_t tm_min;
    205         time_t tm_hour;
    206         time_t tm_mday;
    207         time_t tm_mon;
    208         time_t tm_year;
    209         int tm_wday;
    210         int tm_yday;
    211         int tm_isdst;
    212 };
    213 
    214 /**
    215  *
    216  * @param ltm
    217  * @param ptm
    218  */
    219 static void _posix_to_long_tm(struct _long_tm *ltm, struct posix_tm *ptm)
    220 {
    221         assert(ltm != NULL && ptm != NULL);
    222         ltm->tm_sec = ptm->tm_sec;
    223         ltm->tm_min = ptm->tm_min;
    224         ltm->tm_hour = ptm->tm_hour;
    225         ltm->tm_mday = ptm->tm_mday;
    226         ltm->tm_mon = ptm->tm_mon;
    227         ltm->tm_year = ptm->tm_year;
    228         ltm->tm_wday = ptm->tm_wday;
    229         ltm->tm_yday = ptm->tm_yday;
    230         ltm->tm_isdst = ptm->tm_isdst;
    231 }
    232 
    233 /**
    234  *
    235  * @param ptm
    236  * @param ltm
    237  */
    238 static void _long_to_posix_tm(struct posix_tm *ptm, struct _long_tm *ltm)
    239 {
    240         assert(ltm != NULL && ptm != NULL);
    241         // FIXME: the cast should be unnecessary, libarch/common.h brain-damage
    242         assert((ltm->tm_year >= (int) INT_MIN) && (ltm->tm_year <= (int) INT_MAX));
    243 
    244         ptm->tm_sec = ltm->tm_sec;
    245         ptm->tm_min = ltm->tm_min;
    246         ptm->tm_hour = ltm->tm_hour;
    247         ptm->tm_mday = ltm->tm_mday;
    248         ptm->tm_mon = ltm->tm_mon;
    249         ptm->tm_year = ltm->tm_year;
    250         ptm->tm_wday = ltm->tm_wday;
    251         ptm->tm_yday = ltm->tm_yday;
    252         ptm->tm_isdst = ltm->tm_isdst;
    253 }
    254 
    255 /**
    256  *
    257  * @param tm
    258  */
    259 static void _normalize_time(struct _long_tm *tm)
     220        return _floor_mod((_days_since_epoch(year, mon, mday) + 4), 7);
     221}
     222
     223/**
     224 * Normalizes the broken-down time and optionally adds specified amount of
     225 * seconds.
     226 *
     227 * @param tm Broken-down time to normalize.
     228 * @param sec_add Seconds to add.
     229 * @return 0 on success, -1 on overflow
     230 */
     231static int _normalize_time(struct posix_tm *tm, time_t sec_add)
    260232{
    261233        // TODO: DST correction
    262234
     235        /* Set initial values. */
     236        time_t sec = tm->tm_sec + sec_add;
     237        time_t min = tm->tm_min;
     238        time_t hour = tm->tm_hour;
     239        time_t day = tm->tm_mday - 1;
     240        time_t mon = tm->tm_mon;
     241        time_t year = tm->tm_year;
     242
    263243        /* Adjust time. */
    264         tm->tm_min += _floor_div(tm->tm_sec, SECS_PER_MIN);
    265         tm->tm_sec = _floor_mod(tm->tm_sec, SECS_PER_MIN);
    266         tm->tm_hour += _floor_div(tm->tm_min, MINS_PER_HOUR);
    267         tm->tm_min = _floor_mod(tm->tm_min, MINS_PER_HOUR);
    268         tm->tm_mday += _floor_div(tm->tm_hour, HOURS_PER_DAY);
    269         tm->tm_hour = _floor_mod(tm->tm_hour, HOURS_PER_DAY);
     244        min += _floor_div(sec, SECS_PER_MIN);
     245        sec = _floor_mod(sec, SECS_PER_MIN);
     246        hour += _floor_div(min, MINS_PER_HOUR);
     247        min = _floor_mod(min, MINS_PER_HOUR);
     248        day += _floor_div(hour, HOURS_PER_DAY);
     249        hour = _floor_mod(hour, HOURS_PER_DAY);
    270250
    271251        /* Adjust month. */
    272         tm->tm_year += _floor_div(tm->tm_mon, 12);
    273         tm->tm_mon = _floor_mod(tm->tm_mon, 12);
     252        year += _floor_div(mon, 12);
     253        mon = _floor_mod(mon, 12);
    274254
    275255        /* Now the difficult part - days of month. */
    276         /* Slow, but simple. */
    277         // FIXME: do this faster
    278 
    279         while (tm->tm_mday < 1) {
    280                 tm->tm_mon--;
    281                 if (tm->tm_mon == -1) {
    282                         tm->tm_mon = 11;
    283                         tm->tm_year--;
     256       
     257        /* First, deal with whole cycles of 400 years = 146097 days. */
     258        year += _floor_div(day, 146097) * 400;
     259        day = _floor_mod(day, 146097);
     260       
     261        /* Then, go in one year steps. */
     262        if (mon <= 1) {
     263                /* January and February. */
     264                while (day > 365) {
     265                        day -= _is_leap_year(year) ? 366 : 365;
     266                        year++;
    284267                }
    285                
    286                 tm->tm_mday += _days_in_month(tm->tm_year, tm->tm_mon);
    287         }
    288 
    289         while (tm->tm_mday > _days_in_month(tm->tm_year, tm->tm_mon)) {
    290                 tm->tm_mday -= _days_in_month(tm->tm_year, tm->tm_mon);
    291                
    292                 tm->tm_mon++;
    293                 if (tm->tm_mon == 12) {
    294                         tm->tm_mon = 0;
    295                         tm->tm_year++;
     268        } else {
     269                /* Rest of the year. */
     270                while (day > 365) {
     271                        day -= _is_leap_year(year + 1) ? 366 : 365;
     272                        year++;
    296273                }
    297274        }
    298 
     275       
     276        /* Finally, finish it off month per month. */
     277        while (day >= _days_in_month(year, mon)) {
     278                day -= _days_in_month(year, mon);
     279                mon++;
     280                if (mon >= 12) {
     281                        mon -= 12;
     282                        year++;
     283                }
     284        }
     285       
    299286        /* Calculate the remaining two fields. */
    300         tm->tm_yday = _day_of_year(tm->tm_year, tm->tm_mon, tm->tm_mday);
    301         tm->tm_wday = _day_of_week(tm->tm_year, tm->tm_mon, tm->tm_mday);
    302 }
    303 
    304 /**
    305  * Which day the week-based year starts on relative to the first calendar day.
     287        tm->tm_yday = _day_of_year(year, mon, day + 1);
     288        tm->tm_wday = _day_of_week(year, mon, day + 1);
     289       
     290        /* And put the values back to the struct. */
     291        tm->tm_sec = (int) sec;
     292        tm->tm_min = (int) min;
     293        tm->tm_hour = (int) hour;
     294        tm->tm_mday = (int) day + 1;
     295        tm->tm_mon = (int) mon;
     296       
     297        /* Casts to work around libc brain-damage. */
     298        if (year > ((int)INT_MAX) || year < ((int)INT_MIN)) {
     299                tm->tm_year = (year < 0) ? ((int)INT_MIN) : ((int)INT_MAX);
     300                return -1;
     301        }
     302       
     303        tm->tm_year = (int) year;
     304        return 0;
     305}
     306
     307/**
     308 * Which day the week-based year starts on, relative to the first calendar day.
    306309 * E.g. if the year starts on December 31st, the return value is -1.
    307310 *
    308  * @param year
    309  * @return
     311 * @param Year since 1900.
     312 * @return Offset of week-based year relative to calendar year.
    310313 */
    311314static int _wbyear_offset(int year)
     
    317320/**
    318321 * Returns week-based year of the specified time.
    319  * Assumes normalized broken-down time.
    320  *
    321  * @param tm
    322  * @return
     322 *
     323 * @param tm Normalized broken-down time.
     324 * @return Week-based year.
    323325 */
    324326static int _wbyear(const struct posix_tm *tm)
     
    329331                return tm->tm_year - 1;
    330332        }
    331         if (day > 364 + _is_leap_year(tm->tm_year)){
     333        if (day > 364 + _is_leap_year(tm->tm_year)) {
    332334                /* First week of next year. */
    333335                return tm->tm_year + 1;
     
    368370                return 53;
    369371        }
    370         if (day > 364 + _is_leap_year(tm->tm_year)){
     372        if (day > 364 + _is_leap_year(tm->tm_year)) {
    371373                /* First week of next year. */
    372374                return 1;
     
    397399
    398400/**
    399  *
     401 * Set timezone conversion information.
    400402 */
    401403void posix_tzset(void)
     
    409411
    410412/**
    411  *
    412  * @param time1
    413  * @param time0
    414  * @return
     413 * Calculate the difference between two times, in seconds.
     414 *
     415 * @param time1 First time.
     416 * @param time0 Second time.
     417 * @return Time in seconds.
    415418 */
    416419double posix_difftime(time_t time1, time_t time0)
     
    425428 *
    426429 * @param tm Broken-down time.
    427  * @return time_t representation of the time, undefined value on overflow
     430 * @return time_t representation of the time, undefined value on overflow.
    428431 */
    429432time_t posix_mktime(struct posix_tm *tm)
     
    432435        // TODO: detect overflow
    433436
    434         struct _long_tm ltm;
    435         _posix_to_long_tm(&ltm, tm);
    436         _normalize_time(&ltm);
    437         _long_to_posix_tm(tm, &ltm);
    438 
     437        _normalize_time(tm, 0);
    439438        return _secs_since_epoch(tm);
    440439}
    441440
    442441/**
    443  *
    444  * @param timer
    445  * @return
     442 * Converts a time value to a broken-down UTC time.
     443 *
     444 * @param timer Time to convert.
     445 * @return Normalized broken-down time in UTC, NULL on overflow.
    446446 */
    447447struct posix_tm *posix_gmtime(const time_t *timer)
    448448{
     449        assert(timer != NULL);
     450
    449451        static struct posix_tm result;
    450452        return posix_gmtime_r(timer, &result);
     
    452454
    453455/**
    454  *
    455  * @param timer
    456  * @param result
    457  * @return
     456 * Converts a time value to a broken-down UTC time.
     457 *
     458 * @param timer Time to convert.
     459 * @param result Structure to store the result to.
     460 * @return Value of result on success, NULL on overflow.
    458461 */
    459462struct posix_tm *posix_gmtime_r(const time_t *restrict timer,
     
    463466        assert(result != NULL);
    464467
    465         /* Set epoch and seconds to _long_tm struct and normalize to get
    466          * correct values.
    467          */
    468         struct _long_tm ltm = {
    469                 .tm_sec = *timer,
    470                 .tm_min = 0,
    471                 .tm_hour = 0, /* 00:00:xx */
    472                 .tm_mday = 1,
    473                 .tm_mon = 0, /* January 1st */
    474                 .tm_year = 70, /* 1970 */
    475         };
    476         _normalize_time(&ltm);
    477 
    478         if (ltm.tm_year < (int) INT_MIN || ltm.tm_year > (int) INT_MAX) {
     468        /* Set result to epoch. */
     469        result->tm_sec = 0;
     470        result->tm_min = 0;
     471        result->tm_hour = 0;
     472        result->tm_mday = 1;
     473        result->tm_mon = 0;
     474        result->tm_year = 70; /* 1970 */
     475
     476        if (_normalize_time(result, *timer) == -1) {
    479477                errno = EOVERFLOW;
    480478                return NULL;
    481479        }
    482480
    483         _long_to_posix_tm(result, &ltm);
    484481        return result;
    485482}
    486483
    487484/**
    488  *
    489  * @param timer
    490  * @return
     485 * Converts a time value to a broken-down local time.
     486 *
     487 * @param timer Time to convert.
     488 * @return Normalized broken-down time in local timezone, NULL on overflow.
    491489 */
    492490struct posix_tm *posix_localtime(const time_t *timer)
     
    497495
    498496/**
    499  *
    500  * @param timer
    501  * @param result
    502  * @return
     497 * Converts a time value to a broken-down local time.
     498 *
     499 * @param timer Time to convert.
     500 * @param result Structure to store the result to.
     501 * @return Value of result on success, NULL on overflow.
    503502 */
    504503struct posix_tm *posix_localtime_r(const time_t *restrict timer,
     
    511510
    512511/**
    513  *
    514  * @param timeptr
    515  * @return
     512 * Converts broken-down time to a string in format
     513 * "Sun Jan 1 00:00:00 1970\n". (Obsolete)
     514 *
     515 * @param timeptr Broken-down time structure.
     516 * @return Pointer to a statically allocated string.
    516517 */
    517518char *posix_asctime(const struct posix_tm *timeptr)
     
    522523
    523524/**
    524  *
    525  * @param timeptr
    526  * @param buf
    527  * @return
     525 * Converts broken-down time to a string in format
     526 * "Sun Jan 1 00:00:00 1970\n". (Obsolete)
     527 *
     528 * @param timeptr Broken-down time structure.
     529 * @param buf Buffer to store string to, must be at least ASCTIME_BUF_LEN
     530 *     bytes long.
     531 * @return Value of buf.
    528532 */
    529533char *posix_asctime_r(const struct posix_tm *restrict timeptr,
     
    552556
    553557/**
    554  *
    555  * @param timer
    556  * @return
     558 * Equivalent to asctime(localtime(clock)).
     559 *
     560 * @param timer Time to convert.
     561 * @return Pointer to a statically allocated string holding the date.
    557562 */
    558563char *posix_ctime(const time_t *timer)
     
    566571
    567572/**
    568  *
    569  * @param timer
    570  * @param buf
    571  * @return
     573 * Reentrant variant of ctime().
     574 *
     575 * @param timer Time to convert.
     576 * @param buf Buffer to store string to. Must be at least ASCTIME_BUF_LEN
     577 *     bytes long.
     578 * @return Pointer to buf on success, NULL on falure.
    572579 */
    573580char *posix_ctime_r(const time_t *timer, char *buf)
     
    581588
    582589/**
    583  *
    584  * @param s
    585  * @param maxsize
    586  * @param format
    587  * @param tm
    588  * @return
     590 * Convert time and date to a string, based on a specified format and
     591 * current locale.
     592 *
     593 * @param s Buffer to write string to.
     594 * @param maxsize Size of the buffer.
     595 * @param format Format of the output.
     596 * @param tm Broken-down time to format.
     597 * @return Number of bytes written.
    589598 */
    590599size_t posix_strftime(char *restrict s, size_t maxsize,
    591600    const char *restrict format, const struct posix_tm *restrict tm)
    592601{
     602        assert(s != NULL);
     603        assert(format != NULL);
     604        assert(tm != NULL);
     605
    593606        // TODO: use locale
    594607        static const char *wday_abbr[] = {
     
    767780
    768781/**
    769  *
    770  * @param s
    771  * @param maxsize
    772  * @param format
    773  * @param tm
    774  * @param loc
    775  * @return
    776  */
    777 extern size_t posix_strftime_l(char *restrict s, size_t maxsize,
    778     const char *restrict format, const struct posix_tm *restrict tm,
    779     posix_locale_t loc)
    780 {
    781         // TODO
    782         not_implemented();
    783 }
    784 
    785 /**
    786  *
    787  * @param clock_id
    788  * @param res
    789  * @return
     782 * Get clock resolution. Only CLOCK_REALTIME is supported.
     783 *
     784 * @param clock_id Clock ID.
     785 * @param res Pointer to the variable where the resolution is to be written.
     786 * @return 0 on success, -1 with errno set on failure.
    790787 */
    791788int posix_clock_getres(posix_clockid_t clock_id, struct posix_timespec *res)
     
    805802
    806803/**
    807  *
    808  * @param clock_id
    809  * @param tp
    810  * @return
     804 * Get time. Only CLOCK_REALTIME is supported.
     805 *
     806 * @param clock_id ID of the clock to query.
     807 * @param tp Pointer to the variable where the time is to be written.
     808 * @return 0 on success, -1 with errno on failure.
    811809 */
    812810int posix_clock_gettime(posix_clockid_t clock_id, struct posix_timespec *tp)
     
    829827
    830828/**
    831  *
    832  * @param clock_id
    833  * @param tp
    834  * @return
     829 * Set time on a specified clock. As HelenOS doesn't support this yet,
     830 * this function always fails.
     831 *
     832 * @param clock_id ID of the clock to set.
     833 * @param tp Time to set.
     834 * @return 0 on success, -1 with errno on failure.
    835835 */
    836836int posix_clock_settime(posix_clockid_t clock_id,
     
    853853
    854854/**
    855  *
    856  * @param clock_id
    857  * @param flags
    858  * @param rqtp
    859  * @param rmtp
    860  * @return
     855 * Sleep on a specified clock.
     856 *
     857 * @param clock_id ID of the clock to sleep on (only CLOCK_REALTIME supported).
     858 * @param flags Flags (none supported).
     859 * @param rqtp Sleep time.
     860 * @param rmtp Remaining time is written here if sleep is interrupted.
     861 * @return 0 on success, -1 with errno set on failure.
    861862 */
    862863int posix_clock_nanosleep(posix_clockid_t clock_id, int flags,
     
    882883}
    883884
    884 #if 0
    885 
    886 struct __posix_timer {
    887         posix_clockid_t clockid;
    888         struct posix_sigevent evp;
    889 };
    890 
    891 /**
    892  *
    893  * @param clockid
    894  * @param evp
    895  * @param timerid
    896  * @return
    897  */
    898 int posix_timer_create(posix_clockid_t clockid,
    899     struct posix_sigevent *restrict evp,
    900     posix_timer_t *restrict timerid)
    901 {
    902         // TODO
    903         not_implemented();
    904 }
    905 
    906 /**
    907  *
    908  * @param timerid
    909  * @return
    910  */
    911 int posix_timer_delete(posix_timer_t timerid)
    912 {
    913         // TODO
    914         not_implemented();
    915 }
    916 
    917 /**
    918  *
    919  * @param timerid
    920  * @return
    921  */
    922 int posix_timer_getoverrun(posix_timer_t timerid)
    923 {
    924         // TODO
    925         not_implemented();
    926 }
    927 
    928 /**
    929  *
    930  * @param timerid
    931  * @param value
    932  * @return
    933  */
    934 int posix_timer_gettime(posix_timer_t timerid,
    935     struct posix_itimerspec *value)
    936 {
    937         // TODO
    938         not_implemented();
    939 }
    940 
    941 /**
    942  *
    943  * @param timerid
    944  * @param flags
    945  * @param value
    946  * @param ovalue
    947  * @return
    948  */
    949 int posix_timer_settime(posix_timer_t timerid, int flags,
    950     const struct posix_itimerspec *restrict value,
    951     struct posix_itimerspec *restrict ovalue)
    952 {
    953         // TODO
    954         not_implemented();
    955 }
    956 
    957 #endif
    958 
    959885/**
    960886 * Get CPU time used since the process invocation.
Note: See TracChangeset for help on using the changeset viewer.