strftime.c raw

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