Ignore:
File:
1 edited

Legend:

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

    r1ab8539 rbf9cb2f  
    5454#include <malloc.h>
    5555
    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
    6457
    6558/** Pointer to kernel shared variables with time */
     
    7063} *ktime = NULL;
    7164
    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.
    7576 *
    7677 * @param year Year since 1900 (e.g. for 1970, the value is 70).
    77  *
    7878 * @return true if year is a leap year, false otherwise
    79  *
    80  */
    81 static bool is_leap_year(time_t year)
     79 */
     80static bool _is_leap_year(time_t year)
    8281{
    8382        year += 1900;
    84        
     83
    8584        if (year % 400 == 0)
    8685                return true;
    87        
    8886        if (year % 100 == 0)
    8987                return false;
    90        
    9188        if (year % 4 == 0)
    9289                return true;
    93        
    9490        return false;
    9591}
    9692
    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.
    10095 * Note that year is only taken into account if month is February.
    10196 *
    10297 * @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.
    10599 * @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 */
     101static 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
    117108        if (mon == 1) {
    118                 /* February */
    119109                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 year
     110                /* 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
    129119 * it is.
    130120 *
    131121 * For example, given date 2011-01-03, the corresponding expression is:
    132  * day_of_year(111, 0, 3) == 2
     122 *     _day_of_year(111, 0, 3) == 2
    133123 *
    134124 * @param year Year (year 1900 = 0, can be negative).
    135  * @param mon  Month (January = 0).
     125 * @param mon Month (January = 0).
    136126 * @param mday Day of month (First day is 1).
    137  *
    138127 * @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 */
     129static 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.
    157142 *
    158143 * @param op1 Dividend.
    159144 * @param op2 Divisor.
    160  *
    161145 * @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 */
     147static time_t _floor_div(time_t op1, time_t op2)
     148{
     149        if (op1 >= 0 || op1 % op2 == 0) {
    167150                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.
    175159 *
    176160 * @param op1 Dividend.
    177161 * @param op2 Divisor.
    178  *
    179162 * @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 */
     164static 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. */
    194174        assert(result >= 0);
    195175        assert(result < op2);
     
    199179}
    200180
    201 /** Number of days since the Epoch.
    202  *
     181/**
     182 * Number of days since the Epoch.
    203183 * Epoch is 1970-01-01, which is also equal to day 0.
    204184 *
    205185 * @param year Year (year 1900 = 0, may be negative).
    206  * @param mon  Month (January = 0).
     186 * @param mon Month (January = 0).
    207187 * @param mday Day of month (first day = 1).
    208  *
    209188 * @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 */
     190static 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 *
    223200 * @param tm Normalized broken-down time.
    224  *
    225201 * @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 */
     203static time_t _secs_since_epoch(const struct tm *tm)
     204{
     205        return _days_since_epoch(tm->tm_year, tm->tm_mon, tm->tm_mday) *
    231206            SECS_PER_DAY + tm->tm_hour * SECS_PER_HOUR +
    232207            tm->tm_min * SECS_PER_MIN + tm->tm_sec;
    233208}
    234209
    235 /** Which day of week the specified date is.
    236  *
     210/**
     211 * Which day of week the specified date is.
     212 *
    237213 * @param year Year (year 1900 = 0).
    238  * @param mon  Month (January = 0).
     214 * @param mon Month (January = 0).
    239215 * @param mday Day of month (first = 1).
    240  *
    241216 * @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 */
     218static int _day_of_week(time_t year, time_t mon, time_t mday)
    245219{
    246220        /* 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 of seconds.
    253  *
    254  * @param tm      Broken-down time to normalize.
     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.
    255229 * @param sec_add Seconds to add.
    256  *
    257230 * @return 0 on success, -1 on overflow
    258  *
    259  */
    260 static int normalize_time(struct tm *tm, time_t sec_add)
     231 */
     232static int _normalize_time(struct tm *tm, time_t sec_add)
    261233{
    262234        // TODO: DST correction
    263        
     235
    264236        /* Set initial values. */
    265237        time_t sec = tm->tm_sec + sec_add;
     
    269241        time_t mon = tm->tm_mon;
    270242        time_t year = tm->tm_year;
    271        
     243
    272244        /* 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
    280252        /* 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
    284256        /* Now the difficult part - days of month. */
    285257       
    286258        /* 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);
    289261       
    290262        /* Then, go in one year steps. */
     
    292264                /* January and February. */
    293265                while (day > 365) {
    294                         day -= is_leap_year(year) ? 366 : 365;
     266                        day -= _is_leap_year(year) ? 366 : 365;
    295267                        year++;
    296268                }
     
    298270                /* Rest of the year. */
    299271                while (day > 365) {
    300                         day -= is_leap_year(year + 1) ? 366 : 365;
     272                        day -= _is_leap_year(year + 1) ? 366 : 365;
    301273                        year++;
    302274                }
     
    304276       
    305277        /* 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);
    308280                mon++;
    309                
    310281                if (mon >= 12) {
    311282                        mon -= 12;
     
    315286       
    316287        /* 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);
    319290       
    320291        /* And put the values back to the struct. */
     
    325296        tm->tm_mon = (int) mon;
    326297       
    327         /* Casts to work around POSIX brain-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);
    330301                return -1;
    331302        }
     
    335306}
    336307
    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.
    341311 *
    342312 * @param Year since 1900.
    343  *
    344313 * @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 */
     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.
    355323 *
    356324 * @param tm Normalized broken-down time.
    357  *
    358325 * @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 */
     327static int _wbyear(const struct tm *tm)
     328{
     329        int day = tm->tm_yday - _wbyear_offset(tm->tm_year);
    365330        if (day < 0) {
    366331                /* Last week of previous year. */
    367332                return tm->tm_year - 1;
    368333        }
    369        
    370         if (day > 364 + is_leap_year(tm->tm_year)) {
     334        if (day > 364 + _is_leap_year(tm->tm_year)) {
    371335                /* First week of next year. */
    372336                return tm->tm_year + 1;
    373337        }
    374        
    375338        /* All the other days are in the calendar year. */
    376339        return tm->tm_year;
    377340}
    378341
    379 /** Week number of the year (assuming weeks start on Sunday).
    380  *
     342/**
     343 * Week number of the year, assuming weeks start on sunday.
    381344 * The first Sunday of January is the first day of week 1;
    382345 * days in the new year before this are in week 0.
    383346 *
    384347 * @param tm Normalized broken-down time.
    385  *
    386348 * @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 */
     350static int _sun_week_number(const struct tm *tm)
     351{
     352        int first_day = (7 - _day_of_week(tm->tm_year, 0, 1)) % 7;
    393353        return (tm->tm_yday - first_day + 7) / 7;
    394354}
    395355
    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
    402361 * of January are always in week 1.
    403362 *
    404363 * @param tm Normalized broken-down time.
    405  *
    406364 * @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 */
     366static int _iso_week_number(const struct tm *tm)
     367{
     368        int day = tm->tm_yday - _wbyear_offset(tm->tm_year);
    413369        if (day < 0) {
    414370                /* Last week of previous year. */
    415371                return 53;
    416372        }
    417        
    418         if (day > 364 + is_leap_year(tm->tm_year)) {
     373        if (day > 364 + _is_leap_year(tm->tm_year)) {
    419374                /* First week of next year. */
    420375                return 1;
    421376        }
    422        
    423377        /* All the other days give correct answer. */
    424378        return (day / 7 + 1);
    425379}
    426380
    427 /** Week number of the year (assuming weeks start on Monday).
    428  *
     381/**
     382 * Week number of the year, assuming weeks start on monday.
    429383 * 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. 
    431385 *
    432386 * @param tm Normalized broken-down time.
    433  *
    434387 * @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 */
     389static int _mon_week_number(const struct tm *tm)
     390{
     391        int first_day = (1 - _day_of_week(tm->tm_year, 0, 1)) % 7;
    441392        return (tm->tm_yday - first_day + 7) / 7;
    442393}
     394
     395/******************************************************************************/
     396
    443397
    444398/** Add microseconds to given timeval.
     
    514468}
    515469
    516 /** Get time of day.
     470/** Get time of day
    517471 *
    518472 * The time variables are memory mapped (read-only) from kernel which
     
    528482 *
    529483 */
    530 void gettimeofday(struct timeval *tv, struct timezone *tz)
    531 {
     484int 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
    532496        if (tz) {
    533497                tz->tz_minuteswest = 0;
    534498                tz->tz_dsttime = DST_NONE;
    535499        }
    536        
     500
    537501        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);
    540503                if (rc != EOK)
    541                         goto fallback;
    542                
    543                 service_id_t *svc_ids;
    544                 size_t svc_cnt;
     504                        goto ret_uptime;
     505
    545506                rc = loc_category_get_svcs(cat_id, &svc_ids, &svc_cnt);
    546507                if (rc != EOK)
    547                         goto fallback;
    548                
     508                        goto ret_uptime;
     509
    549510                if (svc_cnt == 0)
    550                         goto fallback;
    551                
    552                 char *svc_name;
     511                        goto ret_uptime;
     512
    553513                rc = loc_service_get_name(svc_ids[0], &svc_name);
    554                 free(svc_ids);
    555514                if (rc != EOK)
    556                         goto fallback;
    557                
    558                 service_id_t svc_id;
     515                        goto ret_uptime;
     516
    559517                rc = loc_service_get_id(svc_name, &svc_id, 0);
    560                 free(svc_name);
    561518                if (rc != EOK)
    562                         goto fallback;
    563                
     519                        goto ret_uptime;
     520
    564521                clock_conn = loc_service_connect(EXCHANGE_SERIALIZE,
    565522                    svc_id, IPC_FLAG_BLOCKING);
    566523                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);
    572528        if (rc != EOK)
    573                 goto fallback;
    574        
     529                goto ret_uptime;
     530
    575531        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
     539ret_uptime:
     540
     541        free(svc_name);
     542        free(svc_ids);
     543
     544        return getuptime(tv);
     545}
     546
     547int getuptime(struct timeval *tv)
    585548{
    586549        if (ktime == NULL) {
     
    589552                if (rc != EOK) {
    590553                        errno = rc;
    591                         goto fallback;
     554                        return -1;
    592555                }
    593556               
     
    598561                        as_area_destroy(addr);
    599562                        errno = rc;
    600                         goto fallback;
     563                        return -1;
    601564                }
    602565               
     
    617580        } else
    618581                tv->tv_sec = s1;
    619        
    620         return;
    621        
    622 fallback:
    623         tv->tv_sec = 0;
    624         tv->tv_usec = 0;
     582
     583        return 0;
    625584}
    626585
     
    628587{
    629588        struct timeval tv;
    630         gettimeofday(&tv, NULL);
     589        if (gettimeofday(&tv, NULL))
     590                return (time_t) -1;
    631591       
    632592        if (tloc)
     
    671631}
    672632
    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.
    679637 *
    680638 * @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.
    685640 */
    686641time_t mktime(struct tm *tm)
     
    688643        // TODO: take DST flag into account
    689644        // 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.
    726655 * @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.
    730658 * @return Number of bytes written.
    731  *
    732659 */
    733660size_t strftime(char *restrict s, size_t maxsize,
     
    737664        assert(format != NULL);
    738665        assert(tm != NULL);
    739        
     666
    740667        // TODO: use locale
    741        
    742668        static const char *wday_abbr[] = {
    743669                "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
    744670        };
    745        
    746671        static const char *wday[] = {
    747672                "Sunday", "Monday", "Tuesday", "Wednesday",
    748673                "Thursday", "Friday", "Saturday"
    749674        };
    750        
    751675        static const char *mon_abbr[] = {
    752676                "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    753677                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    754678        };
    755        
    756679        static const char *mon[] = {
    757680                "January", "February", "March", "April", "May", "June", "July",
     
    759682        };
    760683       
    761         if (maxsize < 1)
     684        if (maxsize < 1) {
    762685                return 0;
     686        }
    763687       
    764688        char *ptr = s;
     
    766690        size_t remaining = maxsize;
    767691       
     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       
    768715        while (*format != '\0') {
    769716                if (*format != '%') {
    770                         APPEND("%c", *format);
     717                        append("%c", *format);
    771718                        format++;
    772719                        continue;
     
    774721               
    775722                format++;
    776                 if ((*format == '0') || (*format == '+')) {
     723                if (*format == '0' || *format == '+') {
    777724                        // TODO: padding
    778725                        format++;
    779726                }
    780                
    781727                while (isdigit(*format)) {
    782728                        // TODO: padding
    783729                        format++;
    784730                }
    785                
    786                 if ((*format == 'O') || (*format == 'E')) {
     731                if (*format == 'O' || *format == 'E') {
    787732                        // TODO: locale's alternative format
    788733                        format++;
     
    791736                switch (*format) {
    792737                case 'a':
    793                         APPEND("%s", wday_abbr[tm->tm_wday]);
    794                         break;
     738                        append("%s", wday_abbr[tm->tm_wday]); break;
    795739                case 'A':
    796                         APPEND("%s", wday[tm->tm_wday]);
    797                         break;
     740                        append("%s", wday[tm->tm_wday]); break;
    798741                case 'b':
    799                         APPEND("%s", mon_abbr[tm->tm_mon]);
    800                         break;
     742                        append("%s", mon_abbr[tm->tm_mon]); break;
    801743                case 'B':
    802                         APPEND("%s", mon[tm->tm_mon]);
    803                         break;
     744                        append("%s", mon[tm->tm_mon]); break;
    804745                case 'c':
    805746                        // TODO: locale-specific datetime format
    806                         RECURSE("%Y-%m-%d %H:%M:%S");
    807                         break;
     747                        recurse("%Y-%m-%d %H:%M:%S"); break;
    808748                case 'C':
    809                         APPEND("%02d", (1900 + tm->tm_year) / 100);
    810                         break;
     749                        append("%02d", (1900 + tm->tm_year) / 100); break;
    811750                case 'd':
    812                         APPEND("%02d", tm->tm_mday);
    813                         break;
     751                        append("%02d", tm->tm_mday); break;
    814752                case 'D':
    815                         RECURSE("%m/%d/%y");
    816                         break;
     753                        recurse("%m/%d/%y"); break;
    817754                case 'e':
    818                         APPEND("%2d", tm->tm_mday);
    819                         break;
     755                        append("%2d", tm->tm_mday); break;
    820756                case 'F':
    821                         RECURSE("%+4Y-%m-%d");
    822                         break;
     757                        recurse("%+4Y-%m-%d"); break;
    823758                case 'g':
    824                         APPEND("%02d", wbyear(tm) % 100);
    825                         break;
     759                        append("%02d", _wbyear(tm) % 100); break;
    826760                case 'G':
    827                         APPEND("%d", wbyear(tm));
    828                         break;
     761                        append("%d", _wbyear(tm)); break;
    829762                case 'h':
    830                         RECURSE("%b");
    831                         break;
     763                        recurse("%b"); break;
    832764                case 'H':
    833                         APPEND("%02d", tm->tm_hour);
    834                         break;
     765                        append("%02d", tm->tm_hour); break;
    835766                case 'I':
    836                         APPEND("%02d", TO_12H(tm->tm_hour));
    837                         break;
     767                        append("%02d", TO_12H(tm->tm_hour)); break;
    838768                case 'j':
    839                         APPEND("%03d", tm->tm_yday);
    840                         break;
     769                        append("%03d", tm->tm_yday); break;
    841770                case 'k':
    842                         APPEND("%2d", tm->tm_hour);
    843                         break;
     771                        append("%2d", tm->tm_hour); break;
    844772                case 'l':
    845                         APPEND("%2d", TO_12H(tm->tm_hour));
    846                         break;
     773                        append("%2d", TO_12H(tm->tm_hour)); break;
    847774                case 'm':
    848                         APPEND("%02d", tm->tm_mon);
    849                         break;
     775                        append("%02d", tm->tm_mon); break;
    850776                case 'M':
    851                         APPEND("%02d", tm->tm_min);
    852                         break;
     777                        append("%02d", tm->tm_min); break;
    853778                case 'n':
    854                         APPEND("\n");
    855                         break;
     779                        append("\n"); break;
    856780                case 'p':
    857                         APPEND("%s", tm->tm_hour < 12 ? "AM" : "PM");
    858                         break;
     781                        append("%s", tm->tm_hour < 12 ? "AM" : "PM"); break;
    859782                case 'P':
    860                         APPEND("%s", tm->tm_hour < 12 ? "am" : "PM");
    861                         break;
     783                        append("%s", tm->tm_hour < 12 ? "am" : "PM"); break;
    862784                case 'r':
    863                         RECURSE("%I:%M:%S %p");
    864                         break;
     785                        recurse("%I:%M:%S %p"); break;
    865786                case 'R':
    866                         RECURSE("%H:%M");
    867                         break;
     787                        recurse("%H:%M"); break;
    868788                case 's':
    869                         APPEND("%ld", secs_since_epoch(tm));
    870                         break;
     789                        append("%ld", _secs_since_epoch(tm)); break;
    871790                case 'S':
    872                         APPEND("%02d", tm->tm_sec);
    873                         break;
     791                        append("%02d", tm->tm_sec); break;
    874792                case 't':
    875                         APPEND("\t");
    876                         break;
     793                        append("\t"); break;
    877794                case 'T':
    878                         RECURSE("%H:%M:%S");
    879                         break;
     795                        recurse("%H:%M:%S"); break;
    880796                case 'u':
    881                         APPEND("%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);
     797                        append("%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);
    882798                        break;
    883799                case 'U':
    884                         APPEND("%02d", sun_week_number(tm));
    885                         break;
     800                        append("%02d", _sun_week_number(tm)); break;
    886801                case 'V':
    887                         APPEND("%02d", iso_week_number(tm));
    888                         break;
     802                        append("%02d", _iso_week_number(tm)); break;
    889803                case 'w':
    890                         APPEND("%d", tm->tm_wday);
    891                         break;
     804                        append("%d", tm->tm_wday); break;
    892805                case 'W':
    893                         APPEND("%02d", mon_week_number(tm));
    894                         break;
     806                        append("%02d", _mon_week_number(tm)); break;
    895807                case 'x':
    896808                        // TODO: locale-specific date format
    897                         RECURSE("%Y-%m-%d");
    898                         break;
     809                        recurse("%Y-%m-%d"); break;
    899810                case 'X':
    900811                        // TODO: locale-specific time format
    901                         RECURSE("%H:%M:%S");
    902                         break;
     812                        recurse("%H:%M:%S"); break;
    903813                case 'y':
    904                         APPEND("%02d", tm->tm_year % 100);
    905                         break;
     814                        append("%02d", tm->tm_year % 100); break;
    906815                case 'Y':
    907                         APPEND("%d", 1900 + tm->tm_year);
    908                         break;
     816                        append("%d", 1900 + tm->tm_year); break;
    909817                case 'z':
    910818                        // TODO: timezone
     
    914822                        break;
    915823                case '%':
    916                         APPEND("%%");
     824                        append("%%");
    917825                        break;
    918826                default:
    919827                        /* Invalid specifier, print verbatim. */
    920                         while (*format != '%')
     828                        while (*format != '%') {
    921829                                format--;
    922                        
    923                         APPEND("%%");
     830                        }
     831                        append("%%");
    924832                        break;
    925833                }
    926                
    927834                format++;
    928835        }
    929836       
     837        #undef append
     838        #undef recurse
     839       
    930840        return maxsize - remaining;
    931841}
    932842
    933 /** Convert a time value to a broken-down UTC time/
    934  *
    935  * @param time   Time to convert
    936  * @param result Structure to store the result to
    937  *
    938  * @return EOK or a negative error code
    939  *
     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
    940850 */
    941851int time_utc2tm(const time_t time, struct tm *restrict result)
    942852{
    943853        assert(result != NULL);
    944        
     854
    945855        /* Set result to epoch. */
    946856        result->tm_sec = 0;
     
    950860        result->tm_mon = 0;
    951861        result->tm_year = 70; /* 1970 */
    952        
    953         if (normalize_time(result, time) == -1)
     862
     863        if (_normalize_time(result, time) == -1)
    954864                return EOVERFLOW;
    955        
     865
    956866        return EOK;
    957867}
    958868
    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.
    969877 */
    970878int time_utc2str(const time_t time, char *restrict buf)
    971879{
    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);
    978887        return EOK;
    979888}
    980889
    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)
    984894 *
    985895 * @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.
    989898 */
    990899void time_tm2str(const struct tm *restrict timeptr, char *restrict buf)
     
    992901        assert(timeptr != NULL);
    993902        assert(buf != NULL);
    994        
     903
    995904        static const char *wday[] = {
    996905                "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
    997906        };
    998        
    999907        static const char *mon[] = {
    1000908                "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    1001909                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    1002910        };
    1003        
     911
    1004912        snprintf(buf, ASCTIME_BUF_LEN, "%s %s %2d %02d:%02d:%02d %d\n",
    1005913            wday[timeptr->tm_wday],
     
    1010918}
    1011919
    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.
    1021928 */
    1022929int time_local2tm(const time_t time, struct tm *restrict result)
    1023930{
    1024         // TODO: Deal with timezones.
    1025         //       Currently assumes system and all times are in UTC
    1026        
     931        // TODO: deal with timezone
     932        // currently assumes system and all times are in GMT
     933
    1027934        /* Set result to epoch. */
    1028935        result->tm_sec = 0;
     
    1032939        result->tm_mon = 0;
    1033940        result->tm_year = 70; /* 1970 */
    1034        
    1035         if (normalize_time(result, time) == -1)
     941
     942        if (_normalize_time(result, time) == -1)
    1036943                return EOVERFLOW;
    1037        
     944
    1038945        return EOK;
    1039946}
    1040947
    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 the
     948/**
     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
    1044951 * 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.
    1052958 */
    1053959int time_local2str(const time_t time, char *buf)
    1054960{
    1055961        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
    1060967        time_tm2str(&loctime, buf);
     968
    1061969        return EOK;
    1062970}
    1063971
    1064 /** Calculate the difference between two times, in seconds.
    1065  *
     972/**
     973 * Calculate the difference between two times, in seconds.
     974 *
    1066975 * @param time1 First time.
    1067976 * @param time0 Second time.
    1068  *
    1069  * @return Time difference in seconds.
    1070  *
     977 * @return Time in seconds.
    1071978 */
    1072979double difftime(time_t time1, time_t time0)
Note: See TracChangeset for help on using the changeset viewer.