scc

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

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 }