X-Git-Url: http://nsz.repo.hu/git/?a=blobdiff_plain;f=src%2Ftime%2Fstrftime.c;h=cc53d5369ca717900ef5816ed7bdc400d149dc6b;hb=b1dfb734a45d4f74c7a24c5f07d37f7e74451802;hp=f1b9463114281a6a792f2247cd351fa647a7ad94;hpb=0b44a0315b47dd8eced9f3b7f31580cf14bbfc01;p=musl diff --git a/src/time/strftime.c b/src/time/strftime.c index f1b94631..cc53d536 100644 --- a/src/time/strftime.c +++ b/src/time/strftime.c @@ -1,172 +1,281 @@ #include #include +#include #include +#include #include -#include "__time.h" +#include +#include "locale_impl.h" +#include "time_impl.h" -// FIXME: integer overflows +static int is_leap(int y) +{ + /* Avoid overflow */ + if (y>INT_MAX-1900) y -= 2000; + y += 1900; + return !(y%4) && ((y%100) || !(y%400)); +} -const char *__langinfo(nl_item); +static int week_num(const struct tm *tm) +{ + int val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7; + /* If 1 Jan is just 1-3 days past Monday, + * the previous week is also in this year. */ + if ((tm->tm_wday + 371U - tm->tm_yday - 2) % 7 <= 2) + val++; + if (!val) { + val = 52; + /* If 31 December of prev year a Thursday, + * or Friday of a leap year, then the + * prev year has 53 weeks. */ + int dec31 = (tm->tm_wday + 7U - tm->tm_yday - 1) % 7; + if (dec31 == 4 || (dec31 == 5 && is_leap(tm->tm_year%400-1))) + val++; + } else if (val == 53) { + /* If 1 January is not a Thursday, and not + * a Wednesday of a leap year, then this + * year has only 52 weeks. */ + int jan1 = (tm->tm_wday + 371U - tm->tm_yday) % 7; + if (jan1 != 4 && (jan1 != 3 || !is_leap(tm->tm_year))) + val = 1; + } + return val; +} -size_t strftime(char *s, size_t n, const char *f, const struct tm *tm) +const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm *tm, locale_t loc, int pad) { nl_item item; - int val; - const char *fmt; - size_t l; - for (l=0; *f && ltm_wday; - goto nl_strcat; - case 'A': - item = DAY_1 + tm->tm_wday; - goto nl_strcat; - case 'h': - case 'b': - item = ABMON_1 + tm->tm_mon; - goto nl_strcat; - case 'B': - item = MON_1 + tm->tm_mon; - goto nl_strcat; - case 'c': - item = D_T_FMT; - goto nl_strftime; - case 'C': - val = (1900+tm->tm_year) / 100; - fmt = "%02d"; - goto number; - case 'd': - val = tm->tm_mday; - fmt = "%02d"; - goto number; - case 'D': - fmt = "%m/%d/%y"; - goto recu_strftime; - case 'e': - val = tm->tm_mday; - fmt = "%2d"; - goto number; - case 'F': - fmt = "%Y-%m-%d"; - goto recu_strftime; - case 'g': - // FIXME - val = 0; //week_based_year(tm)%100; - fmt = "%02d"; - goto number; - case 'G': - // FIXME - val = 0; //week_based_year(tm); - fmt = "%04d"; - goto number; - case 'H': - val = tm->tm_hour; - fmt = "%02d"; - goto number; - case 'I': - val = tm->tm_hour; - if (!val) val = 12; - else if (val > 12) val -= 12; - fmt = "%02d"; - goto number; - case 'j': - val = tm->tm_yday+1; - fmt = "%03d"; - goto number; - case 'm': - val = tm->tm_mon+1; - fmt = "%02d"; - goto number; - case 'M': - val = tm->tm_min; - fmt = "%02d"; - goto number; - case 'n': - s[l++] = '\n'; - continue; - case 'p': - item = tm->tm_hour >= 12 ? PM_STR : AM_STR; - goto nl_strcat; - case 'r': - item = T_FMT_AMPM; - goto nl_strftime; - case 'R': - fmt = "%H:%M"; - goto recu_strftime; - case 'S': - val = tm->tm_sec; - fmt = "%02d"; - goto number; - case 't': - s[l++] = '\t'; - continue; - case 'T': - fmt = "%H:%M:%S"; - goto recu_strftime; - case 'u': - val = tm->tm_wday ? tm->tm_wday : 7; - fmt = "%d"; - goto number; - case 'U': - case 'V': - case 'W': - // FIXME: week number mess.. - continue; - case 'w': - val = tm->tm_wday; - fmt = "%d"; - goto number; - case 'x': - item = D_FMT; - goto nl_strftime; - case 'X': - item = T_FMT; - goto nl_strftime; - case 'y': - val = tm->tm_year % 100; - fmt = "%02d"; - goto number; - case 'Y': - val = tm->tm_year + 1900; - fmt = "%04d"; - goto number; - case 'z': - if (tm->tm_isdst < 0) continue; - val = -__timezone - (tm->tm_isdst ? __dst_offset : 0); - l += snprintf(s+l, n-l, "%+.2d%.2d", val/3600, abs(val%3600)/60); - continue; - case 'Z': - if (tm->tm_isdst < 0 || !__tzname[0] || !__tzname[0][0]) - continue; - l += snprintf(s+l, n-l, "%s", __tzname[!!tm->tm_isdst]); - continue; - default: - return 0; + long long val; + const char *fmt = "-"; + int width = 2, def_pad = '0'; + + switch (f) { + case 'a': + if (tm->tm_wday > 6U) goto string; + item = ABDAY_1 + tm->tm_wday; + goto nl_strcat; + case 'A': + if (tm->tm_wday > 6U) goto string; + item = DAY_1 + tm->tm_wday; + goto nl_strcat; + case 'h': + case 'b': + if (tm->tm_mon > 11U) goto string; + item = ABMON_1 + tm->tm_mon; + goto nl_strcat; + case 'B': + if (tm->tm_mon > 11U) goto string; + item = MON_1 + tm->tm_mon; + goto nl_strcat; + case 'c': + item = D_T_FMT; + goto nl_strftime; + case 'C': + val = (1900LL+tm->tm_year) / 100; + goto number; + case 'e': + def_pad = '_'; + case 'd': + val = tm->tm_mday; + goto number; + case 'D': + fmt = "%m/%d/%y"; + goto recu_strftime; + case 'F': + fmt = "%Y-%m-%d"; + goto recu_strftime; + case 'g': + case 'G': + val = tm->tm_year + 1900LL; + if (tm->tm_yday < 3 && week_num(tm) != 1) val--; + else if (tm->tm_yday > 360 && week_num(tm) == 1) val++; + if (f=='g') val %= 100; + else width = 4; + goto number; + case 'H': + val = tm->tm_hour; + goto number; + case 'I': + val = tm->tm_hour; + if (!val) val = 12; + else if (val > 12) val -= 12; + goto number; + case 'j': + val = tm->tm_yday+1; + width = 3; + goto number; + case 'm': + val = tm->tm_mon+1; + goto number; + case 'M': + val = tm->tm_min; + goto number; + case 'n': + *l = 1; + return "\n"; + case 'p': + item = tm->tm_hour >= 12 ? PM_STR : AM_STR; + goto nl_strcat; + case 'r': + item = T_FMT_AMPM; + goto nl_strftime; + case 'R': + fmt = "%H:%M"; + goto recu_strftime; + case 's': + val = __tm_to_secs(tm) - tm->__tm_gmtoff; + width = 1; + goto number; + case 'S': + val = tm->tm_sec; + goto number; + case 't': + *l = 1; + return "\t"; + case 'T': + fmt = "%H:%M:%S"; + goto recu_strftime; + case 'u': + val = tm->tm_wday ? tm->tm_wday : 7; + width = 1; + goto number; + case 'U': + val = (tm->tm_yday + 7U - tm->tm_wday) / 7; + goto number; + case 'W': + val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7; + goto number; + case 'V': + val = week_num(tm); + goto number; + case 'w': + val = tm->tm_wday; + width = 1; + goto number; + case 'x': + item = D_FMT; + goto nl_strftime; + case 'X': + item = T_FMT; + goto nl_strftime; + case 'y': + val = (tm->tm_year + 1900LL) % 100; + if (val < 0) val = -val; + goto number; + case 'Y': + val = tm->tm_year + 1900LL; + if (val >= 10000) { + *l = snprintf(*s, sizeof *s, "+%lld", val); + return *s; } + width = 4; + goto number; + case 'z': + if (tm->tm_isdst < 0) { + *l = 0; + return ""; } -literal: - s[l++] = *f; - continue; + *l = snprintf(*s, sizeof *s, "%+.4ld", + tm->__tm_gmtoff/3600*100 + tm->__tm_gmtoff%3600/60); + return *s; + case 'Z': + if (tm->tm_isdst < 0) { + *l = 0; + return ""; + } + fmt = __tm_to_tzname(tm); + goto string; + case '%': + *l = 1; + return "%"; + default: + return 0; + } number: - l += snprintf(s+l, n-l, fmt, val); - continue; + switch (pad ? pad : def_pad) { + case '-': *l = snprintf(*s, sizeof *s, "%lld", val); break; + case '_': *l = snprintf(*s, sizeof *s, "%*lld", width, val); break; + case '0': + default: *l = snprintf(*s, sizeof *s, "%0*lld", width, val); break; + } + return *s; nl_strcat: - l += snprintf(s+l, n-l, "%s", __langinfo(item)); - continue; + fmt = __nl_langinfo_l(item, loc); +string: + *l = strlen(fmt); + return fmt; nl_strftime: - fmt = __langinfo(item); + fmt = __nl_langinfo_l(item, loc); recu_strftime: - l += strftime(s+l, n-l, fmt, tm); + *l = __strftime_l(*s, sizeof *s, fmt, tm, loc); + if (!*l) return 0; + return *s; +} + +size_t __strftime_l(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm, locale_t loc) +{ + size_t l, k; + char buf[100]; + char *p; + const char *t; + int pad, plus; + unsigned long width; + for (l=0; ltm_year < -1900) { + s[l++] = '-'; + width--; + } else if (plus && d+(width-k) >= (*p=='C'?3:5)) { + s[l++] = '+'; + width--; + } + for (; width > k && l < n; width--) + s[l++] = '0'; + } + if (k > n-l) k = n-l; + memcpy(s+l, t, k); + l += k; + } + if (n) { + if (l==n) l=n-1; + s[l] = 0; } - if (l >= n) return 0; - s[l] = 0; - return l; + return 0; +} + +size_t strftime(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm) +{ + return __strftime_l(s, n, f, tm, CURRENT_LOCALE); } + +weak_alias(__strftime_l, strftime_l);