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 }