From 42c1f166dc6e92d51489222e72db6e9b49f44122 Mon Sep 17 00:00:00 2001 From: Anton Yakutovich Date: Sun, 25 Oct 2026 14:21:59 +0400 Subject: [PATCH] fix(db): say under a timeseries table that day is the UTC date of each sample db rows hr and battery bound their rows on the local day while the day column holds the sample's UTC date, so a row could read the day before the one in the title with nothing on screen to explain it. The table view now ends with one line saying so, naming the timezone. Closes #131 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018qh9xqviBHYmzVtmQWSKqA --- CHANGELOG.md | 3 +++ src/commands/db.test.ts | 20 ++++++++++++++++++++ src/commands/db.ts | 7 ++++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3166362..43d2451 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Fixed +- `db rows hr` and `db rows battery` end the table with `day is the UTC date of each sample; rows are selected by the local day in .` Rows were already bounded on the local day, like `fetch`, while `day` holds the UTC date of the sample, so `db rows battery --day 2026-09-10` in Asia/Dubai listed rows dated 2026-09-09 with nothing on screen to say why. JSON is unchanged. (#131) + ## [0.9.1] - 2026-09-15 ### Fixed diff --git a/src/commands/db.test.ts b/src/commands/db.test.ts index aa7a37e..81b6615 100644 --- a/src/commands/db.test.ts +++ b/src/commands/db.test.ts @@ -201,4 +201,24 @@ describe('db rows (#73)', () => { expect(text.stdout).toContain('No cached ring rows.'); expect(text.stdout).toContain('oura-cli fetch ring'); }); + + it('says under a timeseries table that day is the UTC date while rows follow the local day (#131)', async () => { + const path = join(tmpdir(), `oura-db-rows-utc-${process.pid}.db`); + const db = new Database(path); + ensureSchema(db); + db.query('INSERT INTO ring_battery_level (timestamp, level, charging, in_charger, day) VALUES (?,?,?,?,?)').run('2026-09-09T22:00:00+00:00', 80, 0, 0, '2026-09-09'); + db.close(); + const rows = (...collection: string[]) => Bun.spawn(['bun', 'run', 'src/index.ts', '--db', path, '--tz', 'Asia/Dubai', 'db', 'rows', ...collection, '--day', '2026-09-10', '--format', 'table'], { stdout: 'pipe', stderr: 'pipe' }); + try { + const battery = rows('battery'); + const lines = (await new Response(battery.stdout).text()).trimEnd().split('\n'); + expect(await battery.exited).toBe(0); + expect(lines.at(-2)).toBe(' 2026-09-09T22:00:00+00:00 80 0 0 2026-09-09'); + expect(lines.at(-1)).toBe(' day is the UTC date of each sample; rows are selected by the local day in Asia/Dubai.'); + const tags = rows('tags'); + expect(await new Response(tags.stdout).text()).not.toContain('UTC'); + } finally { + for (const suffix of ['', '-wal', '-shm']) rmSync(path + suffix, { force: true }); + } + }); }); diff --git a/src/commands/db.ts b/src/commands/db.ts index 7ae2372..456c248 100644 --- a/src/commands/db.ts +++ b/src/commands/db.ts @@ -7,6 +7,8 @@ import { formatDaySummary, formatWeekTable, formatTrends, formatStats, PUBLISH_D import { formatRows } from '../render/format-rows.js'; import { CliError } from '../lib/errors.js'; import { daysBack } from '../lib/time.js'; +import { screenWidth } from '../lib/terminal.js'; +import { wrap } from '../lib/wrap.js'; import { assertCalendarDate, assertPositiveInt } from '../lib/validate.js'; import { assertRangeAllowed, resolveRange } from './fetch.js'; import { dataCommand } from './run-command.js'; @@ -122,7 +124,10 @@ export const dbCommand = defineCommand({ const hint = range === null ? `Run \`oura-cli sync\` to fill the cache, or \`oura-cli fetch ${c.name}\` to read the API.` : `Run \`oura-cli sync\` (\`sync --from \` for older days), or \`oura-cli fetch ${c.name}\` to read the API.`; - return { json: rows, text: () => formatRows(c, rows, scope, 'table', hint, undefined, all.length) }; + const utcNote = range !== null && c.rangeParams === 'datetime' && rows.length > 0 + ? wrap(`day is the UTC date of each sample; rows are selected by the local day in ${ctx.tz}.`, screenWidth(), ' ') + : []; + return { json: rows, text: () => [formatRows(c, rows, scope, 'table', hint, undefined, all.length), ...utcNote].join('\n') }; }, }), },