github.com/afumu/libc@v0.0.6/musl/src/time/strftime.c (about)

     1  #include <stdio.h>
     2  #include <stdlib.h>
     3  #include <string.h>
     4  #include <langinfo.h>
     5  #include <locale.h>
     6  #include <time.h>
     7  #include <limits.h>
     8  #include "locale_impl.h"
     9  #include "time_impl.h"
    10  
    11  static int is_leap(int y)
    12  {
    13  	/* Avoid overflow */
    14  	if (y>INT_MAX-1900) y -= 2000;
    15  	y += 1900;
    16  	return !(y%4) && ((y%100) || !(y%400));
    17  }
    18  
    19  static int week_num(const struct tm *tm)
    20  {
    21  	int val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7;
    22  	/* If 1 Jan is just 1-3 days past Monday,
    23  	 * the previous week is also in this year. */
    24  	if ((tm->tm_wday + 371U - tm->tm_yday - 2) % 7 <= 2)
    25  		val++;
    26  	if (!val) {
    27  		val = 52;
    28  		/* If 31 December of prev year a Thursday,
    29  		 * or Friday of a leap year, then the
    30  		 * prev year has 53 weeks. */
    31  		int dec31 = (tm->tm_wday + 7U - tm->tm_yday - 1) % 7;
    32  		if (dec31 == 4 || (dec31 == 5 && is_leap(tm->tm_year%400-1)))
    33  			val++;
    34  	} else if (val == 53) {
    35  		/* If 1 January is not a Thursday, and not
    36  		 * a Wednesday of a leap year, then this
    37  		 * year has only 52 weeks. */
    38  		int jan1 = (tm->tm_wday + 371U - tm->tm_yday) % 7;
    39  		if (jan1 != 4 && (jan1 != 3 || !is_leap(tm->tm_year)))
    40  			val = 1;
    41  	}
    42  	return val;
    43  }
    44  
    45  const char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm *tm, locale_t loc, int pad)
    46  {
    47  	nl_item item;
    48  	long long val;
    49  	const char *fmt = "-";
    50  	int width = 2, def_pad = '0';
    51  
    52  	switch (f) {
    53  	case 'a':
    54  		if (tm->tm_wday > 6U) goto string;
    55  		item = ABDAY_1 + tm->tm_wday;
    56  		goto nl_strcat;
    57  	case 'A':
    58  		if (tm->tm_wday > 6U) goto string;
    59  		item = DAY_1 + tm->tm_wday;
    60  		goto nl_strcat;
    61  	case 'h':
    62  	case 'b':
    63  		if (tm->tm_mon > 11U) goto string;
    64  		item = ABMON_1 + tm->tm_mon;
    65  		goto nl_strcat;
    66  	case 'B':
    67  		if (tm->tm_mon > 11U) goto string;
    68  		item = MON_1 + tm->tm_mon;
    69  		goto nl_strcat;
    70  	case 'c':
    71  		item = D_T_FMT;
    72  		goto nl_strftime;
    73  	case 'C':
    74  		val = (1900LL+tm->tm_year) / 100;
    75  		goto number;
    76  	case 'e':
    77  		def_pad = '_';
    78  	case 'd':
    79  		val = tm->tm_mday;
    80  		goto number;
    81  	case 'D':
    82  		fmt = "%m/%d/%y";
    83  		goto recu_strftime;
    84  	case 'F':
    85  		fmt = "%Y-%m-%d";
    86  		goto recu_strftime;
    87  	case 'g':
    88  	case 'G':
    89  		val = tm->tm_year + 1900LL;
    90  		if (tm->tm_yday < 3 && week_num(tm) != 1) val--;
    91  		else if (tm->tm_yday > 360 && week_num(tm) == 1) val++;
    92  		if (f=='g') val %= 100;
    93  		else width = 4;
    94  		goto number;
    95  	case 'H':
    96  		val = tm->tm_hour;
    97  		goto number;
    98  	case 'I':
    99  		val = tm->tm_hour;
   100  		if (!val) val = 12;
   101  		else if (val > 12) val -= 12;
   102  		goto number;
   103  	case 'j':
   104  		val = tm->tm_yday+1;
   105  		width = 3;
   106  		goto number;
   107  	case 'm':
   108  		val = tm->tm_mon+1;
   109  		goto number;
   110  	case 'M':
   111  		val = tm->tm_min;
   112  		goto number;
   113  	case 'n':
   114  		*l = 1;
   115  		return "\n";
   116  	case 'p':
   117  		item = tm->tm_hour >= 12 ? PM_STR : AM_STR;
   118  		goto nl_strcat;
   119  	case 'r':
   120  		item = T_FMT_AMPM;
   121  		goto nl_strftime;
   122  	case 'R':
   123  		fmt = "%H:%M";
   124  		goto recu_strftime;
   125  	case 's':
   126  		val = __tm_to_secs(tm) - tm->__tm_gmtoff;
   127  		width = 1;
   128  		goto number;
   129  	case 'S':
   130  		val = tm->tm_sec;
   131  		goto number;
   132  	case 't':
   133  		*l = 1;
   134  		return "\t";
   135  	case 'T':
   136  		fmt = "%H:%M:%S";
   137  		goto recu_strftime;
   138  	case 'u':
   139  		val = tm->tm_wday ? tm->tm_wday : 7;
   140  		width = 1;
   141  		goto number;
   142  	case 'U':
   143  		val = (tm->tm_yday + 7U - tm->tm_wday) / 7;
   144  		goto number;
   145  	case 'W':
   146  		val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7;
   147  		goto number;
   148  	case 'V':
   149  		val = week_num(tm);
   150  		goto number;
   151  	case 'w':
   152  		val = tm->tm_wday;
   153  		width = 1;
   154  		goto number;
   155  	case 'x':
   156  		item = D_FMT;
   157  		goto nl_strftime;
   158  	case 'X':
   159  		item = T_FMT;
   160  		goto nl_strftime;
   161  	case 'y':
   162  		val = (tm->tm_year + 1900LL) % 100;
   163  		if (val < 0) val = -val;
   164  		goto number;
   165  	case 'Y':
   166  		val = tm->tm_year + 1900LL;
   167  		if (val >= 10000) {
   168  			*l = snprintf(*s, sizeof *s, "+%lld", val);
   169  			return *s;
   170  		}
   171  		width = 4;
   172  		goto number;
   173  	case 'z':
   174  		if (tm->tm_isdst < 0) {
   175  			*l = 0;
   176  			return "";
   177  		}
   178  		*l = snprintf(*s, sizeof *s, "%+.4ld",
   179  			tm->__tm_gmtoff/3600*100 + tm->__tm_gmtoff%3600/60);
   180  		return *s;
   181  	case 'Z':
   182  		if (tm->tm_isdst < 0) {
   183  			*l = 0;
   184  			return "";
   185  		}
   186  		fmt = __tm_to_tzname(tm);
   187  		goto string;
   188  	case '%':
   189  		*l = 1;
   190  		return "%";
   191  	default:
   192  		return 0;
   193  	}
   194  number:
   195  	switch (pad ? pad : def_pad) {
   196  	case '-': *l = snprintf(*s, sizeof *s, "%lld", val); break;
   197  	case '_': *l = snprintf(*s, sizeof *s, "%*lld", width, val); break;
   198  	case '0':
   199  	default:  *l = snprintf(*s, sizeof *s, "%0*lld", width, val); break;
   200  	}
   201  	return *s;
   202  nl_strcat:
   203  	fmt = __nl_langinfo_l(item, loc);
   204  string:
   205  	*l = strlen(fmt);
   206  	return fmt;
   207  nl_strftime:
   208  	fmt = __nl_langinfo_l(item, loc);
   209  recu_strftime:
   210  	*l = __strftime_l(*s, sizeof *s, fmt, tm, loc);
   211  	if (!*l) return 0;
   212  	return *s;
   213  }
   214  
   215  size_t __strftime_l(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm, locale_t loc)
   216  {
   217  	size_t l, k;
   218  	char buf[100];
   219  	char *p;
   220  	const char *t;
   221  	int pad, plus;
   222  	unsigned long width;
   223  	for (l=0; l<n; f++) {
   224  		if (!*f) {
   225  			s[l] = 0;
   226  			return l;
   227  		}
   228  		if (*f != '%') {
   229  			s[l++] = *f;
   230  			continue;
   231  		}
   232  		f++;
   233  		pad = 0;
   234  		if (*f == '-' || *f == '_' || *f == '0') pad = *f++;
   235  		if ((plus = (*f == '+'))) f++;
   236  		width = strtoul(f, &p, 10);
   237  		if (*p == 'C' || *p == 'F' || *p == 'G' || *p == 'Y') {
   238  			if (!width && p!=f) width = 1;
   239  		} else {
   240  			width = 0;
   241  		}
   242  		f = p;
   243  		if (*f == 'E' || *f == 'O') f++;
   244  		t = __strftime_fmt_1(&buf, &k, *f, tm, loc, pad);
   245  		if (!t) break;
   246  		if (width) {
   247  			/* Trim off any sign and leading zeros, then
   248  			 * count remaining digits to determine behavior
   249  			 * for the + flag. */
   250  			if (*t=='+' || *t=='-') t++, k--;
   251  			for (; *t=='0' && t[1]-'0'<10U; t++, k--);
   252  			if (width < k) width = k;
   253  			size_t d;
   254  			for (d=0; t[d]-'0'<10U; d++);
   255  			if (tm->tm_year < -1900) {
   256  				s[l++] = '-';
   257  				width--;
   258  			} else if (plus && d+(width-k) >= (*p=='C'?3:5)) {
   259  				s[l++] = '+';
   260  				width--;
   261  			}
   262  			for (; width > k && l < n; width--)
   263  				s[l++] = '0';
   264  		}
   265  		if (k > n-l) k = n-l;
   266  		memcpy(s+l, t, k);
   267  		l += k;
   268  	}
   269  	if (n) {
   270  		if (l==n) l=n-1;
   271  		s[l] = 0;
   272  	}
   273  	return 0;
   274  }
   275  
   276  size_t strftime(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm)
   277  {
   278  	return __strftime_l(s, n, f, tm, CURRENT_LOCALE);
   279  }
   280  
   281  weak_alias(__strftime_l, strftime_l);