Skip to content

Split date-time runtime fixes from locale PR#185

Merged
JarbasAl merged 5 commits into
devfrom
feat/datetime-runtime-fixes
May 10, 2026
Merged

Split date-time runtime fixes from locale PR#185
JarbasAl merged 5 commits into
devfrom
feat/datetime-runtime-fixes

Conversation

@goldyfruit
Copy link
Copy Markdown
Collaborator

@goldyfruit goldyfruit commented Mar 9, 2026

Summary

Summary by CodeRabbit

  • New Features

    • Locale-aware, case-insensitive location phrase handling and improved punctuation stripping for more robust location recognition.
    • Better weekend interpretation using natural-language cues to determine "upcoming weekend" behavior.
  • Bug Fixes

    • Time responses now gracefully return no result when a valid time cannot be determined.
    • Ambiguous location names no longer produce incorrect timezone resolutions; GUI time display is guarded when time is unavailable.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 9, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6d70546b-5ea9-45e3-b671-5eb1d4f8c397

📥 Commits

Reviewing files that changed from the base of the PR and between cd5061a and 2076bb9.

📒 Files selected for processing (1)
  • test/unittests/test_skill_loading.py
✅ Files skipped from review due to trivial changes (1)
  • test/unittests/test_skill_loading.py

📝 Walkthrough

Walkthrough

Adds locale-aware phrase normalization and cached phrase sets, improves case-insensitive location extraction and preprocessing, treats locale-listed ambiguous locations as unresolved for timezone resolution, updates time methods to return Optional[str] and accept anchor_date, and refactors weekend calculation using weekday arithmetic and locale cues.

Changes

Cohort / File(s) Summary
Core TimeSkill changes
__init__.py
Added _normalize_phrase, _load_locale_phrase_set, _is_ambiguous_location, _sanitize_location, _resolve_location, and _mentions_current_weekend. Updated location extraction to be case-insensitive and strip punctuation. get_spoken_time/get_display_time now return Optional[str]. speak_time accepts anchor_date. Weekend calculation replaced with weekday arithmetic and locale cue handling.
Tests — loader import updates
test/unittests/test_skill_loading.py
Updated imports to use ovos_workshop.skill_launcher (PluginSkillLoader, SkillLoader) and ovos_skill_date_time.TimeSkill instead of previous module paths; test logic otherwise unchanged.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor User as "User (utterance)"
    participant Skill as "TimeSkill"
    participant Locale as "Locale resources"
    participant TZ as "Timezone resolver"

    User->>Skill: speak/query (utterance, optional location slot)
    Skill->>Locale: _load_locale_phrase_set / _normalize_phrase
    Locale-->>Skill: phrase sets (cached)
    Skill->>Skill: _resolve_location(utterance, slot)
    alt location ambiguous per locale
        Skill->>Skill: _is_ambiguous_location -> return None
        Skill-->>User: respond with unresolved location / fallback
    else location resolved
        Skill->>TZ: get_timezone_in_location(location)
        TZ-->>Skill: timezone or None
        Skill->>Skill: compute datetime (anchor_date if provided)
        Skill-->>User: spoken/display time (or None)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through phrases, tidy and neat,
Normalized words on tiny feet,
I nibbled ambiguities down to a thread,
Counted weekends by weekday instead,
Now time answers hop home — soft and sweet.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change—splitting runtime fixes from a locale-focused PR to isolate code changes. It is specific and relates directly to the changeset's primary objective.
Docstring Coverage ✅ Passed Docstring coverage is 94.74% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/datetime-runtime-fixes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@goldyfruit
Copy link
Copy Markdown
Collaborator Author

Refactored the locale-specific exceptions out of __init__.py.

This keeps the runtime fix generic and moves the data into optional per-locale .list resources:

  • ambiguous_locations.list
  • non_location_phrases.list
  • current_weekend_phrases.list

I added en-us and fr-fr coverage for the timezone/location cases, plus tr-tr for the current-weekend wording that already exists in that locale.

@goldyfruit
Copy link
Copy Markdown
Collaborator Author

Split the locale resources into #186 so this PR stays code-only.\n\nThe locale data for ambiguous locations, non-location phrases, and current-weekend wording now lives there.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
__init__.py (3)

416-429: Use explicit Optional type hints for parameters.

Both location and anchor_date parameters have implicit Optional types.

The propagation of anchor_date through the call chain and location sanitization logic are correctly implemented.

Proposed fix
-    def speak_time(self, dialog: str, location: str = None,
-                   anchor_date: datetime.datetime = None):
+    def speak_time(self, dialog: str, location: Optional[str] = None,
+                   anchor_date: Optional[datetime.datetime] = None):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__init__.py` around lines 416 - 429, The speak_time method currently uses
implicit Optional types for location and anchor_date; update its signature to
use explicit typing (e.g., location: Optional[str], anchor_date:
Optional[datetime.datetime]) and import typing.Optional if not already present,
then propagate the explicit Optional type to calls and any related helper
methods (get_spoken_time, get_display_time, and _sanitize_location) as needed to
keep type consistency across the call chain and avoid type-checker warnings.

344-358: Use explicit Optional type hints.

The parameter type hints use implicit Optional which is prohibited by PEP 484. The static analyzer flagged this.

Proposed fix
     def get_spoken_time(self, location: str = None, force_ampm=False,
-                        anchor_date: datetime.datetime = None) -> Optional[str]:
+                        anchor_date: Optional[datetime.datetime] = None) -> Optional[str]:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__init__.py` around lines 344 - 358, The function get_spoken_time currently
uses None defaults without explicit Optional annotations for parameters
(location: str = None and anchor_date: datetime.datetime = None); update these
to use explicit Optional types (location: Optional[str] and anchor_date:
Optional[datetime.datetime]) and ensure Optional is imported/available in the
module, keeping the existing return type of Optional[str] and leaving force_ampm
as bool.

370-384: Use explicit Optional type hint.

Same implicit Optional issue as get_spoken_time.

Proposed fix
     def get_display_time(self, location: str = None, force_ampm=False,
-                         anchor_date: datetime.datetime = None) -> Optional[str]:
+                         anchor_date: Optional[datetime.datetime] = None) -> Optional[str]:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__init__.py` around lines 370 - 384, The get_display_time signature uses
location: str = None which is implicitly allowing None; change the parameter
typing to Optional[str] and mirror the explicit optional typing used for
anchor_date (and adjust force_ampm typing if desired), e.g. update the function
signature for get_display_time to use location: Optional[str] = None and
anchor_date: Optional[datetime.datetime] = None while keeping the return type ->
Optional[str]; ensure Optional is imported/available like in get_spoken_time.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@__init__.py`:
- Around line 416-429: The speak_time method currently uses implicit Optional
types for location and anchor_date; update its signature to use explicit typing
(e.g., location: Optional[str], anchor_date: Optional[datetime.datetime]) and
import typing.Optional if not already present, then propagate the explicit
Optional type to calls and any related helper methods (get_spoken_time,
get_display_time, and _sanitize_location) as needed to keep type consistency
across the call chain and avoid type-checker warnings.
- Around line 344-358: The function get_spoken_time currently uses None defaults
without explicit Optional annotations for parameters (location: str = None and
anchor_date: datetime.datetime = None); update these to use explicit Optional
types (location: Optional[str] and anchor_date: Optional[datetime.datetime]) and
ensure Optional is imported/available in the module, keeping the existing return
type of Optional[str] and leaving force_ampm as bool.
- Around line 370-384: The get_display_time signature uses location: str = None
which is implicitly allowing None; change the parameter typing to Optional[str]
and mirror the explicit optional typing used for anchor_date (and adjust
force_ampm typing if desired), e.g. update the function signature for
get_display_time to use location: Optional[str] = None and anchor_date:
Optional[datetime.datetime] = None while keeping the return type ->
Optional[str]; ensure Optional is imported/available like in get_spoken_time.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9484dae6-7264-4b38-88f7-1f3dd7cb0d3d

📥 Commits

Reviewing files that changed from the base of the PR and between 4463a09 and 0beaea0.

📒 Files selected for processing (8)
  • __init__.py
  • locale/en-us/ambiguous_locations.list
  • locale/en-us/current_weekend_phrases.list
  • locale/en-us/non_location_phrases.list
  • locale/fr-fr/ambiguous_locations.list
  • locale/fr-fr/current_weekend_phrases.list
  • locale/fr-fr/non_location_phrases.list
  • locale/tr-tr/current_weekend_phrases.list

@JarbasAl JarbasAl requested a review from mikejgray March 9, 2026 01:56
@goldyfruit goldyfruit self-assigned this Mar 9, 2026
@goldyfruit goldyfruit added the translation Localization and translation changes label Mar 9, 2026
@mikejgray
Copy link
Copy Markdown
Collaborator

Hey @goldyfruit , a few items of feedback:

  1. _sanitize_location gets called in handlers, speak_time, AND get_datetime; should pick one canonical spot
  2. Unguarded get_display_time return in speak_time — if it returns None, could propagate to show_time
  3. Weekend calculation logic is correct but convoluted — could use a comment

I know it's out of scope for this particular PR but it would be nice to see if the tests pass with your changes.

@goldyfruit
Copy link
Copy Markdown
Collaborator Author

goldyfruit commented Apr 7, 2026

Addressed the runtime cleanup feedback here:

  • picked one canonical handler-boundary location resolution path via _resolve_location(...)
  • guarded the display-side show_time(...) call so a missing get_display_time() result does not propagate
  • added a short clarifying comment on the weekend defaulting logic

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 7, 2026

Beep! Your PR results are served. 🍽️

I've aggregated the results of the automated checks for this PR below.

📋 Repo Health

Ensuring the repo isn't allergic to new features. 🤧

✅ All required files present.

Latest Version: 1.1.10a1

version.py — Version file
README.md — README
LICENSE — License file
⚠️ pyproject.toml — pyproject.toml
setup.py — setup.py
CHANGELOG.md — Changelog
requirements.txt — Requirements
version.py has valid version block markers

🎙️ Skill

Skill structure analysis complete! 🧠

🎙️ ovos-skill-date-time.openvoiceos — 20 languages

en-us: 12 intents · 18 dialogs · skill.json ✅

Translation coverage — 19 languages (1 complete, 12 partial, 6 incomplete)
Language Progress Coverage
ca-ES █████████░ ⚠️ 92.1% (35/38)
cs-CZ █████░░░░░ ❌ 47.4% (18/38)
da-DK █████████░ ⚠️ 86.8% (33/38)
de-DE █████████░ ⚠️ 92.1% (35/38)
es-ES █████████░ ⚠️ 92.1% (35/38)
eu-ES ███████░░░ ⚠️ 68.4% (26/38)
fa-IR ██████░░░░ ⚠️ 63.2% (24/38)
fr-FR ██████████ ✅ 100.0% (38/38)
gl-ES █████████░ ⚠️ 92.1% (35/38)
hu-HU █████░░░░░ ❌ 47.4% (18/38)
it-IT ██████░░░░ ⚠️ 60.5% (23/38)
nl-NL █████░░░░░ ⚠️ 50.0% (19/38)
pl-PL █████░░░░░ ❌ 47.4% (18/38)
pt-BR █████████░ ⚠️ 92.1% (35/38)
pt-PT ████████░░ ⚠️ 76.3% (29/38)
ru-RU █████░░░░░ ❌ 47.4% (18/38)
sv-FI █████░░░░░ ❌ 47.4% (18/38)
sv-SE █████░░░░░ ❌ 47.4% (18/38)
tr-TR █████░░░░░ ⚠️ 50.0% (19/38)

🔍 Lint

Checking if we've met all our check criteria. ✅

ruff: issues found — see job log

🔌 Skill Tests (ovoscope)

A comprehensive review of the skill's conversational flow. 🌊

0/0 passed

🚌 Bus Coverage

Is the message bus fully covered? Let's see the stats. 📊

⚠️ Bus coverage report unavailable — check the job log.

🏷️ Release Preview

Ensuring the 'Dependency Updates' are documented. 📦

Current: 1.1.10a1Next: 1.1.10a2

Signal Value
Label translation
PR title Split date-time runtime fixes from locale PR
Bump alpha

⚠️ No conventional commit prefix — alpha-only bump.
Suggested: fix: update the thing or feat: update the thing


🚀 Release Channel Compatibility

Predicted next version: 1.1.10a2

Channel Status Note Current Constraint
Stable Too new (must be <0.5.0) ovos-skill-date-time>=0.4.20,<0.5.0
Testing Compatible ovos-skill-date-time>=1.1.5,<2.0.0
Alpha Compatible ovos-skill-date-time>=1.1.10a1

🔨 Build Tests

The build process has successfully terminated. 🏁

✅ All versions pass

Python Build Install Tests
3.10
3.11
3.12
3.13
3.14

⚖️ License Check

Checking if the licenses are compatible with OVOS. 🧩

✅ No license violations found (64 packages).

License distribution: 15× MIT License, 11× Apache Software License, 10× MIT, 7× Apache-2.0, 3× BSD-3-Clause, 2× BSD License, 2× ISC License (ISCL), 2× PSF-2.0, +10 more

Full breakdown — 64 packages
Package Version License URL
audioop-lts 0.2.2 PSF-2.0 link
build 1.4.2 MIT link
certifi 2026.2.25 Mozilla Public License 2.0 (MPL 2.0) link
cffi 2.0.0 MIT link
charset-normalizer 3.4.7 MIT link
click 8.3.2 BSD-3-Clause link
combo_lock 0.3.1 Apache-2.0 link
dateparser 1.4.0 BSD License link
decorator 5.2.1 BSD License
filelock 3.25.2 MIT link
flatbuffers 25.12.19 Apache Software License link
ftfy 6.3.1 Apache-2.0 link
future 1.0.0 MIT License link
geocoder 1.38.1 Apache Software License link
h3 4.4.2 Apache Software License link
idna 3.11 BSD-3-Clause link
importlib_metadata 9.0.0 Apache-2.0 link
json-database 0.10.1 MIT link
kthread 0.2.3 MIT License link
langcodes 3.5.1 MIT License link
markdown-it-py 4.0.0 MIT License link
mdurl 0.1.2 MIT License link
memory-tempfile 2.2.3 MIT License link
numpy 2.4.4 BSD-3-Clause AND 0BSD AND MIT AND Zlib AND CC0-1.0 link
ovos-config 2.1.1 Apache-2.0 link
ovos-date-parser 0.6.5 Apache Software License link
ovos-number-parser 0.5.1 Apache Software License link
ovos-plugin-manager 2.2.0 Apache-2.0 link
ovos-skill-date-time 1.1.10a1 Apache-2.0 link
ovos-solver-yes-no-plugin 0.2.8 MIT link
ovos-utils 0.8.5 Apache-2.0 link
ovos-utterance-normalizer 0.2.3 Apache Software License link
ovos-workshop 7.0.6 apache-2.0 link
ovos_bus_client 1.5.0 Apache Software License link
packaging 26.0 Apache-2.0 OR BSD-2-Clause link
padacioso 1.0.0 apache-2.0 link
pexpect 4.9.0 ISC License (ISCL) link
ptyprocess 0.7.0 ISC License (ISCL) link
pycparser 3.0 BSD-3-Clause link
pyee 12.1.1 MIT License link
Pygments 2.20.0 BSD-2-Clause link
pyproject_hooks 1.2.0 MIT License link
python-dateutil 2.9.0.post0 Apache Software License; BSD License link
pytz 2026.1.post1 MIT License link
PyYAML 6.0.3 MIT License link
quebra-frases 0.3.7 Apache Software License link
RapidFuzz 3.14.5 MIT link
ratelim 0.1.6 MIT link
regex 2026.4.4 Apache-2.0 AND CNRI-Python link
requests 2.33.1 Apache Software License link
rich 13.9.4 MIT License link
rich-click 1.9.7 MIT License

Copyright (c) 2022 Phil Ewels

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
| link |
| simplematch | 1.4 | MIT License | link |
| six | 1.17.0 | MIT License | link |
| standard-aifc | 3.13.0 | Python Software Foundation License | link |
| standard-chunk | 3.13.0 | Python Software Foundation License | link |
| timezonefinder | 8.2.2 | The MIT License (MIT)

Copyright (c) 2016 Jannik Michelfeit

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
| link |
| typing_extensions | 4.15.0 | PSF-2.0 | link |
| tzlocal | 5.3.1 | MIT License | link |
| unicode-rbnf | 2.4.0 | MIT License | |
| urllib3 | 2.6.3 | MIT | link |
| watchdog | 6.0.0 | Apache Software License | link |
| websocket-client | 1.9.0 | Apache Software License | link |
| zipp | 3.23.0 | MIT | link |

Policy: Apache 2.0 (universal donor). StrongCopyleft / NetworkCopyleft / WeakCopyleft / Other / Error categories fail. MPL allowed.

📊 Coverage

Exploring the coverage frontier of your PR. 🚀

21.5% total coverage

Per-file coverage (3 files)
File Coverage Missing lines
setup.py 0.0% 50
version.py 0.0% 4
__init__.py 25.0% 249

Full report: download the coverage-report artifact.

🔒 Security (pip-audit)

Ensuring our cookies are secure and fresh. 🍪

✅ No known vulnerabilities found (84 packages scanned).


The inspector has left the building 🕵️

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
__init__.py (1)

476-479: ⚠️ Potential issue | 🟡 Minor

Pipeline failure: Remove unused variable e.

The except Exception as e: captures the exception but e is never used. Since self.log.exception() automatically includes the exception traceback, the variable is unnecessary.

Proposed fix
-        except Exception as e:
+        except Exception:
             self.log.exception(f"failed to extract date from '{utt}'")
             dt = now
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__init__.py` around lines 476 - 479, The except block unnecessarily binds the
exception to the unused variable "e"; change the handler to not capture the
variable by using "except Exception:" (keeping the call to self.log.exception
and the fallback assignment dt = now) so the traceback is still logged but no
unused variable remains; look for the except clause surrounding the call to
extract_datetime (and references to self.log.exception and dt) and update the
exception header accordingly.
🧹 Nitpick comments (3)
__init__.py (3)

425-444: Use explicit Optional type hints; None guard correctly addresses review feedback.

The guard at lines 443-444 properly addresses the PR feedback about potentially passing None to show_time. However, update the parameter type hints to use explicit Optional.

Proposed fix
-    def speak_time(self, dialog: str, location: str = None,
-                   anchor_date: datetime.datetime = None):
+    def speak_time(self, dialog: str, location: Optional[str] = None,
+                   anchor_date: Optional[datetime.datetime] = None):
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__init__.py` around lines 425 - 444, Update the speak_time signature to use
explicit Optional type hints: import Optional from typing and change the
parameters to location: Optional[str] = None and anchor_date:
Optional[datetime.datetime] = None in the speak_time method; keep the existing
None-guard logic and show_time call as-is since it already prevents passing
None. Refer to the speak_time function name and ensure the typing import is
added/updated.

353-367: Use explicit Optional type hints per PEP 484.

The return type is correctly updated to Optional[str], but the parameter type hints use implicit Optional (e.g., location: str = None), which PEP 484 prohibits.

Proposed fix
-    def get_spoken_time(self, location: str = None, force_ampm=False,
-                        anchor_date: datetime.datetime = None) -> Optional[str]:
+    def get_spoken_time(self, location: Optional[str] = None, force_ampm: bool = False,
+                        anchor_date: Optional[datetime.datetime] = None) -> Optional[str]:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__init__.py` around lines 353 - 367, Update get_spoken_time to use explicit
Optional annotations for parameters that default to None: change the signature
parameters location and anchor_date to location: Optional[str] = None and
anchor_date: Optional[datetime.datetime] = None (keep force_ampm as bool =
False). Ensure Optional is imported from typing if not already. This applies to
the get_spoken_time method definition so the PEP 484 rule is followed
consistently.

379-393: Use explicit Optional type hints per PEP 484.

Same issue as get_spoken_time — parameter type hints should use explicit Optional.

Proposed fix
-    def get_display_time(self, location: str = None, force_ampm=False,
-                         anchor_date: datetime.datetime = None) -> Optional[str]:
+    def get_display_time(self, location: Optional[str] = None, force_ampm: bool = False,
+                         anchor_date: Optional[datetime.datetime] = None) -> Optional[str]:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@__init__.py` around lines 379 - 393, The function signature for
get_display_time should use explicit Optional annotations like get_spoken_time
does: change location: str = None to location: Optional[str] and anchor_date:
datetime.datetime = None to anchor_date: Optional[datetime.datetime]; ensure
Optional is imported from typing if not already present and leave force_ampm as
bool = False. Update the get_display_time declaration accordingly and run type
checks to confirm no other changes are needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@__init__.py`:
- Around line 476-479: The except block unnecessarily binds the exception to the
unused variable "e"; change the handler to not capture the variable by using
"except Exception:" (keeping the call to self.log.exception and the fallback
assignment dt = now) so the traceback is still logged but no unused variable
remains; look for the except clause surrounding the call to extract_datetime
(and references to self.log.exception and dt) and update the exception header
accordingly.

---

Nitpick comments:
In `@__init__.py`:
- Around line 425-444: Update the speak_time signature to use explicit Optional
type hints: import Optional from typing and change the parameters to location:
Optional[str] = None and anchor_date: Optional[datetime.datetime] = None in the
speak_time method; keep the existing None-guard logic and show_time call as-is
since it already prevents passing None. Refer to the speak_time function name
and ensure the typing import is added/updated.
- Around line 353-367: Update get_spoken_time to use explicit Optional
annotations for parameters that default to None: change the signature parameters
location and anchor_date to location: Optional[str] = None and anchor_date:
Optional[datetime.datetime] = None (keep force_ampm as bool = False). Ensure
Optional is imported from typing if not already. This applies to the
get_spoken_time method definition so the PEP 484 rule is followed consistently.
- Around line 379-393: The function signature for get_display_time should use
explicit Optional annotations like get_spoken_time does: change location: str =
None to location: Optional[str] and anchor_date: datetime.datetime = None to
anchor_date: Optional[datetime.datetime]; ensure Optional is imported from
typing if not already present and leave force_ampm as bool = False. Update the
get_display_time declaration accordingly and run type checks to confirm no other
changes are needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 899db3da-6ca2-4880-8955-e39e640ac4f4

📥 Commits

Reviewing files that changed from the base of the PR and between 0beaea0 and cd5061a.

📒 Files selected for processing (1)
  • __init__.py

@JarbasAl JarbasAl merged commit 0477e2f into dev May 10, 2026
15 of 16 checks passed
@JarbasAl JarbasAl deleted the feat/datetime-runtime-fixes branch May 10, 2026 21:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

translation Localization and translation changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants