Index: lib/sdk/crt/time/strftime.c =================================================================== --- lib/sdk/crt/time/strftime.c (revision 57579) +++ lib/sdk/crt/time/strftime.c (working copy) @@ -5,15 +5,544 @@ * PURPOSE: * PROGRAMER: */ + + /* + Needs locale and E/O specifier support +*/ #include #include +typedef struct _ISO8601_TIME +{ + int week; + int year; + int short_year; +} ISO8601_TIME, *PISO8601_TIME; + +typedef enum _DAY_NUMBER +{ + SUNDAY, + MONDAY, + TUESDAY, + WEDNESDAY, + THURSDAY, + FRIDAY, + SATURDAY +} DAY_NUMBER, *PDAY_NUMBER; + +static TCHAR weekdays[7][15] = {_T("Sunday"), _T("Monday"), _T("Tuesday"), _T("Wednesday"), _T("Thursday"), _T("Friday"), _T("Saturday")}; +static TCHAR abb_weekdays[7][4] = {_T("Sun"), _T("Mon"), _T("Tue"), _T("Wed"), _T("Thu"), _T("Fri"), _T("Sat")}; +static TCHAR months[12][15] = {_T("January"), _T("February"), _T("March"), _T("April"), _T("May"), _T("June"), _T("July"), _T("August"), _T("September"), _T("October"), _T("November"), _T("December")}; +static TCHAR abb_months[12][4] = {_T("Jan"), _T("Feb"), _T("Mar"), _T("Apr"), _T("May"), _T("Jun"), _T("July"), _T("Aug"), _T("Sep"), _T("Oct"), _T("Nov"), _T("Dec")}; + size_t _tcsftime(_TCHAR *strDest, size_t maxsize, const _TCHAR *format, const struct tm *timeptr) + { - return 0; + int length; + int yl; + int week; + int utc; + int status; + ISO8601_TIME isotime; + TCHAR *output; + + *strDest = 0; + length = 0; + + if (NULL == strDest || NULL == format || NULL == time) + return 0; + + while (*format) { + if (*format != _T('%') || (_T('%') == *++format)) + { + if (length + 1 >= maxsize) + return 0; + + _stprintf(&strDest[length++], "%c", *format++); + } + else + { + if (_T('E') == *format || _T('O') == *format) + ++format; + switch (*format++) + { + case _T('a'): + if (time->tm_wday < 0 || time->tm_wday > 6) + return 0; + + status = gout(strDest, maxsize - 1 - length, _T("%s"), (void*)abb_weekdays[time->tm_wday]); + if (status < 0) + return 0; + length += status; + break; + case _T('A'): + if (time->tm_wday < 0 || time->tm_wday > 6) + return 0; + + status = gout(strDest, maxsize - length, _T("%s"), (void*)weekdays[time->tm_wday]); + if (status < 0) + return 0; + length += status; + break; + case _T('b'): + case _T('h'): + if (time->tm_mon < 0 || time->tm_mon > 11) + return 0; + + status = gout(strDest, maxsize - length, _T("%s"), (void*)abb_months[time->tm_mon]); + if (status < 0) + return 0; + length += status; + break; + case _T('B'): + if (time->tm_mon < 0 || time->tm_mon > 11) + return 0; + + status = gout(strDest, maxsize - length, _T("%s"), (void*)months[time->tm_mon]); + if (status < 0) + return 0; + length += status; + break; + case _T('c'): + status = bout(strDest, maxsize - length, _T("%a %b %e %T %Y"), time); + if (status < 0) + return 0; + length += status; + break; + case _T('C'): + status = gout(strDest, maxsize - length, _T("%.2d"), (void*)(time->tm_year%100)); + if (status < 0) + return 0; + length += status; + break; + case _T('d'): + if (time->tm_mday < 1 || time->tm_mday > 31) + return 0; + + status = gout(strDest, maxsize - length, _T("%.2d"), (void*)time->tm_mday); + if (status < 0) + return 0; + length += status; + break; + case _T('D'): + status = bout(strDest, maxsize - length, _T("%m/%d/%y"), time); + if (status < 0) + return 0; + length += status; + break; + case _T('e'): + if (time->tm_mday < 1 || time->tm_mday > 31) + return 0; + + status = gout(strDest, maxsize - length, _T("%.2d"), (void*)time->tm_mday); + if (status < 0) + return 0; + length += status; + break; + case _T('F'): + status = bout(strDest, maxsize - length, _T("%Y-%m-%d"), time); + if (status < 0) + return 0; + length += status; + break; + case _T('g'): + // ISO8601 short year + if (tm_to_iso8601(*time, &isotime) < 0) + return 0; + + status = gout(strDest, maxsize - length, _T(".2%d"), (void*)isotime.short_year); + if (status < 0) + return 0; + length += status; + break; + case _T('G'): + // ISO8601 year + if (tm_to_iso8601(*time, &isotime) < 0) + return 0; + + status = gout(strDest, maxsize - length, _T("%d"), (void*)isotime.year); + if (status < 0) + return 0; + length += status; + break; + case _T('H'): + if (time->tm_hour < 0 || time->tm_hour > 23) + return 0; + + status = gout(strDest, maxsize - length, _T("%.2d"), (void*)time->tm_hour); + if (status < 0) + return 0; + length += status; + break; + case _T('I'): + if (time->tm_hour < 0 || time->tm_hour > 23) + return 0; + + status = gout(strDest, maxsize - length, _T("%.2d"), (void*)((time->tm_hour%12)+1)); + if (status < 0) + return 0; + length += status; + break; + case _T('j'): + if (time->tm_yday < 0 || time->tm_yday > 365) + return 0; + + status = gout(strDest, maxsize - length, _T("%.3d"), (void*)(time->tm_yday+1)); + if (status < 0) + return 0; + length += status; + break; + case _T('m'): + if (time->tm_mon < 0 || time->tm_mon > 11) + return 0; + + status = gout(strDest, maxsize - length, _T("%.2d"), (void*)(time->tm_mon+1)); + if (status < 0) + return 0; + length += status; + break; + case _T('M'): + if (time->tm_min < 0 || time->tm_min > 59) + return 0; + + status = gout(strDest, maxsize - length, _T("%.2d"), (void*)time->tm_min); + if (status < 0) + return 0; + length += status; + break; + case _T('n'): + _tcscat(strDest, "\n"); + length += 1; + break; + case _T('p'): + if (time->tm_hour < 0 || time->tm_hour > 23) + return 0; + + if (time->tm_hour <= 11) + _tcscat(strDest, _T("AM")); + else + _tcscat(strDest, _T("PM")); + length += 2; + break; + case _T('r'): + status = bout(strDest, maxsize - length, _T("%I:%M:%S %p"), time); + if (status < 0) + return 0; + length += status; + break; + case _T('R'): + status = bout(strDest, maxsize - length, _T("%H:%M"), time); + if (status < 0) + return 0; + length += status; + break; + case _T('S'): + if (time->tm_sec < 0 || time->tm_sec > 60) + return 0; + + status = gout(strDest, maxsize - length, _T("%.2d"), (void*)time->tm_sec); + if (status < 0) + return 0; + length += status; + break; + case _T('t'): + _tcscat(strDest, _T("\t")); + ++length; + break; + case _T('T'): + status = bout(strDest, maxsize - length, _T("%H:%M:%S"), time); + if (status < 0) + return 0; + length += status; + break; + case _T('u'): + if (time->tm_wday < 0 || time->tm_wday > 6) + return 0; + + if (0 == time->tm_wday) + { + _tcscat(strDest, _T("7")); + status = 1; + } + else + status = gout(strDest, maxsize - length, _T("%d"), (void*)(time->tm_wday + 1)); + + if (status < 0) + return 0; + length += status; + break; + case _T('U'): + // First Sunday as first day of week one + if (time->tm_yday < 0 || time->tm_yday > 365) + return 0; + + week = get_week_by_first_day_of_first_week(*time, SUNDAY); + if (week < 0) + return 0; + + status = gout(strDest, maxsize - length, _T("%.2d"), (void*)week); + if (status < 0) + return 0; + length += status; + break; + case _T('V'): + // ISO 8601 week number + if (tm_to_iso8601(*time, &isotime) < 0) + return 0; + + status = gout(strDest, maxsize - length, _T("%.2d"), (void*)isotime.week); + if (status < 0) + return 0; + length += status; + break; + case _T('w'): + if (time->tm_wday < 0 || time->tm_wday > 6) + return 0; + + status = gout(strDest, maxsize - length, _T("%d"), (void*)time->tm_wday); + if (status < 0) + return 0; + length += status; + break; + case _T('W'): + // First Monday as first day of week one + if (time->tm_yday < 0 || time->tm_yday > 365) + return 0; + + week = get_week_by_first_day_of_first_week(*time, MONDAY); + if (week < 0) + return 0; + + status = gout(strDest, maxsize - length, _T("%.2d"), (void*)week); + if (status < 0) + return 0; + length += status; + break; + case _T('x'): + // Locale's app data + status = bout(strDest, maxsize - length, _T("%m/%d/%y"), time); + if (status < 0) + return 0; + length += status; + break; + case _T('X'): + // Locale's app time + status = bout(strDest, maxsize - length, _T("%T"), time); + if (status < 0) + return 0; + length += status; + break; + case _T('y'): + status = gout(strDest, maxsize - length, _T("%.2d"), (void*)(time->tm_year%100)); + if (status < 0) + return 0; + length += status; + break; + case _T('Y'): + if (time->tm_year + 1900 > INT_MAX) + return 0; + + status = gout(strDest, maxsize - length, _T("%d"), (void*)(1900 + time->tm_year)); + if (status < 0) + return 0; + length += status; + break; + case _T('z'): + // Offset from UTC + utc = get_utc(*time); + if (abs(utc) > 1200) + return 0; + + status = gout(strDest, maxsize - length, _T("%.4d"), (void*)utc); + if (status < 0) + return 0; + length += status; + break; + case _T('Z'): + // Time zone name or abb + break; + default: + break; + } + } + } + strDest[length] = 0; + return length; } +static int gout(TCHAR *out, int out_size, TCHAR *fmt, void *amt) +{ + TCHAR *buff; + int status; + + if (NULL == out || NULL == fmt) + return -1; + + buff = malloc(sizeof(TCHAR) * out_size); + if (NULL == buff) + return -1; + + status = _stprintf(buff, fmt, amt); + if (status < 0 || status >= out_size) { + free (buff); + return -1; + } + + _tcscat(out, buff); + free (buff); + return status; +} + +static int bout(TCHAR *out, int out_size, TCHAR *fmt, const struct tm *time) +{ + TCHAR *temp; + int status; + + temp = malloc(sizeof(TCHAR) * out_size); + if (NULL == temp) + return -1; + + status = _tcsftime(temp, out_size, fmt, time); + if (status == 0 || status >= out_size) { + free (temp); + return -1; + } + + _tcscat(out, temp); + free (temp); + return status; +} + +static int first_wday(struct tm time, DAY_NUMBER wday) +{ + int start_wday; + + if (time.tm_wday < 0 || time.tm_wday > 6 || time.tm_mday < 0 || time.tm_mday > 365 || wday < 0 || wday > 6) + return -1; + + start_wday = ((time.tm_wday - (time.tm_mday - 1) % 7) + 7) % 7; + return (wday - start_wday + 7) % 7; +} + +static int days_in_year(int year) +{ + int y; + if (year >= 0) { + y = year + 1900; + if (!(y % 400)) + return 366; + else if (!(y % 100)) + return 365; + else if (!(y % 4)) + return 366; + else + return 365; + } + return -1; +} + +static int tm_to_iso8601(struct tm time, ISO8601_TIME *isotime) +{ + int yl; + int prev_yl; + int first_monday; + int first_friday; + int delta; + + if (time.tm_yday < 0 || time.tm_yday > 365 || + time.tm_wday < 0 || time.tm_wday > 6) + return -1; + + yl = days_in_year(time.tm_year); + if (yl < 0) + return -1; + + first_monday = first_wday(time, MONDAY); + if (first_monday < 0) + return -1; + + if (time.tm_yday < first_monday && first_monday <= 3) + { + /* Last week of previous year + * Determine if the previous year had 53 weeks + * 1) Regular years start and end on the same weekday + * 2) Leap years end on the weekday after the first day + * 3) A year must have a Thursday in the first and last week in order to have 53 weeks + */ + first_friday = first_wday(time, FRIDAY); + if (first_friday < 0) + return -1; + + prev_yl = days_in_year(time.tm_year - 1); + if (prev_yl < 0) + return -1; + + if ((365 == prev_yl && first_friday == 0) || (366 == prev_yl && (first_friday == 0 || first_friday == 6))) + isotime->week = 53; + else + isotime->week = 52; + + isotime->year = time.tm_year - 1 + 1900; + isotime->short_year = isotime->year % 100; + } + else if ((time.tm_wday >= 1 && time.tm_wday <= 3) && + (time.tm_yday >= (yl - 3) + (time.tm_wday - 1))) + { + /* First week of next year + * ----- Cases ----- + * Leap Year => {Monday >= 363, Tuesday >= 364, Wednesday >= 365} + * NonLeap Year => {Monday >= 362, Tuesday >= 363, Wendesday >= 364} + */ + isotime->week = 1; + isotime->year = time.tm_year + 1 + 1900; + isotime->short_year = isotime->year % 100; + } + else + { + delta = time.tm_yday - first_monday; + isotime->week = (delta < 0 ? -1 : delta/7) + (first_monday >= 4 ? 1 : 0) + 1; + isotime->year = time.tm_year + 1900; + isotime->short_year = isotime->year % 100; + } + + return 1; +} + +int get_week_by_first_day_of_first_week(struct tm time, DAY_NUMBER first_day) +{ + int first_given_day; + struct tm last_day_last_year; + + if (time.tm_yday < 0 || time.tm_yday > 365) + return -1; + + first_given_day = first_wday(time, first_day); + if (first_given_day < 0) + return -1; + + if (time.tm_yday < first_given_day && time.tm_yday <= 5) + { + // This day is a part of last year + last_day_last_year.tm_yday = days_in_year(time.tm_year - 1) - 1; + last_day_last_year.tm_wday = time.tm_wday - time.tm_yday % 7 - 1; + last_day_last_year.tm_wday = (last_day_last_year.tm_wday + 7) % 7; + return get_week_by_first_day_of_first_week(last_day_last_year, first_day); + } + else + return (time.tm_yday - first_given_day)/7; +} + +int get_utc(struct tm dst_time) +{ + int hours; + int mins; + + hours = (_timezone / (60*60)); + mins = _timezone / 60 - hours * 60; + + return (hours + (dst_time.tm_isdst ? 1 : 0)) * 100 + mins; +}