a clock already past rolls to its next occurrence (V-544)

At 14:41 "напомни в половине первого пообедать" was set for 12:30 the same
day, two hours gone, and confirmed as "напомню сегодня в 12:30". dateparser
is handed PREFER_DATES_FROM future and does not apply it to an HH:MM time on
today's date. parseClock in the stub has always rolled forward, so the two
parsers disagreed and the production one was the wrong half.

rollPastClockForward runs on the python result. Only a bare clock rolls: a
sentence naming its day keeps it, so a deliberate "сегодня в 12:30" stays
where he put it, and past by a day or more is not a clock resolved onto today.
NamesADay reads weekdays by lemma, the relative day words and the month names,
all from the lexicon.

Measured against real dateparser in a venv: "в половине первого" 05 Aug 12:30
to 06 Aug 12:30, "в 12:30" the same, "сегодня в 12:30" unchanged, and the
relative and named-day cases unchanged.

Left open: a reminder he places in the past is still accepted silently. Saying
the hour has gone is a phrasing gap, not this fix.
This commit is contained in:
2026-08-05 15:14:29 +04:00
parent 1b354e9b39
commit dc3cda666e
3 changed files with 108 additions and 0 deletions
+30
View File
@@ -62,6 +62,36 @@ func isDigitClock(tok string) bool {
return err == nil && mn >= 0 && mn <= 59
}
// NamesADay reports whether the sentence names a calendar day: a weekday, a
// relative day word, or a month beside a date. A bare clock names none of them,
// which is what lets a parser roll it forward to the next occurrence.
//
// "напомни сегодня в 12:30" names the day, so it stays on it even when 12:30 has
// passed. Rolling that one forward would move a reminder he placed deliberately.
func NamesADay(text string) bool {
for _, raw := range strings.Fields(strings.ToLower(text)) {
tok := cleanWord(raw)
if _, ok := lexicon.DayOffset(tok); ok {
return true
}
if isWeekday(tok) || isMonth(tok) {
return true
}
}
return false
}
// isMonth reports whether the token is a month name. The lexicon holds the
// genitive, which is the form a spoken date uses: "10 июля".
func isMonth(tok string) bool {
for m := 1; m <= 12; m++ {
if tok == lexicon.MonthGenitive(m) {
return true
}
}
return false
}
// isWeekday reports whether the token is a day of the week in any case. The
// lexicon lists the nominative, and "в пятницу" is what a reminder says, so the
// match is by lemma — grammar is morph's job, not a second word list.