Changeset b412168 in mainline for uspace/lib/c/generic/time.c


Ignore:
Timestamp:
2014-11-17T03:25:04Z (9 years ago)
Author:
Jan Vesely <jano.vesely@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
6069061
Parents:
ef3da5a (diff), 5042706 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

merge mainline changes

File:
1 edited

Legend:

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

    ref3da5a rb412168  
    5454#include <malloc.h>
    5555
    56 #define ASCTIME_BUF_LEN 26
     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)
    5764
    5865/** Pointer to kernel shared variables with time */
     
    6370} *ktime = NULL;
    6471
    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.
     72static async_sess_t *clock_conn = NULL;
     73
     74/** Check whether the year is a leap year.
    7675 *
    7776 * @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 static bool _is_leap_year(time_t year)
     79 *
     80 */
     81static bool is_leap_year(time_t year)
    8182{
    8283        year += 1900;
    83 
     84       
    8485        if (year % 400 == 0)
    8586                return true;
     87       
    8688        if (year % 100 == 0)
    8789                return false;
     90       
    8891        if (year % 4 == 0)
    8992                return true;
     93       
    9094        return false;
    9195}
    9296
    93 /**
    94  * Returns how many days there are in the given month of the given year.
     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.
    95100 * Note that year is only taken into account if month is February.
    96101 *
    97102 * @param year Year since 1900 (can be negative).
    98  * @param mon Month of the year. 0 for January, 11 for December.
     103 * @param mon  Month of the year. 0 for January, 11 for December.
     104 *
    99105 * @return Number of days in the specified month.
    100  */
    101 static 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 
     106 *
     107 */
     108static 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       
    108117        if (mon == 1) {
     118                /* February */
    109119                year += 1900;
    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
     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
    119129 * it is.
    120130 *
    121131 * For example, given date 2011-01-03, the corresponding expression is:
    122  *     _day_of_year(111, 0, 3) == 2
     132 * day_of_year(111, 0, 3) == 2
    123133 *
    124134 * @param year Year (year 1900 = 0, can be negative).
    125  * @param mon Month (January = 0).
     135 * @param mon  Month (January = 0).
    126136 * @param mday Day of month (First day is 1).
     137 *
    127138 * @return Day of year (First day is 0).
    128  */
    129 static 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.
     139 *
     140 */
     141static 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.
    142157 *
    143158 * @param op1 Dividend.
    144159 * @param op2 Divisor.
     160 *
    145161 * @return Rounded quotient.
    146  */
    147 static time_t _floor_div(time_t op1, time_t op2)
    148 {
    149         if (op1 >= 0 || op1 % op2 == 0) {
     162 *
     163 */
     164static time_t floor_div(time_t op1, time_t op2)
     165{
     166        if ((op1 >= 0) || (op1 % op2 == 0))
    150167                return op1 / op2;
    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.
     168       
     169        return op1 / op2 - 1;
     170}
     171
     172/** Modulo that rounds to negative infinity.
     173 *
     174 * Used by some functions in this module.
    159175 *
    160176 * @param op1 Dividend.
    161177 * @param op2 Divisor.
     178 *
    162179 * @return Remainder.
    163  */
    164 static 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. */
     180 *
     181 */
     182static 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. */
    174194        assert(result >= 0);
    175195        assert(result < op2);
     
    179199}
    180200
    181 /**
    182  * Number of days since the Epoch.
     201/** Number of days since the Epoch.
     202 *
    183203 * Epoch is 1970-01-01, which is also equal to day 0.
    184204 *
    185205 * @param year Year (year 1900 = 0, may be negative).
    186  * @param mon Month (January = 0).
     206 * @param mon  Month (January = 0).
    187207 * @param mday Day of month (first day = 1).
     208 *
    188209 * @return Number of days since the Epoch.
    189  */
    190 static 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  *
     210 *
     211 */
     212static 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 *
    200223 * @param tm Normalized broken-down time.
     224 *
    201225 * @return Number of seconds since the epoch, not counting leap seconds.
    202  */
    203 static time_t _secs_since_epoch(const struct tm *tm)
    204 {
    205         return _days_since_epoch(tm->tm_year, tm->tm_mon, tm->tm_mday) *
     226 *
     227 */
     228static time_t secs_since_epoch(const struct tm *tm)
     229{
     230        return days_since_epoch(tm->tm_year, tm->tm_mon, tm->tm_mday) *
    206231            SECS_PER_DAY + tm->tm_hour * SECS_PER_HOUR +
    207232            tm->tm_min * SECS_PER_MIN + tm->tm_sec;
    208233}
    209234
    210 /**
    211  * Which day of week the specified date is.
    212  *
     235/** Which day of week the specified date is.
     236 *
    213237 * @param year Year (year 1900 = 0).
    214  * @param mon Month (January = 0).
     238 * @param mon  Month (January = 0).
    215239 * @param mday Day of month (first = 1).
     240 *
    216241 * @return Day of week (Sunday = 0).
    217  */
    218 static int _day_of_week(time_t year, time_t mon, time_t mday)
     242 *
     243 */
     244static time_t day_of_week(time_t year, time_t mon, time_t mday)
    219245{
    220246        /* 1970-01-01 is Thursday */
    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.
     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.
    229255 * @param sec_add Seconds to add.
     256 *
    230257 * @return 0 on success, -1 on overflow
    231  */
    232 static int _normalize_time(struct tm *tm, time_t sec_add)
     258 *
     259 */
     260static int normalize_time(struct tm *tm, time_t sec_add)
    233261{
    234262        // TODO: DST correction
    235 
     263       
    236264        /* Set initial values. */
    237265        time_t sec = tm->tm_sec + sec_add;
     
    241269        time_t mon = tm->tm_mon;
    242270        time_t year = tm->tm_year;
    243 
     271       
    244272        /* Adjust time. */
    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 
     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       
    252280        /* Adjust month. */
    253         year += _floor_div(mon, 12);
    254         mon = _floor_mod(mon, 12);
    255 
     281        year += floor_div(mon, 12);
     282        mon = floor_mod(mon, 12);
     283       
    256284        /* Now the difficult part - days of month. */
    257285       
    258286        /* First, deal with whole cycles of 400 years = 146097 days. */
    259         year += _floor_div(day, 146097) * 400;
    260         day = _floor_mod(day, 146097);
     287        year += floor_div(day, 146097) * 400;
     288        day = floor_mod(day, 146097);
    261289       
    262290        /* Then, go in one year steps. */
     
    264292                /* January and February. */
    265293                while (day > 365) {
    266                         day -= _is_leap_year(year) ? 366 : 365;
     294                        day -= is_leap_year(year) ? 366 : 365;
    267295                        year++;
    268296                }
     
    270298                /* Rest of the year. */
    271299                while (day > 365) {
    272                         day -= _is_leap_year(year + 1) ? 366 : 365;
     300                        day -= is_leap_year(year + 1) ? 366 : 365;
    273301                        year++;
    274302                }
     
    276304       
    277305        /* Finally, finish it off month per month. */
    278         while (day >= _days_in_month(year, mon)) {
    279                 day -= _days_in_month(year, mon);
     306        while (day >= days_in_month(year, mon)) {
     307                day -= days_in_month(year, mon);
    280308                mon++;
     309               
    281310                if (mon >= 12) {
    282311                        mon -= 12;
     
    286315       
    287316        /* Calculate the remaining two fields. */
    288         tm->tm_yday = _day_of_year(year, mon, day + 1);
    289         tm->tm_wday = _day_of_week(year, mon, day + 1);
     317        tm->tm_yday = day_of_year(year, mon, day + 1);
     318        tm->tm_wday = day_of_week(year, mon, day + 1);
    290319       
    291320        /* And put the values back to the struct. */
     
    296325        tm->tm_mon = (int) mon;
    297326       
    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);
     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);
    301330                return -1;
    302331        }
     
    306335}
    307336
    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.
     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.
    311341 *
    312342 * @param Year since 1900.
     343 *
    313344 * @return Offset of week-based year relative to calendar year.
    314  */
    315 static 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.
     345 *
     346 */
     347static 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.
    323355 *
    324356 * @param tm Normalized broken-down time.
     357 *
    325358 * @return Week-based year.
    326  */
    327 static int _wbyear(const struct tm *tm)
    328 {
    329         int day = tm->tm_yday - _wbyear_offset(tm->tm_year);
     359 *
     360 */
     361static int wbyear(const struct tm *tm)
     362{
     363        int day = tm->tm_yday - wbyear_offset(tm->tm_year);
     364       
    330365        if (day < 0) {
    331366                /* Last week of previous year. */
    332367                return tm->tm_year - 1;
    333368        }
    334         if (day > 364 + _is_leap_year(tm->tm_year)) {
     369       
     370        if (day > 364 + is_leap_year(tm->tm_year)) {
    335371                /* First week of next year. */
    336372                return tm->tm_year + 1;
    337373        }
     374       
    338375        /* All the other days are in the calendar year. */
    339376        return tm->tm_year;
    340377}
    341378
    342 /**
    343  * Week number of the year, assuming weeks start on sunday.
     379/** Week number of the year (assuming weeks start on Sunday).
     380 *
    344381 * The first Sunday of January is the first day of week 1;
    345382 * days in the new year before this are in week 0.
    346383 *
    347384 * @param tm Normalized broken-down time.
     385 *
    348386 * @return The week number (0 - 53).
    349  */
    350 static int _sun_week_number(const struct tm *tm)
    351 {
    352         int first_day = (7 - _day_of_week(tm->tm_year, 0, 1)) % 7;
     387 *
     388 */
     389static int sun_week_number(const struct tm *tm)
     390{
     391        int first_day = (7 - day_of_week(tm->tm_year, 0, 1)) % 7;
     392       
    353393        return (tm->tm_yday - first_day + 7) / 7;
    354394}
    355395
    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
     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
    361402 * of January are always in week 1.
    362403 *
    363404 * @param tm Normalized broken-down time.
     405 *
    364406 * @return The week number (1 - 53).
    365  */
    366 static int _iso_week_number(const struct tm *tm)
    367 {
    368         int day = tm->tm_yday - _wbyear_offset(tm->tm_year);
     407 *
     408 */
     409static int iso_week_number(const struct tm *tm)
     410{
     411        int day = tm->tm_yday - wbyear_offset(tm->tm_year);
     412       
    369413        if (day < 0) {
    370414                /* Last week of previous year. */
    371415                return 53;
    372416        }
    373         if (day > 364 + _is_leap_year(tm->tm_year)) {
     417       
     418        if (day > 364 + is_leap_year(tm->tm_year)) {
    374419                /* First week of next year. */
    375420                return 1;
    376421        }
     422       
    377423        /* All the other days give correct answer. */
    378424        return (day / 7 + 1);
    379425}
    380426
    381 /**
    382  * Week number of the year, assuming weeks start on monday.
     427/** Week number of the year (assuming weeks start on Monday).
     428 *
    383429 * The first Monday of January is the first day of week 1;
    384  * days in the new year before this are in week 0. 
     430 * days in the new year before this are in week 0.
    385431 *
    386432 * @param tm Normalized broken-down time.
     433 *
    387434 * @return The week number (0 - 53).
    388  */
    389 static int _mon_week_number(const struct tm *tm)
    390 {
    391         int first_day = (1 - _day_of_week(tm->tm_year, 0, 1)) % 7;
     435 *
     436 */
     437static int mon_week_number(const struct tm *tm)
     438{
     439        int first_day = (1 - day_of_week(tm->tm_year, 0, 1)) % 7;
     440       
    392441        return (tm->tm_yday - first_day + 7) / 7;
    393442}
    394 
    395 /******************************************************************************/
    396 
    397443
    398444/** Add microseconds to given timeval.
     
    468514}
    469515
    470 /** Get time of day
     516/** Get time of day.
    471517 *
    472518 * The time variables are memory mapped (read-only) from kernel which
     
    482528 *
    483529 */
    484 int 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 
     530void gettimeofday(struct timeval *tv, struct timezone *tz)
     531{
    496532        if (tz) {
    497533                tz->tz_minuteswest = 0;
    498534                tz->tz_dsttime = DST_NONE;
    499535        }
    500 
     536       
    501537        if (clock_conn == NULL) {
    502                 rc = loc_category_get_id("clock", &cat_id, IPC_FLAG_BLOCKING);
     538                category_id_t cat_id;
     539                int rc = loc_category_get_id("clock", &cat_id, IPC_FLAG_BLOCKING);
    503540                if (rc != EOK)
    504                         goto ret_uptime;
    505 
     541                        goto fallback;
     542               
     543                service_id_t *svc_ids;
     544                size_t svc_cnt;
    506545                rc = loc_category_get_svcs(cat_id, &svc_ids, &svc_cnt);
    507546                if (rc != EOK)
    508                         goto ret_uptime;
    509 
     547                        goto fallback;
     548               
    510549                if (svc_cnt == 0)
    511                         goto ret_uptime;
    512 
     550                        goto fallback;
     551               
     552                char *svc_name;
    513553                rc = loc_service_get_name(svc_ids[0], &svc_name);
     554                free(svc_ids);
    514555                if (rc != EOK)
    515                         goto ret_uptime;
    516 
     556                        goto fallback;
     557               
     558                service_id_t svc_id;
    517559                rc = loc_service_get_id(svc_name, &svc_id, 0);
     560                free(svc_name);
    518561                if (rc != EOK)
    519                         goto ret_uptime;
    520 
     562                        goto fallback;
     563               
    521564                clock_conn = loc_service_connect(EXCHANGE_SERIALIZE,
    522565                    svc_id, IPC_FLAG_BLOCKING);
    523566                if (!clock_conn)
    524                         goto ret_uptime;
    525         }
    526 
    527         rc = clock_dev_time_get(clock_conn, &t);
     567                        goto fallback;
     568        }
     569       
     570        struct tm time;
     571        int rc = clock_dev_time_get(clock_conn, &time);
    528572        if (rc != EOK)
    529                 goto ret_uptime;
    530 
     573                goto fallback;
     574       
    531575        tv->tv_usec = 0;
    532         tv->tv_sec = mktime(&t);
    533 
    534         free(svc_name);
    535         free(svc_ids);
    536 
    537         return EOK;
    538 
    539 ret_uptime:
    540 
    541         free(svc_name);
    542         free(svc_ids);
    543 
    544         return getuptime(tv);
    545 }
    546 
    547 int getuptime(struct timeval *tv)
     576        tv->tv_sec = mktime(&time);
     577       
     578        return;
     579       
     580fallback:
     581        getuptime(tv);
     582}
     583
     584void getuptime(struct timeval *tv)
    548585{
    549586        if (ktime == NULL) {
     
    552589                if (rc != EOK) {
    553590                        errno = rc;
    554                         return -1;
     591                        goto fallback;
    555592                }
    556593               
     
    561598                        as_area_destroy(addr);
    562599                        errno = rc;
    563                         return -1;
     600                        goto fallback;
    564601                }
    565602               
     
    580617        } else
    581618                tv->tv_sec = s1;
    582 
    583         return 0;
     619       
     620        return;
     621       
     622fallback:
     623        tv->tv_sec = 0;
     624        tv->tv_usec = 0;
    584625}
    585626
     
    587628{
    588629        struct timeval tv;
    589         if (gettimeofday(&tv, NULL))
    590                 return (time_t) -1;
     630        gettimeofday(&tv, NULL);
    591631       
    592632        if (tloc)
     
    631671}
    632672
    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.
     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.
    637679 *
    638680 * @param tm Broken-down time.
    639  * @return time_t representation of the time, undefined value on overflow.
     681 *
     682 * @return time_t representation of the time.
     683 * @return Undefined value on overflow.
     684 *
    640685 */
    641686time_t mktime(struct tm *tm)
     
    643688        // TODO: take DST flag into account
    644689        // TODO: detect overflow
    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.
     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.
    655726 * @param maxsize Size of the buffer.
    656  * @param format Format of the output.
    657  * @param tm Broken-down time to format.
     727 * @param format  Format of the output.
     728 * @param tm      Broken-down time to format.
     729 *
    658730 * @return Number of bytes written.
     731 *
    659732 */
    660733size_t strftime(char *restrict s, size_t maxsize,
     
    664737        assert(format != NULL);
    665738        assert(tm != NULL);
    666 
     739       
    667740        // TODO: use locale
     741       
    668742        static const char *wday_abbr[] = {
    669743                "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
    670744        };
     745       
    671746        static const char *wday[] = {
    672747                "Sunday", "Monday", "Tuesday", "Wednesday",
    673748                "Thursday", "Friday", "Saturday"
    674749        };
     750       
    675751        static const char *mon_abbr[] = {
    676752                "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    677753                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    678754        };
     755       
    679756        static const char *mon[] = {
    680757                "January", "February", "March", "April", "May", "June", "July",
     
    682759        };
    683760       
    684         if (maxsize < 1) {
     761        if (maxsize < 1)
    685762                return 0;
    686         }
    687763       
    688764        char *ptr = s;
     
    690766        size_t remaining = maxsize;
    691767       
    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        
    715768        while (*format != '\0') {
    716769                if (*format != '%') {
    717                         append("%c", *format);
     770                        APPEND("%c", *format);
    718771                        format++;
    719772                        continue;
     
    721774               
    722775                format++;
    723                 if (*format == '0' || *format == '+') {
     776                if ((*format == '0') || (*format == '+')) {
    724777                        // TODO: padding
    725778                        format++;
    726779                }
     780               
    727781                while (isdigit(*format)) {
    728782                        // TODO: padding
    729783                        format++;
    730784                }
    731                 if (*format == 'O' || *format == 'E') {
     785               
     786                if ((*format == 'O') || (*format == 'E')) {
    732787                        // TODO: locale's alternative format
    733788                        format++;
     
    736791                switch (*format) {
    737792                case 'a':
    738                         append("%s", wday_abbr[tm->tm_wday]); break;
     793                        APPEND("%s", wday_abbr[tm->tm_wday]);
     794                        break;
    739795                case 'A':
    740                         append("%s", wday[tm->tm_wday]); break;
     796                        APPEND("%s", wday[tm->tm_wday]);
     797                        break;
    741798                case 'b':
    742                         append("%s", mon_abbr[tm->tm_mon]); break;
     799                        APPEND("%s", mon_abbr[tm->tm_mon]);
     800                        break;
    743801                case 'B':
    744                         append("%s", mon[tm->tm_mon]); break;
     802                        APPEND("%s", mon[tm->tm_mon]);
     803                        break;
    745804                case 'c':
    746805                        // TODO: locale-specific datetime format
    747                         recurse("%Y-%m-%d %H:%M:%S"); break;
     806                        RECURSE("%Y-%m-%d %H:%M:%S");
     807                        break;
    748808                case 'C':
    749                         append("%02d", (1900 + tm->tm_year) / 100); break;
     809                        APPEND("%02d", (1900 + tm->tm_year) / 100);
     810                        break;
    750811                case 'd':
    751                         append("%02d", tm->tm_mday); break;
     812                        APPEND("%02d", tm->tm_mday);
     813                        break;
    752814                case 'D':
    753                         recurse("%m/%d/%y"); break;
     815                        RECURSE("%m/%d/%y");
     816                        break;
    754817                case 'e':
    755                         append("%2d", tm->tm_mday); break;
     818                        APPEND("%2d", tm->tm_mday);
     819                        break;
    756820                case 'F':
    757                         recurse("%+4Y-%m-%d"); break;
     821                        RECURSE("%+4Y-%m-%d");
     822                        break;
    758823                case 'g':
    759                         append("%02d", _wbyear(tm) % 100); break;
     824                        APPEND("%02d", wbyear(tm) % 100);
     825                        break;
    760826                case 'G':
    761                         append("%d", _wbyear(tm)); break;
     827                        APPEND("%d", wbyear(tm));
     828                        break;
    762829                case 'h':
    763                         recurse("%b"); break;
     830                        RECURSE("%b");
     831                        break;
    764832                case 'H':
    765                         append("%02d", tm->tm_hour); break;
     833                        APPEND("%02d", tm->tm_hour);
     834                        break;
    766835                case 'I':
    767                         append("%02d", TO_12H(tm->tm_hour)); break;
     836                        APPEND("%02d", TO_12H(tm->tm_hour));
     837                        break;
    768838                case 'j':
    769                         append("%03d", tm->tm_yday); break;
     839                        APPEND("%03d", tm->tm_yday);
     840                        break;
    770841                case 'k':
    771                         append("%2d", tm->tm_hour); break;
     842                        APPEND("%2d", tm->tm_hour);
     843                        break;
    772844                case 'l':
    773                         append("%2d", TO_12H(tm->tm_hour)); break;
     845                        APPEND("%2d", TO_12H(tm->tm_hour));
     846                        break;
    774847                case 'm':
    775                         append("%02d", tm->tm_mon); break;
     848                        APPEND("%02d", tm->tm_mon);
     849                        break;
    776850                case 'M':
    777                         append("%02d", tm->tm_min); break;
     851                        APPEND("%02d", tm->tm_min);
     852                        break;
    778853                case 'n':
    779                         append("\n"); break;
     854                        APPEND("\n");
     855                        break;
    780856                case 'p':
    781                         append("%s", tm->tm_hour < 12 ? "AM" : "PM"); break;
     857                        APPEND("%s", tm->tm_hour < 12 ? "AM" : "PM");
     858                        break;
    782859                case 'P':
    783                         append("%s", tm->tm_hour < 12 ? "am" : "PM"); break;
     860                        APPEND("%s", tm->tm_hour < 12 ? "am" : "PM");
     861                        break;
    784862                case 'r':
    785                         recurse("%I:%M:%S %p"); break;
     863                        RECURSE("%I:%M:%S %p");
     864                        break;
    786865                case 'R':
    787                         recurse("%H:%M"); break;
     866                        RECURSE("%H:%M");
     867                        break;
    788868                case 's':
    789                         append("%ld", _secs_since_epoch(tm)); break;
     869                        APPEND("%ld", secs_since_epoch(tm));
     870                        break;
    790871                case 'S':
    791                         append("%02d", tm->tm_sec); break;
     872                        APPEND("%02d", tm->tm_sec);
     873                        break;
    792874                case 't':
    793                         append("\t"); break;
     875                        APPEND("\t");
     876                        break;
    794877                case 'T':
    795                         recurse("%H:%M:%S"); break;
     878                        RECURSE("%H:%M:%S");
     879                        break;
    796880                case 'u':
    797                         append("%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);
     881                        APPEND("%d", (tm->tm_wday == 0) ? 7 : tm->tm_wday);
    798882                        break;
    799883                case 'U':
    800                         append("%02d", _sun_week_number(tm)); break;
     884                        APPEND("%02d", sun_week_number(tm));
     885                        break;
    801886                case 'V':
    802                         append("%02d", _iso_week_number(tm)); break;
     887                        APPEND("%02d", iso_week_number(tm));
     888                        break;
    803889                case 'w':
    804                         append("%d", tm->tm_wday); break;
     890                        APPEND("%d", tm->tm_wday);
     891                        break;
    805892                case 'W':
    806                         append("%02d", _mon_week_number(tm)); break;
     893                        APPEND("%02d", mon_week_number(tm));
     894                        break;
    807895                case 'x':
    808896                        // TODO: locale-specific date format
    809                         recurse("%Y-%m-%d"); break;
     897                        RECURSE("%Y-%m-%d");
     898                        break;
    810899                case 'X':
    811900                        // TODO: locale-specific time format
    812                         recurse("%H:%M:%S"); break;
     901                        RECURSE("%H:%M:%S");
     902                        break;
    813903                case 'y':
    814                         append("%02d", tm->tm_year % 100); break;
     904                        APPEND("%02d", tm->tm_year % 100);
     905                        break;
    815906                case 'Y':
    816                         append("%d", 1900 + tm->tm_year); break;
     907                        APPEND("%d", 1900 + tm->tm_year);
     908                        break;
    817909                case 'z':
    818910                        // TODO: timezone
     
    822914                        break;
    823915                case '%':
    824                         append("%%");
     916                        APPEND("%%");
    825917                        break;
    826918                default:
    827919                        /* Invalid specifier, print verbatim. */
    828                         while (*format != '%') {
     920                        while (*format != '%')
    829921                                format--;
    830                         }
    831                         append("%%");
     922                       
     923                        APPEND("%%");
    832924                        break;
    833925                }
     926               
    834927                format++;
    835928        }
    836929       
    837         #undef append
    838         #undef recurse
    839        
    840930        return maxsize - remaining;
    841931}
    842932
    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
     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 *
    850940 */
    851941int time_utc2tm(const time_t time, struct tm *restrict result)
    852942{
    853943        assert(result != NULL);
    854 
     944       
    855945        /* Set result to epoch. */
    856946        result->tm_sec = 0;
     
    860950        result->tm_mon = 0;
    861951        result->tm_year = 70; /* 1970 */
    862 
    863         if (_normalize_time(result, time) == -1)
     952       
     953        if (normalize_time(result, time) == -1)
    864954                return EOVERFLOW;
    865 
     955       
    866956        return EOK;
    867957}
    868958
    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.
     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 *
    877969 */
    878970int time_utc2str(const time_t time, char *restrict buf)
    879971{
    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);
     972        struct tm tm;
     973        int ret = time_utc2tm(time, &tm);
     974        if (ret != EOK)
     975                return ret;
     976       
     977        time_tm2str(&tm, buf);
    887978        return EOK;
    888979}
    889980
    890 
    891 /**
    892  * Converts broken-down time to a string in format
    893  * "Sun Jan 1 00:00:00 1970\n". (Obsolete)
     981/** Convert broken-down time to a NULL-terminated string.
     982 *
     983 * The format is "Sun Jan 1 00:00:00 1970\n". (Obsolete)
    894984 *
    895985 * @param timeptr Broken-down time structure.
    896  * @param buf     Buffer to store string to, must be at least ASCTIME_BUF_LEN
    897  *                bytes long.
     986 * @param buf     Buffer to store string to, must be at least
     987 *                ASCTIME_BUF_LEN bytes long.
     988 *
    898989 */
    899990void time_tm2str(const struct tm *restrict timeptr, char *restrict buf)
     
    901992        assert(timeptr != NULL);
    902993        assert(buf != NULL);
    903 
     994       
    904995        static const char *wday[] = {
    905996                "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
    906997        };
     998       
    907999        static const char *mon[] = {
    9081000                "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    9091001                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    9101002        };
    911 
     1003       
    9121004        snprintf(buf, ASCTIME_BUF_LEN, "%s %s %2d %02d:%02d:%02d %d\n",
    9131005            wday[timeptr->tm_wday],
     
    9181010}
    9191011
    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.
     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 *
    9281021 */
    9291022int time_local2tm(const time_t time, struct tm *restrict result)
    9301023{
    931         // TODO: deal with timezone
    932         // currently assumes system and all times are in GMT
    933 
     1024        // TODO: Deal with timezones.
     1025        //       Currently assumes system and all times are in UTC
     1026       
    9341027        /* Set result to epoch. */
    9351028        result->tm_sec = 0;
     
    9391032        result->tm_mon = 0;
    9401033        result->tm_year = 70; /* 1970 */
    941 
    942         if (_normalize_time(result, time) == -1)
     1034       
     1035        if (normalize_time(result, time) == -1)
    9431036                return EOVERFLOW;
    944 
     1037       
    9451038        return EOK;
    9461039}
    9471040
    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
     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
    9511044 * user's specified timezone.
    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.
     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 *
    9581052 */
    9591053int time_local2str(const time_t time, char *buf)
    9601054{
    9611055        struct tm loctime;
    962         int r;
    963 
    964         if ((r = time_local2tm(time, &loctime)) != EOK)
    965                 return r;
    966 
     1056        int ret = time_local2tm(time, &loctime);
     1057        if (ret != EOK)
     1058                return ret;
     1059       
    9671060        time_tm2str(&loctime, buf);
    968 
    9691061        return EOK;
    9701062}
    9711063
    972 /**
    973  * Calculate the difference between two times, in seconds.
    974  *
     1064/** Calculate the difference between two times, in seconds.
     1065 *
    9751066 * @param time1 First time.
    9761067 * @param time0 Second time.
    977  * @return Time in seconds.
     1068 *
     1069 * @return Time difference in seconds.
     1070 *
    9781071 */
    9791072double difftime(time_t time1, time_t time0)
Note: See TracChangeset for help on using the changeset viewer.