Home
       strftime.c - scc - simple c99 compiler
  HTML git clone git://git.simple-cc.org/scc
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
       strftime.c (5471B)
       ---
            1 #include <string.h>
            2 #include <time.h>
            3 
            4 #include "../libc.h"
            5 
            6 #undef strftime
            7 
            8 static char *days[] = {
            9         "Sunday",   "Monday", "Tuesday",  "Wednesday",
           10         "Thursday", "Friday", "Saturday",
           11 };
           12 
           13 static char *months[] = {
           14         "January",   "February", "March",    "April",
           15         "May",       "June",     "July",     "August",
           16         "September", "October",  "November", "December"
           17 };
           18 
           19 static char *am_pm[] = {"AM", "PM"};
           20 
           21 static int
           22 first(int day, int year)
           23 {
           24         int ny;
           25 
           26         ny = _newyear(year);
           27         if (ny == day)
           28                 return 0;
           29         if (day - ny < 0)
           30                 return 7 - (ny - day);
           31         return day - ny;
           32 }
           33 
           34 static int
           35 weeknum(const struct tm *tm, int day)
           36 {
           37         int fday, val;
           38 
           39         fday = first(day, tm->tm_year);
           40         if (tm->tm_yday < fday) {
           41                 val = 0;
           42         } else {
           43                 val = tm->tm_yday - fday;
           44                 val /= 7;
           45                 val++;
           46         }
           47         return val;
           48 }
           49 
           50 static int
           51 isoyear(const struct tm *tm)
           52 {
           53         int monday;
           54 
           55         if (tm->tm_yday < 7) {
           56                 monday = first(THU, tm->tm_year) - 3;
           57                 if (tm->tm_yday < monday)
           58                         return tm->tm_year - 1;
           59         } else if (tm->tm_yday > 357) {
           60                 monday = first(THU, tm->tm_year + 1) - 3;
           61                 if (tm->tm_mday >= (31 + monday))
           62                         return tm->tm_year + 1;
           63         }
           64         return tm->tm_year;
           65 }
           66 
           67 static int
           68 isoweek(const struct tm *tm)
           69 {
           70         int year, monday, yday, val;
           71 
           72         year = isoyear(tm);
           73         monday = first(THU, year) - 3;
           74         yday = tm->tm_yday;
           75         if (year > tm->tm_year)
           76                 yday = tm->tm_mday - 31 + monday;
           77         else if (year < tm->tm_year)
           78                 yday = _daysyear(year) + yday;
           79         val = yday - monday;
           80         val /= 7;
           81         val++;
           82         return val;
           83 }
           84 
           85 static int
           86 isoday(const struct tm *tm)
           87 {
           88         if (tm->tm_wday == 0)
           89                 return 7;
           90         return tm->tm_wday;
           91 }
           92 
           93 static size_t
           94 sval(char *s, size_t siz, char **strs, int abrev, int idx, int max)
           95 {
           96         char *str;
           97         size_t len;
           98 
           99         if (siz == 0)
          100                 return 0;
          101 
          102         if (idx < 0 && idx >= max) {
          103                 *s = '?';
          104                 return 1;
          105         }
          106 
          107         str = strs[idx];
          108         len = (!abrev) ? strlen(str) : 3;
          109         if (len > siz)
          110                 len = siz;
          111 
          112         memcpy(s, str, len);
          113         return len;
          114 }
          115 
          116 static size_t
          117 dval(char *s, size_t siz, int prec, int fill, int val)
          118 {
          119         char *t;
          120         int n;
          121         static char digits[] = "0123456789";
          122 
          123         if (siz == 0)
          124                 return 0;
          125 
          126         if (prec > siz)
          127                 prec = siz;
          128 
          129         if (val < 0) {
          130                 *s = '?';
          131                 return 1;
          132         }
          133 
          134         n = prec;
          135         do {
          136                 s[--n] = digits[val % 10];
          137                 val /= 10;
          138         } while (n > 0 && val > 0);
          139 
          140         while (n > 0)
          141                 s[--n] = fill;
          142 
          143         return prec;
          144 }
          145 
          146 static size_t
          147 timezone(char *s, size_t siz, const struct tm * restrict tm)
          148 {
          149         size_t n;
          150         long off = tm->tm_gmtoff;
          151 
          152         if (off >= 0) {
          153                 *s++ = '+';
          154         } else {
          155                 *s++ = '-';
          156                 off = -off;
          157         }
          158 
          159         n = 1;
          160         n += dval(s, siz-n, 2, '0', off / 3600);
          161         n += dval(s + n, siz-n, 2, '0', (off % 3600) / 60);
          162 
          163         return n;
          164 }
          165 
          166 size_t
          167 strftime(char *restrict s, size_t maxsize,
          168          const char *restrict format, const struct tm *restrict timeptr)
          169 {
          170         int ch, abrev, val, fill, width;
          171         size_t n, inc;
          172         char *tfmt, *begin;
          173 
          174         begin = s;
          175         for (n = maxsize; (ch = *format++) && n > 0; s += inc, n -= inc) {
          176                 if (ch != '%') {
          177                         *s = ch;
          178                         inc = 1;
          179                         continue;
          180                 }
          181 
          182                 abrev = 0;
          183                 fill = '0';
          184                 width = 2;
          185 
          186                 if (*format == 'E' || *format == 'O')
          187                         format++;
          188 
          189                 switch (*format++) {
          190                 case 'a':
          191                         abrev = 1;
          192                 case 'A':
          193                         inc = sval(s, n, days, abrev, timeptr->tm_wday, 7);
          194                         break;
          195                 case 'h':
          196                 case 'b':
          197                         abrev = 1;
          198                 case 'B':
          199                         inc = sval(s, n, months, abrev, timeptr->tm_mon, 12);
          200                         break;
          201                 case 'c':
          202                         tfmt = "%a %b %e %T %Y";
          203                         goto recursive;
          204                 case 'C':
          205                         val = (timeptr->tm_year + 1900) / 100;
          206                         goto number;
          207                 case 'd':
          208                         val = timeptr->tm_mday;
          209                         goto number;
          210                 case 'D':
          211                         tfmt = "%m/%d/%y";
          212                         goto recursive;
          213                 case 'e':
          214                         fill = ' ';
          215                         val = timeptr->tm_mday;
          216                         goto number;
          217                 case 'F':
          218                         tfmt = "%Y-%m-%d";
          219                         goto recursive;
          220                 case 'g':
          221                         val = isoyear(timeptr);
          222                         goto number;
          223                 case 'G':
          224                         val = isoyear(timeptr);
          225                         val += 1900;
          226                         width = 4;
          227                         goto number;
          228                 case 'H':
          229                         val = timeptr->tm_hour;
          230                         goto number;
          231                 case 'I':
          232                         val = timeptr->tm_hour;
          233                         if (val == 0)
          234                                 val = 12;
          235                         if (val > 12)
          236                                 val -= 12;
          237                         goto number;
          238                 case 'j':
          239                         width = 3;
          240                         val = timeptr->tm_yday+1;
          241                         goto number;
          242                 case 'm':
          243                         val = timeptr->tm_mon+1;
          244                         goto number;
          245                 case 'M':
          246                         val = timeptr->tm_min;
          247                         goto number;
          248                 case 'n':
          249                         val = '\n';
          250                         goto character;
          251                 case 'p':
          252                         inc = sval(s, n, am_pm, 0, timeptr->tm_hour > 12, 2);
          253                         break;
          254                 case 'r':
          255                         tfmt = "%I:%M:%S %p";
          256                         goto recursive;
          257                 case 'R':
          258                         tfmt = "%H:%M";
          259                         goto recursive;
          260                 case 'S':
          261                         val = timeptr->tm_sec;
          262                         goto number;
          263                 case 't':
          264                         val = '\t';
          265                         goto character;
          266                 case 'u':
          267                         width = 1;
          268                         val = isoday(timeptr);
          269                         goto number;
          270                 case 'U':
          271                         val = weeknum(timeptr, SUN);
          272                         goto number;
          273                 case 'V':
          274                         val = isoweek(timeptr);
          275                         goto number;
          276                 case 'w':
          277                         width = 1;
          278                         val = timeptr->tm_wday;
          279                         goto number;
          280                 case 'W':
          281                         val = weeknum(timeptr, MON);
          282                         goto number;
          283                 case 'X':
          284                 case 'T':
          285                         tfmt = "%H:%M:%S";
          286                         goto recursive;
          287                 case 'x':
          288                         tfmt = "%m/%d/%y";
          289                         goto recursive;
          290                 case 'y':
          291                         val = timeptr->tm_year%100;
          292                         goto number;
          293                 case 'Y':
          294                         width = 4;
          295                         val = 1900 + timeptr->tm_year;
          296                         goto number;
          297                 case 'z':
          298                         inc = timezone(s, n, timeptr);
          299                         break;
          300                 case 'Z':
          301                         inc = strlen(timeptr->tm_zone);
          302                         if (inc > n)
          303                                 inc = n;
          304                         memcpy(s, timeptr->tm_zone, inc);
          305                         break;
          306                 case '\0':
          307                         inc = 0;
          308                         --format;
          309                         break;
          310                 case '%':
          311                         val = '%';
          312                 character:
          313                         *s = val;
          314                         inc = 1;
          315                         break;
          316                 number:
          317                         inc = dval(s, n, width, fill, val);
          318                         break;
          319                 recursive:
          320                         inc = strftime(s, n+1, tfmt, timeptr);
          321                         break;
          322                 }
          323         }
          324 
          325         n = s - begin;
          326         if (n == maxsize)
          327                 return 0;
          328         *s = '\0';
          329 
          330         return n;
          331 }