Cygwin: strptime: fix am/pm handling

The %p format specifier is handled immediately.  It requires
that tm_hour is already set.  This falls flat in case the am/pm
marker preceeds the time specification.  Locales with am/pm
marker preceeding time spec by default exist (e. g. ko_KR).

Also, the code expects that tm_hour might be set to an invalid
value because the %p specifier is used in conjunction with %H.
But this usage is invalid in itself and now catched as error
condition after commit 343a2a5581 ("Cygwin: strptime: make
sure to fail on invalid input digits").

Change the %H/%I/%p handling according to GLibC, i. e.

- fix tm_hour for pm only if the time value has been specified
  as 12 hour time %I, and

- perform the fixup only after the entire input has been scanned.
  This decouples the fixup from the %p position relativ to %I.

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2024-02-21 19:54:20 +01:00
parent f0ab27c095
commit 4fe5b1d77b
1 changed files with 9 additions and 3 deletions

View File

@ -335,6 +335,8 @@ __strptime(const char *buf, const char *fmt, struct tm *tm,
const char *new_fmt; const char *new_fmt;
uint ulim; uint ulim;
int ymd = 0; int ymd = 0;
bool got_I = false;
bool got_pm = false;
bp = (const unsigned char *)buf; bp = (const unsigned char *)buf;
const struct lc_time_T *_CurrentTimeLocale = __get_time_locale (locale); const struct lc_time_T *_CurrentTimeLocale = __get_time_locale (locale);
@ -537,6 +539,7 @@ literal:
case 'H': case 'H':
LEGAL_ALT(ALT_O); LEGAL_ALT(ALT_O);
bp = conv_num(bp, &tm->tm_hour, 0, 23, ALT_DIGITS); bp = conv_num(bp, &tm->tm_hour, 0, 23, ALT_DIGITS);
got_I = false;
continue; continue;
case 'l': /* The hour (12-hour clock representation). */ case 'l': /* The hour (12-hour clock representation). */
@ -547,6 +550,7 @@ literal:
bp = conv_num(bp, &tm->tm_hour, 1, 12, ALT_DIGITS); bp = conv_num(bp, &tm->tm_hour, 1, 12, ALT_DIGITS);
if (tm->tm_hour == 12) if (tm->tm_hour == 12)
tm->tm_hour = 0; tm->tm_hour = 0;
got_I = true;
continue; continue;
case 'j': /* The day of year. */ case 'j': /* The day of year. */
@ -573,9 +577,7 @@ literal:
case 'p': /* The locale's equivalent of AM/PM. */ case 'p': /* The locale's equivalent of AM/PM. */
bp = find_string(bp, &i, _ctloc(am_pm), NULL, 2, bp = find_string(bp, &i, _ctloc(am_pm), NULL, 2,
locale); locale);
if (tm->tm_hour > 11) got_pm = (i == 1);
return NULL;
tm->tm_hour += i * 12;
LEGAL_ALT(0); LEGAL_ALT(0);
continue; continue;
@ -751,8 +753,12 @@ literal:
default: /* Unknown/unsupported conversion. */ default: /* Unknown/unsupported conversion. */
return NULL; return NULL;
} }
} }
if (got_I && got_pm)
tm->tm_hour += 12;
if (bp && (era || got_eoff)) if (bp && (era || got_eoff))
{ {
/* Default to current era. */ /* Default to current era. */