From 83b2397c87dd33556c23e6304461479488e3ea20 Mon Sep 17 00:00:00 2001 From: Gamosoft Date: Wed, 6 May 2026 16:06:03 +0200 Subject: [PATCH] custom date/time formats --- backend/utils.py | 49 +++++++++++++++++++++++++++++++------- documentation/FEATURES.md | 1 + documentation/TEMPLATES.md | 33 +++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 8 deletions(-) diff --git a/backend/utils.py b/backend/utils.py index 06a37ac..795aaf0 100644 --- a/backend/utils.py +++ b/backend/utils.py @@ -964,31 +964,64 @@ def get_template_content(notes_dir: str, template_name: str) -> Optional[str]: return None +# Matches custom strftime placeholders like {{date:%Y%m%d}} or {{time:%H%M%S}}. +# The three prefixes mirror the bare {{date}}, {{time}}, {{datetime}} placeholders; +# disallowing '{' and '}' inside the format keeps parsing unambiguous and +# guarantees we never swallow an adjacent placeholder. +_STRFTIME_PLACEHOLDER_RE = re.compile(r'\{\{(date|time|datetime):([^{}]+)\}\}') + + def apply_template_placeholders(content: str, note_path: str) -> str: """ Replace template placeholders with actual values. - - Supported placeholders: + + Built-in named placeholders (fixed default formats): {{date}} - Current date (YYYY-MM-DD) {{time}} - Current time (HH:MM:SS) {{datetime}} - Current datetime (YYYY-MM-DD HH:MM:SS) - {{timestamp}} - Unix timestamp + {{timestamp}} - Unix timestamp (seconds) {{year}} - Current year (YYYY) {{month}} - Current month (MM) {{day}} - Current day (DD) {{title}} - Note name without extension {{folder}} - Parent folder name - + + Custom date/time formats (strftime escape hatch): + {{date:FMT}}, {{time:FMT}}, {{datetime:FMT}} + + Any of the three date/time placeholders above also accepts an + optional ":FMT" suffix where FMT is a Python strftime() format + string. Pick the prefix whose default format covers the same + components you want to format. Examples: + + {{datetime:%Y%m%d%H%M%S}} -> 20260506154200 (filename-safe stamp) + {{date:%d/%m/%Y}} -> 06/05/2026 (European date) + {{date:%A}} -> Wednesday (full weekday name) + {{time:%H%M%S}} -> 154200 (compact time) + {{date:%V}} -> 19 (ISO week number) + + Invalid format strings are left in the output unchanged so a typo + is visible rather than silently swallowed. + Args: content: Template content with placeholders note_path: Path of the note being created - + Returns: Content with placeholders replaced """ now = datetime.now() note = Path(note_path) - + + def _strftime_sub(match: 're.Match[str]') -> str: + fmt = match.group(2) + try: + return now.strftime(fmt) + except (ValueError, TypeError): + return match.group(0) + + content = _STRFTIME_PLACEHOLDER_RE.sub(_strftime_sub, content) + replacements = { '{{date}}': now.strftime('%Y-%m-%d'), '{{time}}': now.strftime('%H:%M:%S'), @@ -1000,11 +1033,11 @@ def apply_template_placeholders(content: str, note_path: str) -> str: '{{title}}': note.stem, '{{folder}}': note.parent.name if str(note.parent) != '.' else 'Root', } - + result = content for placeholder, value in replacements.items(): result = result.replace(placeholder, value) - + return result diff --git a/documentation/FEATURES.md b/documentation/FEATURES.md index 4a3f738..6bb4b11 100644 --- a/documentation/FEATURES.md +++ b/documentation/FEATURES.md @@ -287,6 +287,7 @@ Create notes from reusable templates with dynamic placeholder replacement. - `{{day}}` - Current day (DD) - `{{title}}` - Note name (without extension) - `{{folder}}` - Parent folder name +- `{{date:FORMAT}}` / `{{time:FORMAT}}` / `{{datetime:FORMAT}}` - Custom format using any Python `strftime()` string, e.g. `{{datetime:%Y%m%d%H%M%S}}` → `20251126143045`. See [Templates](TEMPLATES.md#custom-datetime-formats) for the full recipe list. ### Example Template ```markdown diff --git a/documentation/TEMPLATES.md b/documentation/TEMPLATES.md index e6db71d..95fa266 100644 --- a/documentation/TEMPLATES.md +++ b/documentation/TEMPLATES.md @@ -46,6 +46,39 @@ Templates support dynamic placeholders that are replaced when you create a note: | `{{title}}` | Note name (without .md) | `Weekly Meeting` | | `{{folder}}` | Parent folder name | `Projects` | +### Custom date/time formats + +Need a different format than the defaults above? Any of the three +date/time placeholders — `{{date}}`, `{{time}}`, `{{datetime}}` — accepts +an optional `:FORMAT` suffix to override its default format. `FORMAT` is +any [Python `strftime()` format string](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes), +so anything `strftime` can do, your template can do. + +> **One rule to remember:** pick the prefix whose default already covers +> the components you want to format. Use `{{date:…}}` for date-only +> formats, `{{time:…}}` for time-only formats, and `{{datetime:…}}` when +> you need both. + +#### Common recipes + +| Template | Result | Use case | +|---|---|---| +| `{{date:%Y%m%d}}` | `20251126` | Date stamp for filenames | +| `{{time:%H%M%S}}` | `143045` | Time stamp for filenames | +| `{{datetime:%Y%m%d%H%M%S}}` | `20251126143045` | Sortable, filename-safe full stamp | +| `{{datetime:%Y%m%dT%H%M%S}}` | `20251126T143045` | ISO-8601 basic, no separators | +| `{{datetime:%Y-%m-%dT%H:%M:%S}}` | `2025-11-26T14:30:45` | ISO-8601 extended | +| `{{date:%d/%m/%Y}}` | `26/11/2025` | European-style date | +| `{{date:%m/%d/%Y}}` | `11/26/2025` | US-style date | +| `{{date:%A}}` | `Wednesday` | Full weekday name | +| `{{date:%a}}` | `Wed` | Short weekday name | +| `{{date:%B}}` | `November` | Full month name | +| `{{date:%V}}` | `48` | ISO week number | +| `{{date:%j}}` | `330` | Day of year (1–366) | + +> Invalid format strings (typos) are left in the output unchanged so the +> mistake is visible rather than silently swallowed. + ### Example Template ```markdown