1570af302Sopenharmony_ci/** 2570af302Sopenharmony_ci * Copyright (c) 2022 Huawei Device Co., Ltd. 3570af302Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License"); 4570af302Sopenharmony_ci * you may not use this file except in compliance with the License. 5570af302Sopenharmony_ci * You may obtain a copy of the License at 6570af302Sopenharmony_ci * 7570af302Sopenharmony_ci * http://www.apache.org/licenses/LICENSE-2.0 8570af302Sopenharmony_ci * 9570af302Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software 10570af302Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS, 11570af302Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12570af302Sopenharmony_ci * See the License for the specific language governing permissions and 13570af302Sopenharmony_ci * limitations under the License. 14570af302Sopenharmony_ci */ 15570af302Sopenharmony_ci 16570af302Sopenharmony_ci#include <stdio.h> 17570af302Sopenharmony_ci#include <stdlib.h> 18570af302Sopenharmony_ci#include <string.h> 19570af302Sopenharmony_ci#include <langinfo.h> 20570af302Sopenharmony_ci#include <locale.h> 21570af302Sopenharmony_ci#include <time.h> 22570af302Sopenharmony_ci#include <limits.h> 23570af302Sopenharmony_ci#include "locale_impl.h" 24570af302Sopenharmony_ci#include "time_impl.h" 25570af302Sopenharmony_ci 26570af302Sopenharmony_ci#define __YEAR_BASE__ 1900 27570af302Sopenharmony_ci#define __WEEKS_IN_YEAR__ 52 28570af302Sopenharmony_ci#define __WEEKS_IN_YEAR2__ 53 29570af302Sopenharmony_ci#define __DAY_LAST_WEEK__ 4 30570af302Sopenharmony_ci#define __DAY_LAST_WEEK2__ 3 31570af302Sopenharmony_ci#define __YEARS_PER_CENTURY__ 100 32570af302Sopenharmony_ci#define __DAYS_IN_YEAR__ 360 33570af302Sopenharmony_ci#define __LEN_YEAR__ 4 34570af302Sopenharmony_ci#define __HALF_HOUR__ 12 35570af302Sopenharmony_ci#define __LEN_DAY__ 3 36570af302Sopenharmony_ci 37570af302Sopenharmony_cistatic int is_leap(int y) 38570af302Sopenharmony_ci{ 39570af302Sopenharmony_ci /* Avoid overflow */ 40570af302Sopenharmony_ci if (y > INT_MAX - __YEAR_BASE__) { 41570af302Sopenharmony_ci y -= 2000; 42570af302Sopenharmony_ci } 43570af302Sopenharmony_ci y += __YEAR_BASE__; 44570af302Sopenharmony_ci return !(y%4) && ((y%100) || !(y%400)); 45570af302Sopenharmony_ci} 46570af302Sopenharmony_ci 47570af302Sopenharmony_cistatic int week_num(const struct tm *tm) 48570af302Sopenharmony_ci{ 49570af302Sopenharmony_ci int val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7; 50570af302Sopenharmony_ci /* If 1 Jan is just 1-3 days past Monday, 51570af302Sopenharmony_ci * the previous week is also in this year. */ 52570af302Sopenharmony_ci if ((tm->tm_wday + 371U - tm->tm_yday - 2) % 7 <= 2) 53570af302Sopenharmony_ci val++; 54570af302Sopenharmony_ci if (!val) { 55570af302Sopenharmony_ci val = __WEEKS_IN_YEAR__; 56570af302Sopenharmony_ci /* If 31 December of prev year a Thursday, 57570af302Sopenharmony_ci * or Friday of a leap year, then the 58570af302Sopenharmony_ci * prev year has 53 weeks. */ 59570af302Sopenharmony_ci int dec31 = (tm->tm_wday + 7U - tm->tm_yday - 1) % 7; 60570af302Sopenharmony_ci if (dec31 == 4 || (dec31 == 5 && is_leap(tm->tm_year%400-1))) 61570af302Sopenharmony_ci val++; 62570af302Sopenharmony_ci } else if (val == __WEEKS_IN_YEAR2__) { 63570af302Sopenharmony_ci /* If 1 January is not a Thursday, and not 64570af302Sopenharmony_ci * a Wednesday of a leap year, then this 65570af302Sopenharmony_ci * year has only 52 weeks. */ 66570af302Sopenharmony_ci int jan1 = (tm->tm_wday + 371U - tm->tm_yday) % 7; 67570af302Sopenharmony_ci if (jan1 != __DAY_LAST_WEEK__ && (jan1 != 3 || !is_leap(tm->tm_year))) 68570af302Sopenharmony_ci val = 1; 69570af302Sopenharmony_ci } 70570af302Sopenharmony_ci return val; 71570af302Sopenharmony_ci} 72570af302Sopenharmony_ci 73570af302Sopenharmony_ciconst char *__strftime_fmt_1(char (*s)[100], size_t *l, int f, const struct tm *tm, locale_t loc, int pad) 74570af302Sopenharmony_ci{ 75570af302Sopenharmony_ci nl_item item; 76570af302Sopenharmony_ci long long val; 77570af302Sopenharmony_ci const char *fmt = "-"; 78570af302Sopenharmony_ci int width = 2, def_pad = '0'; 79570af302Sopenharmony_ci 80570af302Sopenharmony_ci switch (f) { 81570af302Sopenharmony_ci case 'a': 82570af302Sopenharmony_ci if (tm->tm_wday > 6U) goto string; 83570af302Sopenharmony_ci item = ABDAY_1 + tm->tm_wday; 84570af302Sopenharmony_ci goto nl_strcat; 85570af302Sopenharmony_ci case 'A': 86570af302Sopenharmony_ci if (tm->tm_wday > 6U) goto string; 87570af302Sopenharmony_ci item = DAY_1 + tm->tm_wday; 88570af302Sopenharmony_ci goto nl_strcat; 89570af302Sopenharmony_ci case 'h': 90570af302Sopenharmony_ci case 'b': 91570af302Sopenharmony_ci if (tm->tm_mon > 11U) { 92570af302Sopenharmony_ci goto string; 93570af302Sopenharmony_ci } 94570af302Sopenharmony_ci item = ABMON_1 + tm->tm_mon; 95570af302Sopenharmony_ci goto nl_strcat; 96570af302Sopenharmony_ci case 'B': 97570af302Sopenharmony_ci if (tm->tm_mon > 11U) { 98570af302Sopenharmony_ci goto string; 99570af302Sopenharmony_ci } 100570af302Sopenharmony_ci item = MON_1 + tm->tm_mon; 101570af302Sopenharmony_ci goto nl_strcat; 102570af302Sopenharmony_ci case 'c': 103570af302Sopenharmony_ci item = D_T_FMT; 104570af302Sopenharmony_ci goto nl_strftime; 105570af302Sopenharmony_ci case 'C': 106570af302Sopenharmony_ci val = (1900LL+tm->tm_year) / __YEARS_PER_CENTURY__; 107570af302Sopenharmony_ci goto number; 108570af302Sopenharmony_ci case 'e': 109570af302Sopenharmony_ci def_pad = '_'; 110570af302Sopenharmony_ci case 'd': 111570af302Sopenharmony_ci val = tm->tm_mday; 112570af302Sopenharmony_ci goto number; 113570af302Sopenharmony_ci case 'D': 114570af302Sopenharmony_ci fmt = "%m/%d/%y"; 115570af302Sopenharmony_ci goto recu_strftime; 116570af302Sopenharmony_ci case 'F': 117570af302Sopenharmony_ci fmt = "%Y-%m-%d"; 118570af302Sopenharmony_ci goto recu_strftime; 119570af302Sopenharmony_ci case 'g': 120570af302Sopenharmony_ci case 'G': 121570af302Sopenharmony_ci val = tm->tm_year + 1900LL; 122570af302Sopenharmony_ci if (tm->tm_yday < __DAY_LAST_WEEK2__ && week_num(tm) != 1) { 123570af302Sopenharmony_ci val--; 124570af302Sopenharmony_ci } else if (tm->tm_yday > __DAYS_IN_YEAR__ && week_num(tm) == 1) { 125570af302Sopenharmony_ci val++; 126570af302Sopenharmony_ci } 127570af302Sopenharmony_ci if (f=='g') { 128570af302Sopenharmony_ci val %= __YEARS_PER_CENTURY__; 129570af302Sopenharmony_ci } else { 130570af302Sopenharmony_ci width = __LEN_YEAR__; 131570af302Sopenharmony_ci } 132570af302Sopenharmony_ci goto number; 133570af302Sopenharmony_ci case 'H': 134570af302Sopenharmony_ci val = tm->tm_hour; 135570af302Sopenharmony_ci goto number; 136570af302Sopenharmony_ci case 'I': 137570af302Sopenharmony_ci val = tm->tm_hour; 138570af302Sopenharmony_ci if (!val) { 139570af302Sopenharmony_ci val = __HALF_HOUR__; 140570af302Sopenharmony_ci } else if (val > __HALF_HOUR__) { 141570af302Sopenharmony_ci val -= __HALF_HOUR__; 142570af302Sopenharmony_ci } 143570af302Sopenharmony_ci goto number; 144570af302Sopenharmony_ci case 'j': 145570af302Sopenharmony_ci val = tm->tm_yday+1; 146570af302Sopenharmony_ci width = __LEN_DAY__; 147570af302Sopenharmony_ci goto number; 148570af302Sopenharmony_ci case 'k': 149570af302Sopenharmony_ci def_pad = '_'; 150570af302Sopenharmony_ci val = tm->tm_hour; 151570af302Sopenharmony_ci goto number; 152570af302Sopenharmony_ci case 'l': 153570af302Sopenharmony_ci def_pad = '_'; 154570af302Sopenharmony_ci val = tm->tm_hour; 155570af302Sopenharmony_ci if (!val) { 156570af302Sopenharmony_ci val = __HALF_HOUR__; 157570af302Sopenharmony_ci } else if (val > __HALF_HOUR__) { 158570af302Sopenharmony_ci val -= __HALF_HOUR__; 159570af302Sopenharmony_ci } 160570af302Sopenharmony_ci goto number; 161570af302Sopenharmony_ci case 'm': 162570af302Sopenharmony_ci val = tm->tm_mon+1; 163570af302Sopenharmony_ci goto number; 164570af302Sopenharmony_ci case 'M': 165570af302Sopenharmony_ci val = tm->tm_min; 166570af302Sopenharmony_ci goto number; 167570af302Sopenharmony_ci case 'n': 168570af302Sopenharmony_ci *l = 1; 169570af302Sopenharmony_ci return "\n"; 170570af302Sopenharmony_ci case 'p': 171570af302Sopenharmony_ci item = tm->tm_hour >= __HALF_HOUR__ ? PM_STR : AM_STR; 172570af302Sopenharmony_ci goto nl_strcat; 173570af302Sopenharmony_ci case 'P': 174570af302Sopenharmony_ci item = tm->tm_hour >= __HALF_HOUR__ ? PM_STR_LOWER : AM_STR_LOWER; 175570af302Sopenharmony_ci goto nl_strcat; 176570af302Sopenharmony_ci case 'r': 177570af302Sopenharmony_ci item = T_FMT_AMPM; 178570af302Sopenharmony_ci goto nl_strftime; 179570af302Sopenharmony_ci case 'R': 180570af302Sopenharmony_ci fmt = "%H:%M"; 181570af302Sopenharmony_ci goto recu_strftime; 182570af302Sopenharmony_ci case 's': 183570af302Sopenharmony_ci val = __tm_to_secs(tm) - tm->__tm_gmtoff; 184570af302Sopenharmony_ci width = 1; 185570af302Sopenharmony_ci goto number; 186570af302Sopenharmony_ci case 'S': 187570af302Sopenharmony_ci val = tm->tm_sec; 188570af302Sopenharmony_ci goto number; 189570af302Sopenharmony_ci case 't': 190570af302Sopenharmony_ci *l = 1; 191570af302Sopenharmony_ci return "\t"; 192570af302Sopenharmony_ci case 'T': 193570af302Sopenharmony_ci fmt = "%H:%M:%S"; 194570af302Sopenharmony_ci goto recu_strftime; 195570af302Sopenharmony_ci case 'u': 196570af302Sopenharmony_ci val = tm->tm_wday ? tm->tm_wday : 7; 197570af302Sopenharmony_ci width = 1; 198570af302Sopenharmony_ci goto number; 199570af302Sopenharmony_ci case 'U': 200570af302Sopenharmony_ci val = (tm->tm_yday + 7U - tm->tm_wday) / 7; 201570af302Sopenharmony_ci goto number; 202570af302Sopenharmony_ci case 'W': 203570af302Sopenharmony_ci val = (tm->tm_yday + 7U - (tm->tm_wday+6U)%7) / 7; 204570af302Sopenharmony_ci goto number; 205570af302Sopenharmony_ci case 'V': 206570af302Sopenharmony_ci val = week_num(tm); 207570af302Sopenharmony_ci goto number; 208570af302Sopenharmony_ci case 'v': 209570af302Sopenharmony_ci fmt = "%e-%b-%Y"; 210570af302Sopenharmony_ci goto recu_strftime; 211570af302Sopenharmony_ci case 'w': 212570af302Sopenharmony_ci val = tm->tm_wday; 213570af302Sopenharmony_ci width = 1; 214570af302Sopenharmony_ci goto number; 215570af302Sopenharmony_ci case 'x': 216570af302Sopenharmony_ci item = D_FMT; 217570af302Sopenharmony_ci goto nl_strftime; 218570af302Sopenharmony_ci case 'X': 219570af302Sopenharmony_ci item = T_FMT; 220570af302Sopenharmony_ci goto nl_strftime; 221570af302Sopenharmony_ci case 'y': 222570af302Sopenharmony_ci val = (tm->tm_year + 1900LL) % 100; 223570af302Sopenharmony_ci if (val < 0) { 224570af302Sopenharmony_ci val = -val; 225570af302Sopenharmony_ci } 226570af302Sopenharmony_ci goto number; 227570af302Sopenharmony_ci case 'Y': 228570af302Sopenharmony_ci val = tm->tm_year + 1900LL; 229570af302Sopenharmony_ci if (val >= 10000) { 230570af302Sopenharmony_ci *l = snprintf(*s, sizeof *s, "+%lld", val); 231570af302Sopenharmony_ci return *s; 232570af302Sopenharmony_ci } 233570af302Sopenharmony_ci width = __LEN_YEAR__; 234570af302Sopenharmony_ci goto number; 235570af302Sopenharmony_ci case 'z': 236570af302Sopenharmony_ci if (tm->tm_isdst < 0) { 237570af302Sopenharmony_ci *l = 0; 238570af302Sopenharmony_ci return ""; 239570af302Sopenharmony_ci } 240570af302Sopenharmony_ci *l = snprintf(*s, sizeof *s, "%+.4ld", 241570af302Sopenharmony_ci tm->__tm_gmtoff/3600*100 + tm->__tm_gmtoff%3600/60); 242570af302Sopenharmony_ci return *s; 243570af302Sopenharmony_ci case 'Z': 244570af302Sopenharmony_ci if (tm->tm_isdst < 0) { 245570af302Sopenharmony_ci *l = 0; 246570af302Sopenharmony_ci return ""; 247570af302Sopenharmony_ci } 248570af302Sopenharmony_ci fmt = __tm_to_tzname(tm); 249570af302Sopenharmony_ci goto string; 250570af302Sopenharmony_ci case '%': 251570af302Sopenharmony_ci *l = 1; 252570af302Sopenharmony_ci return "%"; 253570af302Sopenharmony_ci default: 254570af302Sopenharmony_ci return 0; 255570af302Sopenharmony_ci } 256570af302Sopenharmony_cinumber: 257570af302Sopenharmony_ci switch (pad ? pad : def_pad) { 258570af302Sopenharmony_ci case '-': 259570af302Sopenharmony_ci *l = snprintf(*s, sizeof *s, "%lld", val); 260570af302Sopenharmony_ci break; 261570af302Sopenharmony_ci case '_': 262570af302Sopenharmony_ci *l = snprintf(*s, sizeof *s, "%*lld", width, val); 263570af302Sopenharmony_ci break; 264570af302Sopenharmony_ci case '0': 265570af302Sopenharmony_ci default: 266570af302Sopenharmony_ci *l = snprintf(*s, sizeof *s, "%0*lld", width, val); 267570af302Sopenharmony_ci break; 268570af302Sopenharmony_ci } 269570af302Sopenharmony_ci return *s; 270570af302Sopenharmony_cinl_strcat: 271570af302Sopenharmony_ci fmt = __nl_langinfo_l(item, loc); 272570af302Sopenharmony_cistring: 273570af302Sopenharmony_ci *l = strlen(fmt); 274570af302Sopenharmony_ci return fmt; 275570af302Sopenharmony_cinl_strftime: 276570af302Sopenharmony_ci fmt = __nl_langinfo_l(item, loc); 277570af302Sopenharmony_cirecu_strftime: 278570af302Sopenharmony_ci *l = __strftime_l(*s, sizeof *s, fmt, tm, loc); 279570af302Sopenharmony_ci if (!*l) { 280570af302Sopenharmony_ci return 0; 281570af302Sopenharmony_ci } 282570af302Sopenharmony_ci return *s; 283570af302Sopenharmony_ci} 284570af302Sopenharmony_ci 285570af302Sopenharmony_cisize_t __strftime_l(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm, locale_t loc) 286570af302Sopenharmony_ci{ 287570af302Sopenharmony_ci size_t l, k; 288570af302Sopenharmony_ci char buf[100]; 289570af302Sopenharmony_ci char *p; 290570af302Sopenharmony_ci const char *t; 291570af302Sopenharmony_ci int pad, plus; 292570af302Sopenharmony_ci unsigned long width; 293570af302Sopenharmony_ci for (l=0; l<n; f++) { 294570af302Sopenharmony_ci if (!*f) { 295570af302Sopenharmony_ci s[l] = 0; 296570af302Sopenharmony_ci return l; 297570af302Sopenharmony_ci } 298570af302Sopenharmony_ci if (*f != '%') { 299570af302Sopenharmony_ci s[l++] = *f; 300570af302Sopenharmony_ci continue; 301570af302Sopenharmony_ci } 302570af302Sopenharmony_ci f++; 303570af302Sopenharmony_ci pad = 0; 304570af302Sopenharmony_ci if (*f == '-' || *f == '_' || *f == '0') { 305570af302Sopenharmony_ci pad = *f++; 306570af302Sopenharmony_ci } 307570af302Sopenharmony_ci if ((plus = (*f == '+'))) { 308570af302Sopenharmony_ci f++; 309570af302Sopenharmony_ci } 310570af302Sopenharmony_ci width = strtoul(f, &p, 10); 311570af302Sopenharmony_ci if (*p == 'C' || *p == 'F' || *p == 'G' || *p == 'Y') { 312570af302Sopenharmony_ci if (!width && p!=f) { 313570af302Sopenharmony_ci width = 1; 314570af302Sopenharmony_ci } 315570af302Sopenharmony_ci } else { 316570af302Sopenharmony_ci width = 0; 317570af302Sopenharmony_ci } 318570af302Sopenharmony_ci f = p; 319570af302Sopenharmony_ci if (*f == 'E' || *f == 'O') { 320570af302Sopenharmony_ci f++; 321570af302Sopenharmony_ci } 322570af302Sopenharmony_ci t = __strftime_fmt_1(&buf, &k, *f, tm, loc, pad); 323570af302Sopenharmony_ci if (!t) { 324570af302Sopenharmony_ci break; 325570af302Sopenharmony_ci } 326570af302Sopenharmony_ci if (width) { 327570af302Sopenharmony_ci /* Trim off any sign and leading zeros, then 328570af302Sopenharmony_ci * count remaining digits to determine behavior 329570af302Sopenharmony_ci * for the + flag. */ 330570af302Sopenharmony_ci if (*t=='+' || *t=='-') { 331570af302Sopenharmony_ci t++, k--; 332570af302Sopenharmony_ci } 333570af302Sopenharmony_ci for (; *t=='0' && t[1]-'0'<10U; t++, k--) {} 334570af302Sopenharmony_ci if (width < k) { 335570af302Sopenharmony_ci width = k; 336570af302Sopenharmony_ci } 337570af302Sopenharmony_ci size_t d; 338570af302Sopenharmony_ci for (d=0; t[d]-'0'<10U; d++) {} 339570af302Sopenharmony_ci if (tm->tm_year < -1900) { 340570af302Sopenharmony_ci s[l++] = '-'; 341570af302Sopenharmony_ci width--; 342570af302Sopenharmony_ci } else if (plus && d+(width-k) >= (*p=='C'?3:5)) { 343570af302Sopenharmony_ci s[l++] = '+'; 344570af302Sopenharmony_ci width--; 345570af302Sopenharmony_ci } 346570af302Sopenharmony_ci for (; width > k && l < n; width--) { 347570af302Sopenharmony_ci s[l++] = '0'; 348570af302Sopenharmony_ci } 349570af302Sopenharmony_ci } 350570af302Sopenharmony_ci if (k > n-l) { 351570af302Sopenharmony_ci k = n-l; 352570af302Sopenharmony_ci } 353570af302Sopenharmony_ci memcpy(s+l, t, k); 354570af302Sopenharmony_ci l += k; 355570af302Sopenharmony_ci } 356570af302Sopenharmony_ci if (n) { 357570af302Sopenharmony_ci if (l==n) { 358570af302Sopenharmony_ci l=n-1; 359570af302Sopenharmony_ci } 360570af302Sopenharmony_ci s[l] = 0; 361570af302Sopenharmony_ci } 362570af302Sopenharmony_ci return 0; 363570af302Sopenharmony_ci} 364570af302Sopenharmony_ci 365570af302Sopenharmony_cisize_t strftime(char *restrict s, size_t n, const char *restrict f, const struct tm *restrict tm) 366570af302Sopenharmony_ci{ 367570af302Sopenharmony_ci return __strftime_l(s, n, f, tm, CURRENT_LOCALE); 368570af302Sopenharmony_ci} 369570af302Sopenharmony_ci 370570af302Sopenharmony_ciweak_alias(__strftime_l, strftime_l);