From 001b39c033efff8697cd4193ac65dba1c197604e Mon Sep 17 00:00:00 2001 From: Dries Peeters Date: Tue, 8 Sep 2026 20:21:51 +0200 Subject: [PATCH 1/2] fix(timezone): accept plain date in local_date formatting Dashboard crashed when Task.due_date (a calendar date) was piped through |local_date, which assumed a datetime and read .tzinfo. Skip timezone conversion for date-only values, matching format_user_datetime. --- app/utils/timezone.py | 4 ++++ tests/test_utils.py | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/app/utils/timezone.py b/app/utils/timezone.py index 24b5247dc..56a3744a5 100644 --- a/app/utils/timezone.py +++ b/app/utils/timezone.py @@ -410,6 +410,10 @@ def format_local_datetime(utc_dt, format_str="%Y-%m-%d %H:%M"): if utc_dt is None: return "" + # Plain calendar dates have no time component; timezone conversion does not apply. + if isinstance(utc_dt, date) and not isinstance(utc_dt, datetime): + return utc_dt.strftime(format_str) + local_dt = utc_to_local(utc_dt) return local_dt.strftime(format_str) diff --git a/tests/test_utils.py b/tests/test_utils.py index b7bb1433c..3e2ebaab2 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -60,6 +60,21 @@ def test_local_date_filter(app): assert isinstance(result, str) +@pytest.mark.unit +@pytest.mark.utils +def test_local_date_filter_plain_date(app): + """Test local_date filter accepts plain date objects (e.g. Task.due_date).""" + register_template_filters(app) + with app.app_context(): + filter_func = app.jinja_env.filters.get("local_date") + plain_date = datetime.date(2024, 1, 15) + result = filter_func(plain_date) + assert result is not None + assert isinstance(result, str) + assert "2024" in result + assert "15" in result or "01" in result + + @pytest.mark.unit @pytest.mark.utils def test_local_date_filter_none(app): From d2f0c4e7be35d9a71c6296ea54d37174f4488fee Mon Sep 17 00:00:00 2001 From: Dries Peeters Date: Tue, 8 Sep 2026 20:25:03 +0200 Subject: [PATCH 2/2] chore(release): bump version to 5.13.5 and update docs Document timezone local_date, idle paused-timer, and single-client manual-entry fixes; sync desktop, mobile, and extension versions. --- CHANGELOG.md | 16 ++++++++++++++++ README.md | 4 ++++ browser-extension/manifest.json | 2 +- browser-extension/package.json | 2 +- desktop/package-lock.json | 4 ++-- desktop/package.json | 2 +- docs/mobile-desktop-apps/DESKTOP_WEBAPP_GAP.md | 6 +++--- mobile/android/local.properties | 4 ++-- mobile/pubspec.yaml | 2 +- setup.py | 2 +- 10 files changed, 32 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3255a1940..7c9e15ed5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [5.13.5] - 2026-09-08 + +### Fixed + +- **Timezone `local_date` crash on plain dates** — Dashboard crashed when `Task.due_date` (a calendar date) was piped through `|local_date`, which assumed a datetime and read `.tzinfo`. Skip timezone conversion for date-only values, matching `format_user_datetime`. +- **Idle auto-stop on paused timers (#752)** — Paused timers stop heartbeating, so the idle sweep treated them as idle: “Still working?” push, needs-review flag, then optional auto-stop. Guard the idle query on `paused_at IS NULL` so intentionally paused timers are skipped. +- **Manual entry single-client lock (#753)** — The manual time-entry form passed `force_selectable=True` to the client select, which bypassed the single-client readonly lock. Dropping the flag restores the pre-filled lock when the org has exactly one client. + +### Changed + +- **Client versions** — Synced Electron (`desktop/package.json`), Flutter (`mobile/pubspec.yaml`), and Chromium extension (`browser-extension/manifest.json` / `package.json`) to **5.13.5** with the webapp (`setup.py`). + +### Documentation + +- **Version** — Bumped `setup.py` to **5.13.5** (single source of truth for the application version). + ## [5.13.4] - 2026-09-04 ### Fixed diff --git a/README.md b/README.md index 7353693ed..53404a9ed 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,10 @@ TimeTracker has been continuously enhanced with powerful new features! Here's wh **Current version** is defined in `setup.py` (single source of truth). See [CHANGELOG.md](CHANGELOG.md) for versioned release history. +### ✨ Highlights of v5.13.5 + +**Patch (5.13.5):** **Timezone `local_date`** — date-only values (e.g. task due dates) no longer crash the dashboard filter. **Idle skip paused (#752)** — paused timers are excluded from idle notify / needs-review / auto-stop. **Manual entry single-client (#753)** — restores the readonly pre-filled client lock when the org has one client. See [CHANGELOG.md](CHANGELOG.md#5135---2026-09-08). + ### ✨ Highlights of v5.13.4 **Patch (5.13.4):** **Alembic multiple heads** — merge migration `184_merge_183_heads` rejoins the parallel `183` device-token and idle needs-review branches so `flask db upgrade` has a single head. See [CHANGELOG.md](CHANGELOG.md#5134---2026-09-04). diff --git a/browser-extension/manifest.json b/browser-extension/manifest.json index 9790380a5..444e9ea84 100644 --- a/browser-extension/manifest.json +++ b/browser-extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "TimeTracker Timer", - "version": "5.13.4", + "version": "5.13.5", "description": "Start and stop TimeTracker timers from your browser toolbar.", "permissions": [ "storage", diff --git a/browser-extension/package.json b/browser-extension/package.json index 95d549554..7bdabf22c 100644 --- a/browser-extension/package.json +++ b/browser-extension/package.json @@ -1,7 +1,7 @@ { "name": "timetracker-browser-extension", "private": true, - "version": "5.13.4", + "version": "5.13.5", "description": "TimeTracker Chromium extension (Manifest V3)", "type": "module", "scripts": { diff --git a/desktop/package-lock.json b/desktop/package-lock.json index 7863b97b7..703480e25 100644 --- a/desktop/package-lock.json +++ b/desktop/package-lock.json @@ -1,12 +1,12 @@ { "name": "timetracker-desktop", - "version": "5.13.4", + "version": "5.13.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "timetracker-desktop", - "version": "5.13.4", + "version": "5.13.5", "license": "MIT", "dependencies": { "axios": "^1.18.0", diff --git a/desktop/package.json b/desktop/package.json index f9809946b..b33a7386c 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -1,6 +1,6 @@ { "name": "timetracker-desktop", - "version": "5.13.4", + "version": "5.13.5", "description": "TimeTracker desktop app for Windows, Linux, and macOS", "main": "src/main/main.js", "scripts": { diff --git a/docs/mobile-desktop-apps/DESKTOP_WEBAPP_GAP.md b/docs/mobile-desktop-apps/DESKTOP_WEBAPP_GAP.md index 8aff32b1e..367d4fbef 100644 --- a/docs/mobile-desktop-apps/DESKTOP_WEBAPP_GAP.md +++ b/docs/mobile-desktop-apps/DESKTOP_WEBAPP_GAP.md @@ -6,9 +6,9 @@ Living checklist for bringing the Electron desktop client up to speed with the F | Component | Version | Source | |-----------|---------|--------| -| Web / backend | 5.13.4 | `setup.py` | -| Desktop | 5.13.4 | `desktop/package.json` | -| Mobile | 5.13.4 | `mobile/pubspec.yaml` | +| Web / backend | 5.13.5 | `setup.py` | +| Desktop | 5.13.5 | `desktop/package.json` | +| Mobile | 5.13.5 | `mobile/pubspec.yaml` | ## What desktop has diff --git a/mobile/android/local.properties b/mobile/android/local.properties index 17a108175..3048a3f6e 100644 --- a/mobile/android/local.properties +++ b/mobile/android/local.properties @@ -1,5 +1,5 @@ -flutter.versionCode=51304 -flutter.versionName=5.13.4 +flutter.versionCode=51305 +flutter.versionName=5.13.5 flutter.sdk=C:\\Flutter\\flutter sdk.dir=C:\\Users\\dries\\AppData\\Local\\Android\\Sdk flutter.buildMode=release diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index 6c3121bcc..1b0897220 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -1,7 +1,7 @@ name: timetracker_mobile description: TimeTracker mobile app for Android and iOS publish_to: 'none' -version: 5.13.4+1 +version: 5.13.5+1 environment: sdk: '>=3.0.0 <4.0.0' diff --git a/setup.py b/setup.py index 69036c908..8c33c5ad7 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ setup( name='timetracker', - version='5.13.4', + version='5.13.5', packages=find_packages(), include_package_data=True, package_data={