Shifting Clinical Dates logo

Shifting Clinical Dates

CommunityPopular
maziyarpanahi
shifting-clinical-dates

Apply consistent per-patient date shifting in OpenMed that preserves intervals between events while satisfying HIPAA Safe Harbor's date rule. Use when the user needs to de-identify dates but keep temporal structure for research, shift all dates by the same offset per patient, preserve days-between-events for survival or longitudinal analysis, cap ages over 89, or strip everything but the year. Covers deidentify(method="shift_dates", date_shift_days=..., keep_year=...) and per-patient reproducible offsets via consistent=True, seed=.... Pairs with OpenMed deidentifying-clinical-text and auditing-safe-harbor-checklist.

Overview

Publishermaziyarpanahi
Repositoryopenmed
Skill nameshifting-clinical-dates
Stars
5.6K
Forks
708
Bundled files
Instructions only
LicenseApache-2.0
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by maziyarpanahi on GitHub. Read the source before you install it.

Installation

Install the Shifting Clinical Dates AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/maziyarpanahi/openmed/tree/master/skills/shifting-clinical-dates
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/maziyarpanahi/openmed.git /tmp/openmed
mkdir -p .claude/skills
cp -r /tmp/openmed/skills/shifting-clinical-dates .claude/skills/shifting-clinical-dates
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Shifting Clinical Dates in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Shifting Clinical Dates on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Shifting Clinical Dates is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Shifting clinical dates

HIPAA Safe Harbor forbids keeping dates more specific than the year. But naively deleting dates destroys the temporal structure research depends on — time to event, length of stay, intervals between visits. Date shifting is the compromise: move every date by a single random offset so the absolute dates become meaningless while the intervals between them are preserved exactly. OpenMed does this on-device with deidentify(method="shift_dates", ...).

When to use this skill

Use it when downstream analysis needs temporal relationships (survival curves, sepsis-to-antibiotic time, readmission gaps) but the calendar dates must be de-identified. If you can throw dates away entirely, plain method="mask" is simpler — reach for shifting only when intervals matter.

Quick start

python
import openmed

note = (
    "Admitted 2024-03-02, started antibiotics 2024-03-04, discharged 2024-03-09. "
    "Follow-up scheduled 2024-04-02."
)

# Shift every date by the SAME offset -> intervals preserved, dates obscured.
result = openmed.deidentify(
    note,
    method="shift_dates",
    consistent=True,        # one stable offset for this run
    seed=20240519,          # reproducible per-patient offset (use a per-patient key)
    keep_year=False,        # do NOT retain the year (Safe Harbor: year-only is the max)
)
print(result.deidentified_text)
# Admit -> antibiotics is still 2 days; admit -> discharge still 7 days; etc.

How interval preservation works

All dates in the document are moved by one offset (auto-selected as a random non-zero value in roughly ±1 year, or fixed with date_shift_days=). Because the offset is identical for every date, the difference between any two dates is unchanged:

real:    Mar 2  ──2d──▶ Mar 4  ──5d──▶ Mar 9
shifted: Jul 18 ──2d──▶ Jul 20 ──5d──▶ Jul 25     (offset = +138 days, intervals intact)

That is why survival time, length of stay, and visit gaps survive de-identification while the actual calendar is destroyed.

Per-patient consistent offsets

Each patient should get their own offset, and that offset should be stable across documents and reproducible across runs. Derive a per-patient seed (e.g. from a secret keyed hash of the patient ID — never the raw MRN) and pass it as seed=:

python
def patient_offset_seed(patient_key: str) -> int:
    import hashlib, hmac
    # keyed so the mapping from patient -> offset is itself a secret
    digest = hmac.new(b"<vault-secret>", patient_key.encode(), hashlib.sha256).digest()
    return int.from_bytes(digest[:8], "big")

for doc in patient_documents:
    openmed.deidentify(
        doc, method="shift_dates",
        consistent=True, seed=patient_offset_seed(patient_id),
        keep_year=False,
    )

Same patient → same offset everywhere (their notes stay internally consistent); different patients → different offsets (cross-patient dates cannot be aligned).

Workflow

  1. Decide the offset policy. Per-patient is standard for clinical research. Use consistent=True + a per-patient seed. Use a fixed date_shift_days= only when a deterministic, externally-managed offset is required.
  2. Set keep_year=False for Safe Harbor. (keep_year=True retains the year, which is permissible only if dates aren't tied to an individual's care.)
  3. Cap ages over 89 separately. Date shifting moves dates; it does not aggregate ages. Safe Harbor requires ages >89 and any date implying age >89 to collapse to a single "90+" — handle AGE spans explicitly (auditing-safe-harbor-checklist).
  4. Keep names/IDs handled too. shift_dates only touches dates. Run a normal redaction pass (or a policy) for PERSON, ID_NUM, etc.
  5. Verify intervals are preserved and no calendar leaked via audit=True.

Hand-off to / from OpenMed

  • Core de-id: deidentifying-clinical-text — combine date shifting with a policy= so names/IDs are redacted in the same pipeline.
  • Safe Harbor checklist: auditing-safe-harbor-checklist — the date rule and the age-90 cap are categories C in the 18.
  • Audit: auditing-deidentification-runs records the method and per-span actions (offsets/hashes, not raw dates).
  • Other surfaces: MCP openmed_deidentify / REST POST /pii/deidentify accept the same method and parameters.

Edge cases & gotchas

  • Offset 0 is forbidden. An auto-selected offset is always non-zero — a zero shift would silently leave dates unchanged and defeat de-identification.
  • Reuse the seed per patient, not globally. A single global offset lets an attacker re-align all patients to a real anchor date; per-patient offsets break that. Derive seeds from a secret, not the plaintext MRN.
  • keep_year=True is not Safe Harbor by itself when the year reveals the age of someone >89 or ties to care episodes — pair with age capping.
  • Shifting does not cap ages. Age >89 is a separate transformation; date shifting won't fix an explicit "age 94" in the text.
  • Day-first locales. For non-English notes set lang= so 11/04/2024 is parsed in the right order before shifting (deidentifying-multilingual-text).
  • Store the per-patient seed/offset like PHI — it re-identifies the calendar if leaked. Keep it in a vault, separate from the output.

Standards & references

Frequently asked questions

What does the Shifting Clinical Dates AI skill do?

Apply consistent per-patient date shifting in OpenMed that preserves intervals between events while satisfying HIPAA Safe Harbor's date rule. Use when the user needs to de-identify dates but keep temporal structure for research, shift all dates by the same offset per patient, preserve days-between-events for survival or longitudinal analysis, cap ages over 89, or strip everything but the year. Covers deidentify(method="shift_dates", date_shift_days=..., keep_year=...) and per-patient reproducible offsets via consistent=True, seed=.... Pairs with OpenMed deidentifying-clinical-text and audit...

Why use Shifting Clinical Dates on TypingMind?

Because you install it once and use it with any model. Shifting Clinical Dates is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Shifting Clinical Dates in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/maziyarpanahi/openmed/tree/master/skills/shifting-clinical-dates. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Shifting Clinical Dates?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Shifting Clinical Dates?

As many as you like. As long as a model supports skills, you can use Shifting Clinical Dates with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Shifting Clinical Dates AI skill free?

Yes. It is published on GitHub by maziyarpanahi under the Apache-2.0 license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇