1#include <string.h>2#include <time.h>34#include "../libc.h"56#undef strftime78static char *days[] = {9 "Sunday", "Monday", "Tuesday", "Wednesday",10 "Thursday", "Friday", "Saturday",11};1213static char *months[] = {14 "January", "February", "March", "April",15 "May", "June", "July", "August",16 "September", "October", "November", "December"17};1819static char *am_pm[] = {"AM", "PM"};2021static int22first(int day, int year)23{24 int ny;2526 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}3334static int35weeknum(const struct tm *tm, int day)36{37 int fday, val;3839 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}4950static int51isoyear(const struct tm *tm)52{53 int monday;5455 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}6667static int68isoweek(const struct tm *tm)69{70 int year, monday, yday, val;7172 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}8485static int86isoday(const struct tm *tm)87{88 if (tm->tm_wday == 0)89 return 7;90 return tm->tm_wday;91}9293static size_t94sval(char *s, size_t siz, char **strs, int abrev, int idx, int max)95{96 char *str;97 size_t len;9899 if (siz == 0)100 return 0;101102 if (idx < 0 || idx >= max) {103 *s = '?';104 return 1;105 }106107 str = strs[idx];108 len = (!abrev) ? strlen(str) : 3;109 if (len > siz)110 len = siz;111112 memcpy(s, str, len);113 return len;114}115116static size_t117dval(char *s, size_t siz, int prec, int fill, int val)118{119 char *t;120 int n;121 static char digits[] = "0123456789";122123 if (siz == 0)124 return 0;125126 if (prec > siz)127 prec = siz;128129 if (val < 0) {130 *s = '?';131 return 1;132 }133134 n = prec;135 do {136 s[--n] = digits[val % 10];137 val /= 10;138 } while (n > 0 && val > 0);139140 while (n > 0)141 s[--n] = fill;142143 return prec;144}145146static size_t147timezone(char *s, size_t siz, const struct tm * restrict tm)148{149 size_t n;150 long off = tm->tm_gmtoff;151152 if (off >= 0) {153 *s = '+';154 } else {155 *s = '-';156 off = -off;157 }158159 n = 1;160 n += dval(s + n, siz-n, 2, '0', off / 3600);161 n += dval(s + n, siz-n, 2, '0', (off % 3600) / 60);162163 return n;164}165166size_t167strftime(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;173174 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 }181182 abrev = 0;183 fill = '0';184 width = 2;185186 if (*format == 'E' || *format == 'O')187 format++;188189 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 }324325 n = s - begin;326 if (n == maxsize)327 return 0;328 *s = '\0';329330 return n;331}