Skip to content
Merged
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
70 changes: 46 additions & 24 deletions dateparser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -649,31 +649,53 @@ def parse_number(token, skip_component=None):
k: self.num_directives[k] for k in ("month", "day", "year")
}

for component, directives in num_directives.items():
if skip_component == component:
continue
for directive in directives:
try:
do = self._get_date_obj(token, directive)
prev_value = getattr(self, component, None)
if not prev_value:
return set_and_return(token, type, component, do)
else:
try:
prev_token, prev_type = getattr(
self, "_token_%s" % component
)
if prev_type == type:
do = self._get_date_obj(prev_token, directive)
except ValueError:
self.unset_tokens.append(
(prev_token, prev_type, component)
)
def try_directives(skip_directive=None):
for component, directives in num_directives.items():
if skip_component == component:
continue
for directive in directives:
if directive == skip_directive:
continue
try:
do = self._get_date_obj(token, directive)
prev_value = getattr(self, component, None)
if not prev_value:
return set_and_return(token, type, component, do)
except ValueError:
pass
else:
raise ValueError("Unable to parse: %s" % token)
else:
try:
prev_token, prev_type = getattr(
self, "_token_%s" % component
)
if prev_type == type:
do = self._get_date_obj(prev_token, directive)
except ValueError:
self.unset_tokens.append(
(prev_token, prev_type, component)
)
return set_and_return(token, type, component, do)
except ValueError:
pass
else:
raise ValueError("Unable to parse: %s" % token)

order = list(self.ordered_num_directives)
components_after_year = order[order.index("year") + 1 :]
year_position_already_passed = any(
getattr(self, component) is not None
for component in components_after_year
)
if year_position_already_passed:
# A component that the date order places after the year has
# already been found, so the year position in the date string
# has already been passed and this number cannot be a
# two-digit year: in "4月20日" ("April 20"), 20 is the day,
# not the year 2020 (#519). Read it as a two-digit year only
# if it cannot be anything else (e.g. 99 in "4-99").
try:
return try_directives(skip_directive="%y")
except ValueError:
pass
return try_directives()

def parse_alpha(token, skip_component=None):
type = 1
Expand Down
51 changes: 51 additions & 0 deletions tests/test_date_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -1186,6 +1186,57 @@ def test_order(self, date_string, expected=None, order=None):
self.then_date_was_parsed_by_date_parser()
self.then_date_obj_exactly_is(expected)

@parameterized.expand(
[
# Japanese dates without a year: the day must not be read as
# a two-digit year just because the year comes first (#519).
param(
"4月20日 18:10",
expected=datetime(2019, 4, 20, 18, 10),
languages=["ja"],
),
param(
"3月8日",
expected=datetime(2019, 3, 8, 0, 0),
languages=["ja"],
),
# The root cause is locale-independent: any year-first date
# order must not consume the day as a year either.
param(
"4-20 18:10",
expected=datetime(2019, 4, 20, 18, 10),
settings={"DATE_ORDER": "YMD"},
),
# A number that cannot be a day is still parsed as a year.
param(
"4-99",
expected=datetime(1999, 4, 27, 0, 0),
settings={"DATE_ORDER": "YMD"},
),
# Fully specified dates keep their meaning.
param(
"2020年4月20日 18:10",
expected=datetime(2020, 4, 20, 18, 10),
languages=["ja"],
),
param(
"19年4月20日",
expected=datetime(2019, 4, 20, 0, 0),
languages=["ja"],
),
]
)
def test_two_digit_day_is_not_confused_with_year(
self, date_string, expected=None, languages=None, settings=None
):
self.given_parser(
languages=languages,
settings={"RELATIVE_BASE": datetime(2019, 6, 27), **(settings or {})},
)
self.when_date_is_parsed(date_string)
self.then_date_was_parsed_by_date_parser()
self.then_date_obj_exactly_is(expected)

@parameterized.expand(
[
param("201508", expected=datetime(2015, 8, 20, 0, 0), order="DYM"),
Expand Down
Loading