1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <langinfo.h> 5 #include <locale.h> 6 #include <time.h> 7 #include <limits.h> 8 #include "locale_impl.h" 9 #include "time_impl.h" 10 11 static int is_leap(int y) 12 { 13 /* Avoid overflow */ 14 if (y>INT_MAX-1900) y -= 2000; 15 y += 1900; 16 return !(y%4) && ((y%100) || !(y%400)); 17 } 18 19 static int week_num(const struct tm *tm) 20 { 21 int val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7; 22 /* If 1 Jan is just 1-3 days past Monday, 23 * the previous week is also in this year. */ 24 if ((tm->tm_wday + 371U - tm->tm_yday - 2) % 7 <= 2) 25 val++; 26 if (!val) { 27 val = 52; 28 /* If 31 December of prev year a Thursday, 29 * or Friday of a leap year, then the 30 * prev year has 53 weeks. */ 31 { 32 int dec31 = (tm->tm_wday + 7U - tm->tm_yday - 1) % 7; 33 if (dec31 == 4 || (dec31 == 5 && is_leap(tm->tm_year%400-1))) 34 val++; 35 } 36 } else if (val == 53) { 37 /* If 1 January is not a Thursday, and not 38 * a Wednesday of a leap year, then this 39 * year has only 52 weeks. */ 40 int jan1 = (tm->tm_wday + 371U - tm->tm_yday) % 7; 41 if (jan1 != 4 && (jan1 != 3 || !is_leap(tm->tm_year))) 42 val = 1; 43 } 44 return val; 45 } 46 47 const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm *tm, locale_t loc, int pad) 48 { 49 nl_item item; 50 long long val; 51 const char *fmt = "-"; 52 int width = 2, def_pad = '0'; 53 54 switch (f) { 55 case 'a': 56 if (tm->tm_wday > 6U) goto string; 57 item = ABDAY_1 + tm->tm_wday; 58 goto nl_strcat; 59 case 'A': 60 if (tm->tm_wday > 6U) goto string; 61 item = DAY_1 + tm->tm_wday; 62 goto nl_strcat; 63 case 'h': 64 case 'b': 65 if (tm->tm_mon > 11U) goto string; 66 item = ABMON_1 + tm->tm_mon; 67 goto nl_strcat; 68 case 'B': 69 if (tm->tm_mon > 11U) goto string; 70 item = MON_1 + tm->tm_mon; 71 goto nl_strcat; 72 case 'c': 73 item = D_T_FMT; 74 goto nl_strftime; 75 case 'C': 76 val = (1900LL+tm->tm_year) / 100; 77 goto number; 78 case 'e': 79 def_pad = '_'; 80 case 'd': 81 val = tm->tm_mday; 82 goto number; 83 case 'D': 84 fmt = "%m/%d/%y"; 85 goto recu_strftime; 86 case 'F': 87 fmt = "%Y-%m-%d"; 88 goto recu_strftime; 89 case 'g': 90 case 'G': 91 val = tm->tm_year + 1900LL; 92 if (tm->tm_yday < 3 && week_num(tm) != 1) val--; 93 else if (tm->tm_yday > 360 && week_num(tm) == 1) val++; 94 if (f=='g') val %= 100; 95 else width = 4; 96 goto number; 97 case 'H': 98 val = tm->tm_hour; 99 goto number; 100 case 'I': 101 val = tm->tm_hour; 102 if (!val) val = 12; 103 else if (val > 12) val -= 12; 104 goto number; 105 case 'j': 106 val = tm->tm_yday+1; 107 width = 3; 108 goto number; 109 case 'm': 110 val = tm->tm_mon+1; 111 goto number; 112 case 'M': 113 val = tm->tm_min; 114 goto number; 115 case 'n': 116 *l = 1; 117 return "\n"; 118 case 'p': 119 item = tm->tm_hour >= 12 ? PM_STR : AM_STR; 120 goto nl_strcat; 121 case 'r': 122 item = T_FMT_AMPM; 123 goto nl_strftime; 124 case 'R': 125 fmt = "%H:%M"; 126 goto recu_strftime; 127 case 's': 128 val = __tm_to_secs(tm) - tm->__tm_gmtoff; 129 width = 1; 130 goto number; 131 case 'S': 132 val = tm->tm_sec; 133 goto number; 134 case 't': 135 *l = 1; 136 return "\t"; 137 case 'T': 138 fmt = "%H:%M:%S"; 139 goto recu_strftime; 140 case 'u': 141 val = tm->tm_wday ? tm->tm_wday : 7; 142 width = 1; 143 goto number; 144 case 'U': 145 val = (tm->tm_yday + 7U - tm->tm_wday) / 7; 146 goto number; 147 case 'W': 148 val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7; 149 goto number; 150 case 'V': 151 val = week_num(tm); 152 goto number; 153 case 'w': 154 val = tm->tm_wday; 155 width = 1; 156 goto number; 157 case 'x': 158 item = D_FMT; 159 goto nl_strftime; 160 case 'X': 161 item = T_FMT; 162 goto nl_strftime; 163 case 'y': 164 val = (tm->tm_year + 1900LL) % 100; 165 if (val < 0) val = -val; 166 goto number; 167 case 'Y': 168 val = tm->tm_year + 1900LL; 169 if (val >= 10000) { 170 *l = snprintf(*s, sizeof *s, "+%lld", val); 171 return *s; 172 } 173 width = 4; 174 goto number; 175 case 'z': 176 if (tm->tm_isdst < 0) { 177 *l = 0; 178 return ""; 179 } 180 *l = snprintf(*s, sizeof *s, "%+.4ld", 181 tm->__tm_gmtoff/3600*100 + tm->__tm_gmtoff%3600/60); 182 return *s; 183 case 'Z': 184 if (tm->tm_isdst < 0) { 185 *l = 0; 186 return ""; 187 } 188 fmt = __tm_to_tzname(tm); 189 goto string; 190 case '%': 191 *l = 1; 192 return "%"; 193 default: 194 return 0; 195 } 196 number: 197 switch (pad ? pad : def_pad) { 198 case '-': *l = snprintf(*s, sizeof *s, "%lld", val); break; 199 case '_': *l = snprintf(*s, sizeof *s, "%*lld", width, val); break; 200 case '0': 201 default: *l = snprintf(*s, sizeof *s, "%0*lld", width, val); break; 202 } 203 return *s; 204 nl_strcat: 205 fmt = __nl_langinfo_l(item, loc); 206 string: 207 *l = strlen(fmt); 208 return fmt; 209 nl_strftime: 210 fmt = __nl_langinfo_l(item, loc); 211 recu_strftime: 212 *l = __strftime_l(*s, sizeof *s, fmt, tm, loc); 213 if (!*l) return 0; 214 return *s; 215 } 216 217 size_t __strftime_l(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm, locale_t loc) 218 { 219 size_t l, k; 220 char buf[100]; 221 char *p; 222 const char *t; 223 int pad, plus; 224 unsigned long width; 225 for (l=0; l<n; f++) { 226 if (!*f) { 227 s[l] = 0; 228 return l; 229 } 230 if (*f != '%') { 231 s[l++] = *f; 232 continue; 233 } 234 f++; 235 pad = 0; 236 if (*f == '-' || *f == '_' || *f == '0') pad = *f++; 237 if ((plus = (*f == '+'))) f++; 238 width = strtoul(f, &p, 10); 239 if (*p == 'C' || *p == 'F' || *p == 'G' || *p == 'Y') { 240 if (!width && p!=f) width = 1; 241 } else { 242 width = 0; 243 } 244 f = p; 245 if (*f == 'E' || *f == 'O') f++; 246 t = __strftime_fmt_1(&buf, &k, *f, tm, loc, pad); 247 if (!t) break; 248 if (width) { 249 /* Trim off any sign and leading zeros, then 250 * count remaining digits to determine behavior 251 * for the + flag. */ 252 if (*t=='+' || *t=='-') t++, k--; 253 for (; *t=='0' && t[1]-'0'<10U; t++, k--); 254 if (width < k) width = k; 255 { 256 size_t d; 257 for (d=0; t[d]-'0'<10U; d++); 258 if (tm->tm_year < -1900) { 259 s[l++] = '-'; 260 width--; 261 } else if (plus && d+(width-k) >= (*p=='C'?3:5)) { 262 s[l++] = '+'; 263 width--; 264 } 265 for (; width > k && l < n; width--) 266 s[l++] = '0'; 267 } 268 } 269 if (k > n-l) k = n-l; 270 memcpy(s+l, t, k); 271 l += k; 272 } 273 if (n) { 274 if (l==n) l=n-1; 275 s[l] = 0; 276 } 277 return 0; 278 } 279 280 size_t strftime(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm) 281 { 282 return __strftime_l(s, n, f, tm, CURRENT_LOCALE); 283 } 284 285 weak_alias(__strftime_l, strftime_l); 286