Ignore:
File:
1 edited

Legend:

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

    r55b1efd ra12f7f1  
    5151#include "libc/sys/time.h"
    5252
     53// TODO: documentation
    5354// 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 
    6355
    6456/* Helper functions ***********************************************************/
     
    7264
    7365/**
    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
     66 *
     67 * @param year
     68 * @return
    7869 */
    7970static bool _is_leap_year(time_t year)
     
    9182
    9283/**
    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.
     84 *
     85 * @param year
     86 * @param mon
     87 * @return
    9988 */
    10089static int _days_in_month(time_t year, time_t mon)
    10190{
    10291        assert(mon >= 0 && mon <= 11);
     92        year += 1900;
    10393
    10494        static int month_days[] =
     
    10696
    10797        if (mon == 1) {
    108                 year += 1900;
    10998                /* february */
    11099                return _is_leap_year(year) ? 29 : 28;
     
    115104
    116105/**
    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).
     106 *
     107 * @param year
     108 * @param mon
     109 * @param mday
     110 * @return
    127111 */
    128112static int _day_of_year(time_t year, time_t mon, time_t mday)
     
    138122/**
    139123 * Integer division that rounds to negative infinity.
    140  * Used by some functions in this file.
    141  *
    142  * @param op1 Divident.
    143  * @param op2 Divisor.
    144  * @return Rounded quotient.
     124 *
     125 * @param op1
     126 * @param op2
     127 * @return
    145128 */
    146129static time_t _floor_div(time_t op1, time_t op2)
     
    155138/**
    156139 * Modulo that rounds to negative infinity.
    157  * Used by some functions in this file.
    158  *
    159  * @param op1 Divident.
    160  * @param op2 Divisor.
    161  * @return Remainder.
     140 *
     141 * @param op1
     142 * @param op2
     143 * @return
    162144 */
    163145static time_t _floor_mod(time_t op1, time_t op2)
     
    179161
    180162/**
    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.
     163 *
     164 * @param year
     165 * @param mon
     166 * @param mday
     167 * @return
    188168 */
    189169static time_t _days_since_epoch(time_t year, time_t mon, time_t mday)
     
    195175
    196176/**
    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.
     177 * Assumes normalized broken-down time.
     178 *
     179 * @param tm
     180 * @return
    201181 */
    202182static time_t _secs_since_epoch(const struct posix_tm *tm)
     
    208188
    209189/**
    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).
     190 *
     191 * @param year
     192 * @param mon
     193 * @param mday
     194 * @return
    216195 */
    217196static int _day_of_week(time_t year, time_t mon, time_t mday)
    218197{
    219198        /* 1970-01-01 is Thursday */
    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  */
    231 static int _normalize_time(struct posix_tm *tm, time_t sec_add)
     199        return (_days_since_epoch(year, mon, mday) + 4) % 7;
     200}
     201
     202struct _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 */
     219static 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 */
     238static 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 */
     259static void _normalize_time(struct _long_tm *tm)
    232260{
    233261        // TODO: DST correction
    234262
    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 
    243263        /* Adjust time. */
    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);
     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);
    250270
    251271        /* Adjust month. */
    252         year += _floor_div(mon, 12);
    253         mon = _floor_mod(mon, 12);
     272        tm->tm_year += _floor_div(tm->tm_mon, 12);
     273        tm->tm_mon = _floor_mod(tm->tm_mon, 12);
    254274
    255275        /* Now the difficult part - days of month. */
    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++;
     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--;
    267284                }
    268         } else {
    269                 /* Rest of the year. */
    270                 while (day > 365) {
    271                         day -= _is_leap_year(year + 1) ? 366 : 365;
    272                         year++;
     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++;
    273296                }
    274297        }
    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        
     298
    286299        /* Calculate the remaining two fields. */
    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.
     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.
    309306 * E.g. if the year starts on December 31st, the return value is -1.
    310307 *
    311  * @param Year since 1900.
    312  * @return Offset of week-based year relative to calendar year.
     308 * @param year
     309 * @return
    313310 */
    314311static int _wbyear_offset(int year)
     
    320317/**
    321318 * Returns week-based year of the specified time.
    322  *
    323  * @param tm Normalized broken-down time.
    324  * @return Week-based year.
     319 * Assumes normalized broken-down time.
     320 *
     321 * @param tm
     322 * @return
    325323 */
    326324static int _wbyear(const struct posix_tm *tm)
     
    331329                return tm->tm_year - 1;
    332330        }
    333         if (day > 364 + _is_leap_year(tm->tm_year)) {
     331        if (day > 364 + _is_leap_year(tm->tm_year)){
    334332                /* First week of next year. */
    335333                return tm->tm_year + 1;
     
    370368                return 53;
    371369        }
    372         if (day > 364 + _is_leap_year(tm->tm_year)) {
     370        if (day > 364 + _is_leap_year(tm->tm_year)){
    373371                /* First week of next year. */
    374372                return 1;
     
    399397
    400398/**
    401  * Set timezone conversion information.
     399 *
    402400 */
    403401void posix_tzset(void)
     
    411409
    412410/**
    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.
     411 *
     412 * @param time1
     413 * @param time0
     414 * @return
    418415 */
    419416double posix_difftime(time_t time1, time_t time0)
     
    428425 *
    429426 * @param tm Broken-down time.
    430  * @return time_t representation of the time, undefined value on overflow.
     427 * @return time_t representation of the time, undefined value on overflow
    431428 */
    432429time_t posix_mktime(struct posix_tm *tm)
     
    435432        // TODO: detect overflow
    436433
    437         _normalize_time(tm, 0);
     434        struct _long_tm ltm;
     435        _posix_to_long_tm(&ltm, tm);
     436        _normalize_time(&ltm);
     437        _long_to_posix_tm(tm, &ltm);
     438
    438439        return _secs_since_epoch(tm);
    439440}
    440441
    441442/**
    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.
     443 *
     444 * @param timer
     445 * @return
    446446 */
    447447struct posix_tm *posix_gmtime(const time_t *timer)
    448448{
    449         assert(timer != NULL);
    450 
    451449        static struct posix_tm result;
    452450        return posix_gmtime_r(timer, &result);
     
    454452
    455453/**
    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.
     454 *
     455 * @param timer
     456 * @param result
     457 * @return
    461458 */
    462459struct posix_tm *posix_gmtime_r(const time_t *restrict timer,
     
    466463        assert(result != NULL);
    467464
    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) {
     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) {
    477479                errno = EOVERFLOW;
    478480                return NULL;
    479481        }
    480482
     483        _long_to_posix_tm(result, &ltm);
    481484        return result;
    482485}
    483486
    484487/**
    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.
     488 *
     489 * @param timer
     490 * @return
    489491 */
    490492struct posix_tm *posix_localtime(const time_t *timer)
     
    495497
    496498/**
    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.
     499 *
     500 * @param timer
     501 * @param result
     502 * @return
    502503 */
    503504struct posix_tm *posix_localtime_r(const time_t *restrict timer,
     
    510511
    511512/**
    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.
     513 *
     514 * @param timeptr
     515 * @return
    517516 */
    518517char *posix_asctime(const struct posix_tm *timeptr)
     
    523522
    524523/**
    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.
     524 *
     525 * @param timeptr
     526 * @param buf
     527 * @return
    532528 */
    533529char *posix_asctime_r(const struct posix_tm *restrict timeptr,
     
    556552
    557553/**
    558  * Equivalent to asctime(localtime(clock)).
    559  *
    560  * @param timer Time to convert.
    561  * @return Pointer to a statically allocated string holding the date.
     554 *
     555 * @param timer
     556 * @return
    562557 */
    563558char *posix_ctime(const time_t *timer)
     
    571566
    572567/**
    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.
     568 *
     569 * @param timer
     570 * @param buf
     571 * @return
    579572 */
    580573char *posix_ctime_r(const time_t *timer, char *buf)
     
    588581
    589582/**
    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.
     583 *
     584 * @param s
     585 * @param maxsize
     586 * @param format
     587 * @param tm
     588 * @return
    598589 */
    599590size_t posix_strftime(char *restrict s, size_t maxsize,
    600591    const char *restrict format, const struct posix_tm *restrict tm)
    601592{
    602         assert(s != NULL);
    603         assert(format != NULL);
    604         assert(tm != NULL);
    605 
    606593        // TODO: use locale
    607594        static const char *wday_abbr[] = {
     
    780767
    781768/**
    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.
     769 *
     770 * @param s
     771 * @param maxsize
     772 * @param format
     773 * @param tm
     774 * @param loc
     775 * @return
     776 */
     777extern 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
    787790 */
    788791int posix_clock_getres(posix_clockid_t clock_id, struct posix_timespec *res)
     
    802805
    803806/**
    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.
     807 *
     808 * @param clock_id
     809 * @param tp
     810 * @return
    809811 */
    810812int posix_clock_gettime(posix_clockid_t clock_id, struct posix_timespec *tp)
     
    827829
    828830/**
    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.
     831 *
     832 * @param clock_id
     833 * @param tp
     834 * @return
    835835 */
    836836int posix_clock_settime(posix_clockid_t clock_id,
     
    853853
    854854/**
    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.
     855 *
     856 * @param clock_id
     857 * @param flags
     858 * @param rqtp
     859 * @param rmtp
     860 * @return
    862861 */
    863862int posix_clock_nanosleep(posix_clockid_t clock_id, int flags,
     
    883882}
    884883
     884#if 0
     885
     886struct __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 */
     898int 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 */
     911int posix_timer_delete(posix_timer_t timerid)
     912{
     913        // TODO
     914        not_implemented();
     915}
     916
     917/**
     918 *
     919 * @param timerid
     920 * @return
     921 */
     922int 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 */
     934int 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 */
     949int 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
    885959/**
    886960 * Get CPU time used since the process invocation.
Note: See TracChangeset for help on using the changeset viewer.