Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions Lib/_pydatetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ def _parse_hh_mm_ss_ff(tstr):

time_comps = [0, 0, 0, 0]
pos = 0
comp = None
for comp in range(0, 3):
if (len_str - pos) < 2:
raise ValueError("Incomplete time component")
Expand All @@ -392,6 +393,9 @@ def _parse_hh_mm_ss_ff(tstr):
if not next_char or comp >= 2:
break

if next_char == '.' or next_char == ',':
break

if has_sep and next_char != ':':
raise ValueError("Invalid time separator: %c" % next_char)

Expand All @@ -410,9 +414,14 @@ def _parse_hh_mm_ss_ff(tstr):
else:
to_parse = len_remainder

time_comps[3] = int(tstr[pos:(pos+to_parse)])
if to_parse < 6:
time_comps[3] *= _FRACTION_CORRECTION[to_parse-1]
extra_time = float("." + tstr[pos:(pos+to_parse)])
while comp < 2:
element = "hour" if comp == 0 else "minute"
raise ValueError(f"Fractional {element} not supported")
if comp == 2:
time_comps[comp + 1] = int(extra_time * 1e6)

# parse the rest of characters
if (len_remainder > to_parse
and not all(map(_is_ascii_digit, tstr[(pos+to_parse):]))):
raise ValueError("Non-digit values in unparsed fraction")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ISO8601 Format recommends "a decimal fraction may be added to the lowest order time element present."
Thus "T0314.15" should be valid and yield "03:14:09", but currently yields "03:14:00.150000". Due to deliberate choice, such an expression would now throw a ValueError because such a string is more likely a typo than an actual fractional value.
30 changes: 21 additions & 9 deletions Modules/_datetimemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -831,9 +831,11 @@ parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end, int *hour,
int *vals[3] = {hour, minute, second};
// This is initialized to satisfy an erroneous compiler warning.
unsigned char has_separator = 1;
unsigned char has_decimal = 0;

// Parse [HH[:?MM[:?SS]]]
for (size_t i = 0; i < 3; ++i) {
size_t i = 0;
for (; i < 3; ++i) {
p = parse_digits(p, vals[i], 2);
if (NULL == p) {
return -3;
Expand All @@ -844,42 +846,52 @@ parse_hh_mm_ss_ff(const char *tstr, const char *tstr_end, int *hour,
has_separator = (c == ':');
}

if (p >= p_end) {
if (p >= p_end) { // reached timezone info
return c != '\0';
}
else if (has_separator && (c == ':')) {
continue;
}
else if (c == '.' || c == ',') {
has_decimal = 1;
break;
} else if (!has_separator) {
}
else if (!has_separator) {
--p;
} else {
return -4; // Malformed time separator
}
}

// expecting to get exhausted unless
if (!has_decimal)
return -3;
// Parse fractional components
size_t len_remains = p_end - p;
size_t to_parse = len_remains;
if (len_remains >= 6) {
to_parse = 6;
}

p = parse_digits(p, microsecond, to_parse);
int extra_time_digits = 0;
p = parse_digits(p, &extra_time_digits, to_parse);
if (NULL == p) {
return -3;
}

static int correction[] = {
100000, 10000, 1000, 100, 10
int dividing_factor[] = {
10, 100, 1000, 10000, 100000, 1000000
};

if (to_parse < 6) {
*microsecond *= correction[to_parse-1];
float extra_time = extra_time_digits / (dividing_factor[to_parse - 1] * 1.);
while (i < 2) {
return -3;
}
if (i == 2) {
*microsecond = (extra_time * dividing_factor[5]);
}

while (is_digit(*p)){
while (is_digit(*p) && p < p_end) {
++p; // skip truncated digits
}

Expand Down