|
| 1 | +# Worklog time model (0.8.8) |
| 2 | + |
| 3 | +Fixes WI-1279, WI-1280, WI-1281, WI-1282 (report: GitHub issue 247). |
| 4 | + |
| 5 | +## Agreed model |
| 6 | + |
| 7 | +A worklog entry is constructed from a selected civil date, optional clock |
| 8 | +times, and an explicit entry timezone. The three fields together resolve to |
| 9 | +complete UTC start/end timestamps, which are the single source of truth for |
| 10 | +display, filtering, and reporting. |
| 11 | + |
| 12 | +- Explicit `start_time`/`end_time` clocks take precedence over a supplied |
| 13 | + `duration`; when both are sent the duration must match or the request is |
| 14 | + rejected with 400 (`duration does not match start_time and end_time`). |
| 15 | +- `start_time` plus `duration` computes the end clock on the same civil date. |
| 16 | +- An end clock at or before the start clock resolves on the following day, so |
| 17 | + overnight work (23:00-01:00) is one continuous interval. Equal clocks span |
| 18 | + the full 24h daily cap. |
| 19 | +- Duration-only input keeps the established convention: the interval starts |
| 20 | + at local midnight on the selected date in the entry timezone. |
| 21 | +- Clocks are exact wall-clock values: DST gaps and folds are rejected with an |
| 22 | + explicit error, and the offset of each actual date applies (an overnight |
| 23 | + entry across a DST boundary stores the true elapsed time). |
| 24 | +- Moving the selected date on an edit reconstructs both timestamps from the |
| 25 | + new day, preserving the clock times and the day difference. |
| 26 | +- A PATCH that touches no time field preserves the stored interval exactly. |
| 27 | + The frontend form sends time fields only when the user changed one, and |
| 28 | + always sends its explicit `timezone` when it does. |
| 29 | + |
| 30 | +## Entry timezone resolution |
| 31 | + |
| 32 | +`input.timezone` wins, then the acting user's profile timezone, then the |
| 33 | +stored profile default (UTC). The frontend populates and interprets the form |
| 34 | +controls in the profile timezone, so browser/profile differences cannot |
| 35 | +silently reinterpret saved work. |
| 36 | + |
| 37 | +## Reporting semantics (WI-1281) |
| 38 | + |
| 39 | +Date labels, date-range inclusion, and daily totals derive from the stored |
| 40 | +timestamps in an explicitly selected reporting timezone: |
| 41 | + |
| 42 | +- `GET /time/worklogs`, `GET /time/projects/{id}/worklogs`, and v1 |
| 43 | + `GET /rest/api/v1/time/worklogs` accept a `timezone` query parameter for |
| 44 | + their civil date filters; it defaults to the caller's profile timezone. |
| 45 | +- Filters are interval overlaps on the timestamps (`start_time < rangeEnd` |
| 46 | + and `end_time > rangeStart`), so an entry crossing local midnight counts |
| 47 | + toward both days. An interval ending exactly at midnight contributes |
| 48 | + nothing to the following day. |
| 49 | +- List ordering is `start_time DESC`. |
| 50 | +- Desktop views (TimeEntry, Timesheet, TimeReports), exports (CSV/PDF), the |
| 51 | + mobile timer list, and item worklog tabs label and group by the civil date |
| 52 | + of the timestamps in the reporting timezone, splitting minutes at local |
| 53 | + midnight (`frontend/src/lib/utils/worklogTimezone.js`). |
| 54 | +- The daily briefing query uses the same overlap semantics against the |
| 55 | + user-timezone day window. |
| 56 | + |
| 57 | +## Stored `date` column inventory (kept, not dropped) |
| 58 | + |
| 59 | +`time_worklogs.date` still stores the UTC-midnight key of the entry date in |
| 60 | +the entry timezone, and writers still populate it (worklog create/update, |
| 61 | +timer stop, AI `log_time`). Remaining readers after 0.8.8: |
| 62 | + |
| 63 | +- API responses (`date` field on v1 and v2 DTOs) for external consumers. |
| 64 | +- No internal grouping, filtering, or ordering reads the column anymore; the |
| 65 | + daily briefing and every report derive from the timestamps. |
| 66 | + |
| 67 | +Dropping the column requires a migration plus an audit of external API |
| 68 | +consumers of the `date` field; historical rows keep valid timestamps, so no |
| 69 | +backfill is needed before that removal. |
0 commit comments