scc

simple c99 compiler
git clone git://git.simple-cc.org/scc
Log | Files | Refs | README | LICENSE

strftime.c (5326B)


      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(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(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(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(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 (idx < 0 && idx >= max)
    100 		goto wrong;
    101 
    102 	str = strs[idx];
    103 	len = (!abrev) ? strlen(str) : 3;
    104 	if (len > siz)
    105 		goto wrong;
    106 
    107 	memcpy(s, str, len);
    108 	return len;
    109 
    110 wrong:
    111 	*s = '?';
    112 	return 1;
    113 }
    114 
    115 static size_t
    116 dval(char *s, size_t siz, int prec, int fill, int val)
    117 {
    118 	char *t;
    119 	int n;
    120 	static char digits[] = "0123456789";
    121 
    122 	if (prec > siz || val < 0) {
    123 		*s = '?';
    124 		return 1;
    125 	}
    126 
    127 	n = prec;
    128 	do {
    129 		s[--n] = digits[val % 10];
    130 		val /= 10;
    131 	} while (n > 0 && val > 0);
    132 
    133 	while (n > 0)
    134 		s[--n] = fill;
    135 
    136 	return prec;
    137 }
    138 
    139 static size_t
    140 timezone(char *s, size_t prec, const struct tm * restrict tm)
    141 {
    142 	long off = tm->tm_gmtoff;
    143 
    144         if (prec < 5) {
    145                 *s = '?';
    146                 return 1;
    147         }
    148 
    149 	if (off >= 0) {
    150 		*s++ = '+';
    151 	} else {
    152 		*s++ = '-';
    153 		off = -off;
    154 	}
    155 
    156 	dval(s, 2, 2, '0', off / 3600);
    157 	dval(s, 2, 2, '0', (off % 3600) / 60);
    158 
    159 	return 5;
    160 }
    161 
    162 size_t
    163 strftime(char *restrict s, size_t maxsize,
    164 	 const char *restrict format, const struct tm *restrict timeptr)
    165 {
    166 	int ch, abrev, val, fill, width;
    167 	size_t n, inc;
    168 	char *tfmt;
    169 
    170 	for (n = --maxsize; (ch = *format++) && n > 0; s += inc, n -= inc) {
    171 		if (ch != '%') {
    172 			*s = ch;
    173 			inc = 1;
    174 			continue;
    175 		}
    176 
    177 		abrev = 0;
    178 		fill = '0';
    179 		width = 2;
    180 
    181 		if (*format == 'E' || *format == 'O')
    182 			format++;
    183 
    184 		switch (*format++) {
    185 		case 'a':
    186 			abrev = 1;
    187 		case 'A':
    188 			inc = sval(s, n, days, abrev, timeptr->tm_wday, 7);
    189 			break;
    190 		case 'h':
    191 		case 'b':
    192 			abrev = 1;
    193 		case 'B':
    194 			inc = sval(s, n, months, abrev, timeptr->tm_mon, 12);
    195 			break;
    196 		case 'c':
    197 			tfmt = "%a %b %e %T %Y";
    198 			goto recursive;
    199 		case 'C':
    200 			val = (timeptr->tm_year + 1900) / 100;
    201 			goto number;
    202 		case 'd':
    203 			val = timeptr->tm_mday;
    204 			goto number;
    205 		case 'D':
    206 			tfmt = "%m/%d/%y";
    207 			goto recursive;
    208 		case 'e':
    209 			fill = ' ';
    210 			val = timeptr->tm_mday;
    211 			goto number;
    212 		case 'F':
    213 			tfmt = "%Y-%m-%d";
    214 			goto recursive;
    215 		case 'g':
    216 			val = isoyear(timeptr);
    217 			goto number;
    218 		case 'G':
    219 			val = isoyear(timeptr);
    220 			val += 1900;
    221 			width = 4;
    222 			goto number;
    223 		case 'H':
    224 			val = timeptr->tm_hour;
    225 			goto number;
    226 		case 'I':
    227 			val = timeptr->tm_hour;
    228 			if (val == 0)
    229 				val = 12;
    230 			if (val > 12)
    231 				val -= 12;
    232 			goto number;
    233 		case 'j':
    234 			width = 3;
    235 			val = timeptr->tm_yday+1;
    236 			goto number;
    237 		case 'm':
    238 			val = timeptr->tm_mon+1;
    239 			goto number;
    240 		case 'M':
    241 			val = timeptr->tm_min;
    242 			goto number;
    243 		case 'n':
    244 			val = '\n';
    245 			goto character;
    246 		case 'p':
    247 			inc = sval(s, n, am_pm, 0, timeptr->tm_hour > 12, 2);
    248 			break;
    249 		case 'r':
    250 			tfmt = "%I:%M:%S %p";
    251 			goto recursive;
    252 		case 'R':
    253 			tfmt = "%H:%M";
    254 			goto recursive;
    255 		case 'S':
    256 			val = timeptr->tm_sec;
    257 			goto number;
    258 		case 't':
    259 			val = '\t';
    260 			goto character;
    261 		case 'u':
    262 			width = 1;
    263 			val = isoday(timeptr);
    264 			goto number;
    265 		case 'U':
    266 			val = weeknum(timeptr, SUN);
    267 			goto number;
    268 		case 'V':
    269 			val = isoweek(timeptr);
    270 			goto number;
    271 		case 'w':
    272 			width = 1;
    273 			val = timeptr->tm_wday;
    274 			goto number;
    275 		case 'W':
    276 			val = weeknum(timeptr, MON);
    277 			goto number;
    278 		case 'X':
    279 		case 'T':
    280 			tfmt = "%H:%M:%S";
    281 			goto recursive;
    282 		case 'x':
    283 			tfmt = "%m/%d/%y";
    284 			goto recursive;
    285 		case 'y':
    286 			val = timeptr->tm_year%100;
    287 			goto number;
    288 		case 'Y':
    289 			width = 4;
    290 			val = 1900 + timeptr->tm_year;
    291 			goto number;
    292 		case 'z':
    293 			inc = timezone(s, n, timeptr);
    294 			break;
    295 		case 'Z':
    296 			memcpy(s, timeptr->tm_zone, 3);
    297 			inc = 3;
    298 			break;
    299 		case '\0':
    300 			inc = 0;
    301 			--format;
    302 			break;
    303 		case '%':
    304 			val = '%';
    305 		character:
    306 			*s = val;
    307 			inc = 1;
    308 			break;
    309 		number:
    310 			inc = dval(s, n, width, fill, val);
    311 			break;
    312 		recursive:
    313 			inc = strftime(s, n+1, tfmt, timeptr);
    314 			break;
    315 		}
    316 	}
    317 	*s = '\0';
    318 
    319 	return maxsize - n;
    320 }