xref: /plan9/sys/src/ape/lib/ap/gen/strftime.c (revision 3e12c5d1bb89fc02707907988834ef147769ddaf)
1 #include <time.h>
2 #include <string.h>
3 
4 static char *awday[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
5 static char *wday[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
6 			"Friday", "Saturday"};
7 static char *amon[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
8 			    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
9 static char *mon[12] = {"January", "February", "March", "April", "May", "June",
10 		"July", "August", "September", "October", "November", "December"};
11 static char *ampm[2] = {"AM", "PM"};
12 static char *tz[2] = {"EST", "EDT"};
13 
14 static int jan1(int);
15 static char *strval(char *, char *, char **, int, int);
16 static char *dval(char *, char *, int, int);
17 
18 size_t
strftime(char * s,size_t maxsize,const char * format,const struct tm * t)19 strftime(char *s, size_t maxsize, const char *format, const struct tm *t)
20 {
21 	char *sp, *se, *fp;
22 	int i;
23 
24 	sp = s;
25 	se = s+maxsize;
26 	for(fp=(char *)format; *fp && sp<se; fp++){
27 		if(*fp != '%')
28 			*sp++ = *fp;
29 		else switch(*++fp){
30 			case 'a':
31 				sp = strval(sp, se, awday, t->tm_wday, 7);
32 				break;
33 			case 'A':
34 				sp = strval(sp, se, wday, t->tm_wday, 7);
35 				break;
36 			case 'b':
37 				sp = strval(sp, se, amon, t->tm_mon, 12);
38 				break;
39 			case 'B':
40 				sp = strval(sp, se, mon, t->tm_mon, 12);
41 				break;
42 			case 'c':
43 				sp += strftime(sp, se-sp, "%a %b %d %H:%M:%S %Y", t);
44 				break;
45 			case 'd':
46 				sp = dval(sp, se, t->tm_mday, 2);
47 				break;
48 			case 'H':
49 				sp = dval(sp, se, t->tm_hour, 2);
50 				break;
51 			case 'I':
52 				i = t->tm_hour;
53 				if(i == 0)
54 					i = 12;
55 				else if(i > 12)
56 					i -= 12;
57 				sp = dval(sp, se, i, 2);
58 				break;
59 			case 'j':
60 				sp = dval(sp, se, t->tm_yday+1, 3);
61 				break;
62 			case 'm':
63 				sp = dval(sp, se, t->tm_mon+1, 2);
64 				break;
65 			case 'M':
66 				sp = dval(sp, se, t->tm_min, 2);
67 				break;
68 			case 'p':
69 				i = (t->tm_hour < 12)? 0 : 1;
70 				sp = strval(sp, se, ampm, i, 2);
71 				break;
72 			case 'S':
73 				sp = dval(sp, se, t->tm_sec, 2);
74 				break;
75 			case 'U':
76 				i = 7-jan1(t->tm_year);
77 				if(i == 7)
78 					i = 0;
79 				/* Now i is yday number of first sunday in year */
80 				if(t->tm_yday < i)
81 					i = 0;
82 				else
83 					i = (t->tm_yday-i)/7 + 1;
84 				sp = dval(sp, se, i, 2);
85 				break;
86 			case 'w':
87 				sp = dval(sp, se, t->tm_wday, 1);
88 				break;
89 			case 'W':
90 				i = 8-jan1(t->tm_year);
91 				if(i >= 7)
92 					i -= 7;
93 				/* Now i is yday number of first monday in year */
94 				if(t->tm_yday < i)
95 					i = 0;
96 				else
97 					i = (t->tm_yday-i)/7 + 1;
98 				sp = dval(sp, se, i, 2);
99 				break;
100 			case 'x':
101 				sp += strftime(sp, se-sp, "%a %b %d, %Y", t);
102 				break;
103 			case 'X':
104 				sp += strftime(sp, se-sp, "%H:%M:%S", t);
105 				break;
106 			case 'y':
107 				sp = dval(sp, se, t->tm_year%100, 2);
108 				break;
109 			case 'Y':
110 				sp = dval(sp, se, t->tm_year+1900, 4);
111 				break;
112 			case 'Z':
113 				/* hack for now: assume eastern time zone */
114 				i = t->tm_isdst? 1 : 0;
115 				sp = strval(sp, se, tz, i, 2);
116 				break;
117 			case 0:
118 				fp--; /* stop loop after next fp incr */
119 				break;
120 			default:
121 				*sp++ = *fp;
122 			}
123 	}
124 	if(*fp)
125 		sp = s; /* format string didn't end: no room for conversion */
126 	if(sp<se)
127 		*sp = 0;
128 	return sp-s;
129 }
130 
131 static char *
strval(char * start,char * end,char ** array,int index,int alen)132 strval(char *start, char *end, char **array, int index, int alen)
133 {
134 	int n;
135 
136 	if(index<0 || index>=alen){
137 		*start = '?';
138 		return start+1;
139 	}
140 	n = strlen(array[index]);
141 	if(n > end-start)
142 		n = end-start;
143 	memcpy(start, array[index], n);
144 	return start+n;
145 }
146 
147 static char *
dval(char * start,char * end,int val,int width)148 dval(char *start, char *end, int val, int width)
149 {
150 	char *p;
151 
152 	if(val<0 || end-start<width){
153 		*start = '?';
154 		return start+1;
155 	}
156 	p = start+width-1;
157 	while(p>=start){
158 		*p-- = val%10 + '0';
159 		val /= 10;
160 	}
161 	if(val>0)
162 		*start = '*';
163 	return start+width;
164 }
165 
166 /*
167  *	return day of the week
168  *	of jan 1 of given year
169  */
170 static int
jan1(int yr)171 jan1(int yr)
172 {
173 	int y, d;
174 
175 /*
176  *	normal gregorian calendar
177  *	one extra day per four years
178  */
179 
180 	y = yr+1900;
181 	d = 4+y+(y+3)/4;
182 
183 /*
184  *	julian calendar
185  *	regular gregorian
186  *	less three days per 400
187  */
188 
189 	if(y > 1800) {
190 		d -= (y-1701)/100;
191 		d += (y-1601)/400;
192 	}
193 
194 /*
195  *	great calendar changeover instant
196  */
197 
198 	if(y > 1752)
199 		d += 3;
200 
201 	return(d%7);
202 }
203