1
0

strftime.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <langinfo.h>
  4. #include <locale.h>
  5. #include <time.h>
  6. #include <limits.h>
  7. #include "libc.h"
  8. // FIXME: integer overflows
  9. const char *__nl_langinfo_l(nl_item, locale_t);
  10. static int is_leap(int y)
  11. {
  12. /* Avoid overflow */
  13. if (y>INT_MAX-1900) y -= 2000;
  14. y += 1900;
  15. return !(y%4) && ((y%100) || !(y%400));
  16. }
  17. static int week_num(const struct tm *tm)
  18. {
  19. int val = (tm->tm_yday + 7 - (tm->tm_wday+6)%7) / 7;
  20. /* If 1 Jan is just 1-3 days past Monday,
  21. * the previous week is also in this year. */
  22. if ((tm->tm_wday - tm->tm_yday - 2 + 371) % 7 <= 2)
  23. val++;
  24. if (!val) {
  25. val = 52;
  26. /* If 31 December of prev year a Thursday,
  27. * or Friday of a leap year, then the
  28. * prev year has 53 weeks. */
  29. int dec31 = (tm->tm_wday - tm->tm_yday - 1 + 7) % 7;
  30. if (dec31 == 4 || (dec31 == 5 && is_leap(tm->tm_year%400-1)))
  31. val++;
  32. } else if (val == 53) {
  33. /* If 1 January is not a Thursday, and not
  34. * a Wednesday of a leap year, then this
  35. * year has only 52 weeks. */
  36. int jan1 = (tm->tm_wday - tm->tm_yday + 371) % 7;
  37. if (jan1 != 4 && (jan1 != 3 || !is_leap(tm->tm_year)))
  38. val = 1;
  39. }
  40. return val;
  41. }
  42. size_t __strftime_l(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm, locale_t loc)
  43. {
  44. nl_item item;
  45. int val;
  46. const char *fmt;
  47. size_t l;
  48. for (l=0; *f && l<n; f++) {
  49. if (*f != '%') {
  50. literal:
  51. s[l++] = *f;
  52. continue;
  53. }
  54. do_fmt:
  55. switch (*++f) {
  56. case '%':
  57. goto literal;
  58. case 'E':
  59. case 'O':
  60. goto do_fmt;
  61. case 'a':
  62. item = ABDAY_1 + tm->tm_wday;
  63. goto nl_strcat;
  64. case 'A':
  65. item = DAY_1 + tm->tm_wday;
  66. goto nl_strcat;
  67. case 'h':
  68. case 'b':
  69. item = ABMON_1 + tm->tm_mon;
  70. goto nl_strcat;
  71. case 'B':
  72. item = MON_1 + tm->tm_mon;
  73. goto nl_strcat;
  74. case 'c':
  75. item = D_T_FMT;
  76. goto nl_strftime;
  77. case 'C':
  78. val = (1900+tm->tm_year) / 100;
  79. fmt = "%02d";
  80. goto number;
  81. case 'd':
  82. val = tm->tm_mday;
  83. fmt = "%02d";
  84. goto number;
  85. case 'D':
  86. fmt = "%m/%d/%y";
  87. goto recu_strftime;
  88. case 'e':
  89. val = tm->tm_mday;
  90. fmt = "%2d";
  91. goto number;
  92. case 'F':
  93. fmt = "%Y-%m-%d";
  94. goto recu_strftime;
  95. case 'g':
  96. case 'G':
  97. fmt = "%04d";
  98. val = tm->tm_year + 1900;
  99. if (tm->tm_yday < 3 && week_num(tm) != 1) val--;
  100. else if (tm->tm_yday > 360 && week_num(tm) == 1) val++;
  101. if (*f=='g') {
  102. fmt = "%02d";
  103. val %= 100;
  104. }
  105. goto number;
  106. case 'H':
  107. val = tm->tm_hour;
  108. fmt = "%02d";
  109. goto number;
  110. case 'I':
  111. val = tm->tm_hour;
  112. if (!val) val = 12;
  113. else if (val > 12) val -= 12;
  114. fmt = "%02d";
  115. goto number;
  116. case 'j':
  117. val = tm->tm_yday+1;
  118. fmt = "%03d";
  119. goto number;
  120. case 'm':
  121. val = tm->tm_mon+1;
  122. fmt = "%02d";
  123. goto number;
  124. case 'M':
  125. val = tm->tm_min;
  126. fmt = "%02d";
  127. goto number;
  128. case 'n':
  129. s[l++] = '\n';
  130. continue;
  131. case 'p':
  132. item = tm->tm_hour >= 12 ? PM_STR : AM_STR;
  133. goto nl_strcat;
  134. case 'r':
  135. item = T_FMT_AMPM;
  136. goto nl_strftime;
  137. case 'R':
  138. fmt = "%H:%M";
  139. goto recu_strftime;
  140. case 'S':
  141. val = tm->tm_sec;
  142. fmt = "%02d";
  143. goto number;
  144. case 't':
  145. s[l++] = '\t';
  146. continue;
  147. case 'T':
  148. fmt = "%H:%M:%S";
  149. goto recu_strftime;
  150. case 'u':
  151. val = tm->tm_wday ? tm->tm_wday : 7;
  152. fmt = "%d";
  153. goto number;
  154. case 'U':
  155. val = (tm->tm_yday + 7 - tm->tm_wday) / 7;
  156. fmt = "%02d";
  157. goto number;
  158. case 'W':
  159. val = (tm->tm_yday + 7 - (tm->tm_wday+6)%7) / 7;
  160. fmt = "%02d";
  161. goto number;
  162. case 'V':
  163. val = week_num(tm);
  164. fmt = "%02d";
  165. goto number;
  166. case 'w':
  167. val = tm->tm_wday;
  168. fmt = "%d";
  169. goto number;
  170. case 'x':
  171. item = D_FMT;
  172. goto nl_strftime;
  173. case 'X':
  174. item = T_FMT;
  175. goto nl_strftime;
  176. case 'y':
  177. val = tm->tm_year % 100;
  178. fmt = "%02d";
  179. goto number;
  180. case 'Y':
  181. val = tm->tm_year + 1900;
  182. fmt = "%04d";
  183. goto number;
  184. case 'z':
  185. val = -tm->__tm_gmtoff;
  186. l += snprintf(s+l, n-l, "%+.2d%.2d", val/3600, abs(val%3600)/60);
  187. continue;
  188. case 'Z':
  189. l += snprintf(s+l, n-l, "%s", tm->__tm_zone);
  190. continue;
  191. default:
  192. return 0;
  193. }
  194. number:
  195. l += snprintf(s+l, n-l, fmt, val);
  196. continue;
  197. nl_strcat:
  198. l += snprintf(s+l, n-l, "%s", __nl_langinfo_l(item, loc));
  199. continue;
  200. nl_strftime:
  201. fmt = __nl_langinfo_l(item, loc);
  202. recu_strftime:
  203. l += __strftime_l(s+l, n-l, fmt, tm, loc);
  204. }
  205. if (l >= n) return 0;
  206. s[l] = 0;
  207. return l;
  208. }
  209. size_t strftime(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm)
  210. {
  211. return __strftime_l(s, n, f, tm, 0);
  212. }
  213. weak_alias(__strftime_l, strftime_l);