Changeset f7ea5400 in mainline for uspace/lib/posix/time.c


Ignore:
Timestamp:
2012-08-15T17:52:09Z (12 years ago)
Author:
Maurizio Lombardi <m.lombardi85@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
16639bb
Parents:
9dec6d4
Message:

Replace the gmtime(), asctime() localtime() and ctime() with a new set of reentrant functions.
The former will be kept in the posix library for compatibility reasons.

File:
1 edited

Legend:

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

    r9dec6d4 rf7ea5400  
    6161 */
    6262
    63 
    64 
    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.
    76  *
    77  * @param year Year since 1900 (e.g. for 1970, the value is 70).
    78  * @return true if year is a leap year, false otherwise
    79  */
    80 static bool _is_leap_year(time_t year)
    81 {
    82         year += 1900;
    83 
    84         if (year % 400 == 0)
    85                 return true;
    86         if (year % 100 == 0)
    87                 return false;
    88         if (year % 4 == 0)
    89                 return true;
    90         return false;
    91 }
    92 
    93 /**
    94  * Returns how many days there are in the given month of the given year.
    95  * Note that year is only taken into account if month is February.
    96  *
    97  * @param year Year since 1900 (can be negative).
    98  * @param mon Month of the year. 0 for January, 11 for December.
    99  * @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 
    108         if (mon == 1) {
    109                 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
    119  * it is.
    120  *
    121  * For example, given date 2011-01-03, the corresponding expression is:
    122  *     _day_of_year(111, 0, 3) == 2
    123  *
    124  * @param year Year (year 1900 = 0, can be negative).
    125  * @param mon Month (January = 0).
    126  * @param mday Day of month (First day is 1).
    127  * @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.
    142  *
    143  * @param op1 Dividend.
    144  * @param op2 Divisor.
    145  * @return Rounded quotient.
    146  */
    147 static time_t _floor_div(time_t op1, time_t op2)
    148 {
    149         if (op1 >= 0 || op1 % op2 == 0) {
    150                 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.
    159  *
    160  * @param op1 Dividend.
    161  * @param op2 Divisor.
    162  * @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. */
    174         assert(result >= 0);
    175         assert(result < op2);
    176         assert(div * op2 + result == op1);
    177        
    178         return result;
    179 }
    180 
    181 /**
    182  * Number of days since the Epoch.
    183  * Epoch is 1970-01-01, which is also equal to day 0.
    184  *
    185  * @param year Year (year 1900 = 0, may be negative).
    186  * @param mon Month (January = 0).
    187  * @param mday Day of month (first day = 1).
    188  * @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  * Which day of week the specified date is.
    199  *
    200  * @param year Year (year 1900 = 0).
    201  * @param mon Month (January = 0).
    202  * @param mday Day of month (first = 1).
    203  * @return Day of week (Sunday = 0).
    204  */
    205 static int _day_of_week(time_t year, time_t mon, time_t mday)
    206 {
    207         /* 1970-01-01 is Thursday */
    208         return _floor_mod((_days_since_epoch(year, mon, mday) + 4), 7);
    209 }
    210 
    211 /**
    212  * Normalizes the broken-down time and optionally adds specified amount of
    213  * seconds.
    214  *
    215  * @param tm Broken-down time to normalize.
    216  * @param sec_add Seconds to add.
    217  * @return 0 on success, -1 on overflow
    218  */
    219 static int _normalize_time(struct tm *tm, time_t sec_add)
    220 {
    221         // TODO: DST correction
    222 
    223         /* Set initial values. */
    224         time_t sec = tm->tm_sec + sec_add;
    225         time_t min = tm->tm_min;
    226         time_t hour = tm->tm_hour;
    227         time_t day = tm->tm_mday - 1;
    228         time_t mon = tm->tm_mon;
    229         time_t year = tm->tm_year;
    230 
    231         /* Adjust time. */
    232         min += _floor_div(sec, SECS_PER_MIN);
    233         sec = _floor_mod(sec, SECS_PER_MIN);
    234         hour += _floor_div(min, MINS_PER_HOUR);
    235         min = _floor_mod(min, MINS_PER_HOUR);
    236         day += _floor_div(hour, HOURS_PER_DAY);
    237         hour = _floor_mod(hour, HOURS_PER_DAY);
    238 
    239         /* Adjust month. */
    240         year += _floor_div(mon, 12);
    241         mon = _floor_mod(mon, 12);
    242 
    243         /* Now the difficult part - days of month. */
    244        
    245         /* First, deal with whole cycles of 400 years = 146097 days. */
    246         year += _floor_div(day, 146097) * 400;
    247         day = _floor_mod(day, 146097);
    248        
    249         /* Then, go in one year steps. */
    250         if (mon <= 1) {
    251                 /* January and February. */
    252                 while (day > 365) {
    253                         day -= _is_leap_year(year) ? 366 : 365;
    254                         year++;
    255                 }
    256         } else {
    257                 /* Rest of the year. */
    258                 while (day > 365) {
    259                         day -= _is_leap_year(year + 1) ? 366 : 365;
    260                         year++;
    261                 }
    262         }
    263        
    264         /* Finally, finish it off month per month. */
    265         while (day >= _days_in_month(year, mon)) {
    266                 day -= _days_in_month(year, mon);
    267                 mon++;
    268                 if (mon >= 12) {
    269                         mon -= 12;
    270                         year++;
    271                 }
    272         }
    273        
    274         /* Calculate the remaining two fields. */
    275         tm->tm_yday = _day_of_year(year, mon, day + 1);
    276         tm->tm_wday = _day_of_week(year, mon, day + 1);
    277        
    278         /* And put the values back to the struct. */
    279         tm->tm_sec = (int) sec;
    280         tm->tm_min = (int) min;
    281         tm->tm_hour = (int) hour;
    282         tm->tm_mday = (int) day + 1;
    283         tm->tm_mon = (int) mon;
    284        
    285         /* Casts to work around libc brain-damage. */
    286         if (year > ((int)INT_MAX) || year < ((int)INT_MIN)) {
    287                 tm->tm_year = (year < 0) ? ((int)INT_MIN) : ((int)INT_MAX);
    288                 return -1;
    289         }
    290        
    291         tm->tm_year = (int) year;
    292         return 0;
    293 }
    294 
    295 /******************************************************************************/
    296 
    29763int posix_daylight;
    29864long posix_timezone;
     
    32187    struct tm *restrict result)
    32288{
    323         assert(timer != NULL);
    324         assert(result != NULL);
    325 
    326         /* Set result to epoch. */
    327         result->tm_sec = 0;
    328         result->tm_min = 0;
    329         result->tm_hour = 0;
    330         result->tm_mday = 1;
    331         result->tm_mon = 0;
    332         result->tm_year = 70; /* 1970 */
    333 
    334         if (_normalize_time(result, *timer) == -1) {
    335                 errno = EOVERFLOW;
     89        int rc = utctime2tm(*timer, result);
     90        if (rc != EOK) {
     91                errno = rc;
    33692                return NULL;
    33793        }
    33894
    33995        return result;
     96}
     97
     98/**
     99 * Converts a time value to a broken-down UTC time.
     100 * (non reentrant version)
     101 *
     102 * @param timep  Time to convert
     103 * @return       Pointer to a statically allocated structure that stores
     104 *               the result, NULL in case of error.
     105 */
     106struct tm *posix_gmtime(const time_t *restrict timep)
     107{
     108        static struct tm result;
     109
     110        return posix_gmtime_r(timep, &result);
    340111}
    341112
     
    353124        // currently assumes system and all times are in GMT
    354125        return posix_gmtime_r(timer, result);
     126}
     127
     128/**
     129 * Converts a time value to a broken-down local time.
     130 * (non reentrant version)
     131 *
     132 * @param timep    Time to convert.
     133 * @return         Pointer to a statically allocated structure that stores
     134 *                 the result, NULL in case of error.
     135 */
     136struct tm *posix_localtime(const time_t *restrict timep)
     137{
     138        static struct tm result;
     139
     140        return posix_localtime_r(timep, &result);
    355141}
    356142
     
    367153    char *restrict buf)
    368154{
    369         assert(timeptr != NULL);
    370         assert(buf != NULL);
    371 
    372         static const char *wday[] = {
    373                 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
    374         };
    375         static const char *mon[] = {
    376                 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    377                 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    378         };
    379 
    380         snprintf(buf, ASCTIME_BUF_LEN, "%s %s %2d %02d:%02d:%02d %d\n",
    381             wday[timeptr->tm_wday],
    382             mon[timeptr->tm_mon],
    383             timeptr->tm_mday, timeptr->tm_hour,
    384             timeptr->tm_min, timeptr->tm_sec,
    385             1900 + timeptr->tm_year);
    386 
     155        tm2str(timeptr, buf);
    387156        return buf;
    388157}
    389158
    390159/**
    391  * Reentrant variant of ctime().
     160 * Convers broken-down time to a string in format
     161 * "Sun Jan 1 00:00:00 1970\n". (Obsolete)
     162 * (non reentrant version)
     163 *
     164 * @param timeptr    Broken-down time structure.
     165 * @return           Pointer to a statically allocated buffer that stores
     166 *                   the result, NULL in case of error.
     167 */
     168char *posix_asctime(const struct tm *restrict timeptr)
     169{
     170        static char buf[ASCTIME_BUF_LEN];
     171
     172        return posix_asctime_r(timeptr, buf);
     173}
     174
     175/**
     176 * Converts the calendar time to a string in format
     177 * "Sun Jan 1 00:00:00 1970\n" (Obsolete)
    392178 *
    393179 * @param timer Time to convert.
    394180 * @param buf Buffer to store string to. Must be at least ASCTIME_BUF_LEN
    395181 *     bytes long.
    396  * @return Pointer to buf on success, NULL on falure.
     182 * @return Pointer to buf on success, NULL on failure.
    397183 */
    398184char *posix_ctime_r(const time_t *timer, char *buf)
    399185{
    400         struct tm loctime;
    401         if (posix_localtime_r(timer, &loctime) == NULL) {
     186        int r = localtime2str(*timer, buf);
     187        if (r != EOK) {
     188                errno = r;
    402189                return NULL;
    403190        }
    404         return posix_asctime_r(&loctime, buf);
     191
     192        return buf;
     193}
     194
     195/**
     196 * Converts the calendar time to a string in format
     197 * "Sun Jan 1 00:00:00 1970\n" (Obsolete)
     198 * (non reentrant version)
     199 *
     200 * @param timep    Time to convert.
     201 * @return         Pointer to a statically allocated buffer that stores
     202 *                 the result, NULL in case of error.
     203 */
     204char *posix_ctime(const time_t *timep)
     205{
     206        static char buf[ASCTIME_BUF_LEN];
     207
     208        return posix_ctime_r(timep, buf);
    405209}
    406210
Note: See TracChangeset for help on using the changeset viewer.