Skip to content

fix(skills): replace deprecated utcnow in skill timestamp helper#5777

Open
husamemadH wants to merge 1 commit into
odysseus-dev:devfrom
husamemadH:fix/5697-skill-timestamp-utcnow
Open

fix(skills): replace deprecated utcnow in skill timestamp helper#5777
husamemadH wants to merge 1 commit into
odysseus-dev:devfrom
husamemadH:fix/5697-skill-timestamp-utcnow

Conversation

@husamemadH

Copy link
Copy Markdown

Summary

_now_iso() in services/memory/skill_format.py builds the created timestamp written into every skill's frontmatter. It used datetime.utcnow(), which returns a naive datetime and has been deprecated since Python 3.12 and scheduled for removal:

DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for
removal in a future version. Use timezone-aware objects to represent datetimes
in UTC: datetime.datetime.now(datetime.UTC).

This switches the helper to the timezone-aware datetime.now(timezone.utc) while keeping the serialized YYYY-MM-DDTHH:MM:SSZ shape byte-for-byte, so skill files written by earlier versions keep parsing unchanged.

timezone.utc is used rather than the datetime.UTC alias from the warning text, since the alias is only available on Python 3.11+ and timezone.utc works everywhere with no behavioural difference. This also matches the fix suggested in the issue.

Target branch

  • This PR targets dev, not main.

Linked Issue

Fixes #5697

Type of Change

  • Chore / cleanup (non-breaking — removes a deprecated API call)

Checklist

  • I searched open issues and open PRs — this is not a duplicate.
  • This PR targets dev
  • My changes are limited to the scope described above — no unrelated refactors or whitespace changes mixed in.
  • I exercised the changed code path and verified the output is unchanged.

Scope

utcnow() appears 177 times across 39 files in the tree. This PR deliberately changes only the helper named in the issue, which asks for a small scoped cleanup. Converting the rest would be a large mechanical diff better handled separately, and several of the other call sites feed comparisons against naive datetimes that need checking individually rather than blanket-replacing.

How to Test

  1. Confirm the deprecation on current dev:

    import warnings
    from services.memory.skill_format import _now_iso
    with warnings.catch_warnings(record=True) as w:
        warnings.simplefilter("always")
        _now_iso()
    print([str(x.message) for x in w])
    # -> ["datetime.datetime.utcnow() is deprecated ..."]
  2. With this patch the same snippet reports no warnings, and _now_iso() still returns e.g. 2026-07-26T20:19:34Z.

  3. Round-trip through the code path that actually writes frontmatter:

    from services.memory.skill_format import Skill
    md = Skill(name="demo-skill", description="round-trip check").to_markdown()
    print([l for l in md.splitlines() if l.startswith("created")])
    # -> ['created: "2026-07-26T20:19:34Z"']   (matches real UTC, no warning raised)
  4. Tests:

    python -m pytest tests/test_skill_format_timestamp.py -q   # 3 passed
    python -m pytest tests/ -k skill -q                        # 77 passed

Tests added

tests/test_skill_format_timestamp.py pins three properties:

Test Guards against
test_now_iso_emits_no_deprecation_warning the reported regression — verified failing against unpatched dev
test_now_iso_keeps_serialized_shape a future change altering the on-disk format
test_now_iso_is_utc_not_local_time a plausible wrong fix — bare datetime.now() produces the identical shape but emits local wall time

The third is not decorative: patched to datetime.now(), it fails by exactly the local UTC offset. It forces a non-UTC timezone (Asia/Amman) so the two visibly diverge, since under a UTC-configured test runner a naive now() would otherwise pass unnoticed.

Visual / UI changes

  • None — this is a backend-only cleanup with no rendered output. The serialized timestamp is unchanged.

Files changed

  • services/memory/skill_format.py — import timezone; _now_iso() uses datetime.now(timezone.utc).
  • tests/test_skill_format_timestamp.py — new regression tests (see table above).

Disclosure

The patch was drafted with Claude Code. I reproduced the deprecation warning in my own instance, reviewed the change, and confirmed the new tests fail against unpatched dev before they pass with it. Flagging it per CONTRIBUTING.md's note on agent-generated PRs — this is a single targeted change against an existing issue, not a bulk submission.

_now_iso() builds the 'created' value in skill frontmatter. datetime.utcnow()
returns a naive datetime and has been deprecated since Python 3.12, scheduled
for removal. Switch to the timezone-aware datetime.now(timezone.utc), keeping
the serialized YYYY-MM-DDTHH:MM:SSZ shape unchanged so existing skill files
keep parsing.

timezone.utc is used rather than the datetime.UTC alias, which is 3.11+ only.

Adds regression tests covering the deprecation, the serialized shape, and
UTC correctness under a non-UTC local timezone -- the last guards against a
bare datetime.now(), which yields the same shape but local wall time.

Fixes odysseus-dev#5697
@github-actions github-actions Bot added the ready for review Description complete — ready for maintainer review label Jul 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready for review Description complete — ready for maintainer review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Replace deprecated utcnow in skill timestamp helper

1 participant