diff --git a/CHANGELOG.md b/CHANGELOG.md index 85e2af3..0780b17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - The `error:` line is coloured only when stderr is a terminal (and `NO_COLOR` / `--no-color` are not set, or `FORCE_COLOR` asks for it). The decision followed stdout, so `oura-cli sync 2>err.log` on a terminal wrote ANSI codes into the file. (#150) - `--help` fits an 80-column terminal again, bar the root USAGE line that lists every command. citty padded each OPTIONS row with trailing spaces to column 87, so every row wrapped though its text fitted; the padding is gone. A `db` subcommand's screen names it in full (`USAGE oura-cli db rows …`, banner `(oura-cli db rows v…)`) where it said `db rows`; `db stats` and `db rows` have shorter descriptions so the longer banner still fits. (#147) - The progress line on a terminal names the collection, as the summary under it does: `syncing sleep` is the sleep collection and `syncing sleep-periods` the sleep periods, where it printed the API endpoint (`daily_sleep`, `sleep`, `vO2_max`, `ring_battery_level`). Its page counter starts again with each request, so a 61-day `fetch hr`, three 30-day requests, counts up to each request's own pages instead of running to page 87; the row count still runs across them. (#129) +- `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 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 1995235..7785715 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') }; }, }), },