strftime.c (4026B)
1 #include <time.h> 2 #include <string.h> 3 4 #include "../libc.h" 5 #undef strftime 6 7 static char *days[] = { 8 "Sunday", "Monday", "Tuesday", "Wednesday", 9 "Thursday", "Friday", "Saturday", 10 }; 11 12 static char *months[] = { 13 "January", "February", "March", "April", 14 "May", "June", "July", "August", 15 "September", "October", "November", "December" 16 }; 17 18 static char *am_pm[] = {"AM", "PM"}; 19 20 static size_t 21 sval(char *s, size_t siz, char **strs, int abrev, int idx, int max) 22 { 23 char *str; 24 size_t len; 25 26 if (idx < 0 && idx >= max) 27 goto wrong; 28 29 str = strs[idx]; 30 len = (!abrev) ? strlen(str) : 3; 31 if (len > siz) 32 goto wrong; 33 34 memcpy(s, str, len); 35 return len; 36 37 wrong: 38 *s = '?'; 39 return 1; 40 } 41 42 static size_t 43 dval(char *s, size_t siz, int prec, int fill, int val) 44 { 45 char *t; 46 int n; 47 static char digits[] = "0123456789"; 48 49 if (prec > siz || val < 0) { 50 *s = '?'; 51 return 1; 52 } 53 54 n = prec; 55 do { 56 s[--n] = digits[val % 10]; 57 val /= 10; 58 } while (n > 0 && val > 0); 59 60 while (n > 0) 61 s[--n] = fill; 62 63 return prec; 64 } 65 66 static size_t 67 timezone(char *s, size_t prec, const struct tm * restrict tm) 68 { 69 long off = tm->tm_gmtoff; 70 71 if (prec < 5) { 72 *s = '?'; 73 return 1; 74 } 75 76 if (off >= 0) { 77 *s++ = '+'; 78 } else { 79 *s++ = '-'; 80 off = -off; 81 } 82 83 dval(s, 2, 2, '0', off / 3600); 84 dval(s, 2, 2, '0', (off % 3600) / 60); 85 86 return 5; 87 } 88 89 size_t 90 strftime(char * restrict s, size_t siz, 91 const char * restrict fmt, 92 const struct tm * restrict tm) 93 { 94 int ch, abrev, val, fill, width; 95 size_t n, inc; 96 char *tfmt; 97 98 for (n = siz-1; (ch = *fmt++) && n > 0; s += inc, n -= inc) { 99 if (ch != '%') { 100 *s = ch; 101 inc = 1; 102 continue; 103 } 104 105 abrev = 0; 106 fill = '0'; 107 width = 2; 108 109 switch (*fmt++) { 110 case 'Z': 111 if (!tm->tm_zone) 112 break; 113 inc = sval(s, n, &tm->tm_zone, 0, 0, 1); 114 break; 115 case 'a': 116 abrev = 1; 117 case 'A': 118 inc = sval(s, n, days, abrev, tm->tm_wday, 7); 119 break; 120 case 'h': 121 case 'b': 122 abrev = 1; 123 case 'B': 124 inc = sval(s, n, months, abrev, tm->tm_mon, 12); 125 break; 126 case 'p': 127 inc = sval(s, n, am_pm, 0, tm->tm_hour > 12, 2); 128 break; 129 case 'c': 130 tfmt = "%a %b %e %T %Y"; 131 goto recursive; 132 case 'D': 133 tfmt = "%m/%d/%y"; 134 goto recursive; 135 case 'F': 136 tfmt = "%Y-%m-%d"; 137 goto recursive; 138 case 'R': 139 tfmt = "%H:%M"; 140 goto recursive; 141 case 'X': 142 case 'T': 143 tfmt = "%H:%M:%S"; 144 goto recursive; 145 case 'r': 146 tfmt = "%I:%M:%S %p"; 147 goto recursive; 148 case 'x': 149 tfmt = "%m/%d/%y"; 150 goto recursive; 151 recursive: 152 inc = strftime(s, n+1, tfmt, tm) - 1; 153 break; 154 case 'n': 155 val = '\n'; 156 goto character; 157 case 't': 158 val = '\t'; 159 goto character; 160 case '%': 161 val = '%'; 162 character: 163 *s = val; 164 inc = 1; 165 break; 166 case 'e': 167 fill = ' '; 168 val = tm->tm_mday; 169 goto number; 170 case 'd': 171 val = tm->tm_mday; 172 goto number; 173 case 'V': 174 case 'g': 175 case 'G': 176 /* TODO */ 177 break; 178 case 'C': 179 val = tm->tm_year / 100; 180 goto number; 181 case 'H': 182 val = tm->tm_hour; 183 goto number; 184 case 'I': 185 val = tm->tm_hour; 186 if (val == 0) 187 val = 12; 188 if (val > 12) 189 val -= 12; 190 goto number; 191 case 'j': 192 width = 3; 193 val = tm->tm_yday+1; 194 goto number; 195 case 'm': 196 val = tm->tm_mon+1; 197 goto number; 198 case 'M': 199 val = tm->tm_min; 200 goto number; 201 case 'S': 202 val = tm->tm_sec; 203 goto number; 204 case 'u': 205 width = 1; 206 val = tm->tm_wday+1; 207 goto number; 208 case 'U': 209 val = tm->tm_yday / 7; 210 if (_newyear(tm->tm_year) == SAT) 211 val++; 212 goto number; 213 case 'W': 214 val = tm->tm_yday / 7; 215 if (_newyear(tm->tm_year) == MON) 216 val++; 217 goto number; 218 case 'w': 219 width = 1; 220 val = tm->tm_wday; 221 goto number; 222 case 'y': 223 val = tm->tm_year%100; 224 goto number; 225 case 'Y': 226 width = 4; 227 val = 1900 + tm->tm_year; 228 number: 229 inc = dval(s, n, width, fill, val); 230 break; 231 case 'z': 232 inc = timezone(s, n, tm); 233 break; 234 case 'E': 235 case 'O': 236 if (*fmt != '\0') 237 fmt += 2;; 238 case '\0': 239 inc = 0; 240 --fmt; 241 break; 242 } 243 } 244 *s = '\0'; 245 246 return siz - n; 247 }