Limit study time to hours in reviews graph (#4086)

* Add maxUnit argument to naturalUnit

* Limit study time to hours in reviews graph

Relevant discussions:
- https://forums.ankiweb.net/t/reviews-graph-units-of-total-time-studied-suggestion/61237
- https://forums.ankiweb.net/t/why-does-anki-display-study-time-in-months/37722
- https://forums.ankiweb.net/t/poll-use-hours-in-total-time-stats/62076
- https://github.com/ankitects/anki/pull/3901#issuecomment-2973161601

* Use the new approach for native stability in Card Info

* Use a simpler approach
This commit is contained in:
user1823 2025-06-18 13:04:58 +05:30 committed by GitHub
parent 4040a3c1f9
commit 44f3bbbbc9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 19 additions and 9 deletions

View file

@ -7,8 +7,8 @@ export const SECOND = 1.0;
export const MINUTE = 60.0 * SECOND;
export const HOUR = 60.0 * MINUTE;
export const DAY = 24.0 * HOUR;
export const MONTH = 30.417 * DAY; // 365/12 ≈ 30.417
export const YEAR = 365.0 * DAY;
export const MONTH = YEAR / 12;
export enum TimespanUnit {
Seconds,
@ -146,8 +146,13 @@ function i18nFuncForUnit(
If precise is true, show to two decimal places, eg
eg 70 seconds -> "1.17 minutes"
If false, seconds and days are shown without decimals. */
export function timeSpan(seconds: number, short = false, precise = true): string {
const unit = naturalUnit(seconds);
export function timeSpan(
seconds: number,
short = false,
precise = true,
maxUnit: TimespanUnit = TimespanUnit.Years,
): string {
const unit = Math.min(naturalUnit(seconds), maxUnit);
let amount = unitAmount(unit, seconds);
if (!precise && unit < TimespanUnit.Months) {
amount = Math.round(amount);

View file

@ -5,7 +5,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
<script lang="ts">
import type { CardStatsResponse } from "@generated/anki/stats_pb";
import * as tr2 from "@generated/ftl";
import { DAY, timeSpan, Timestamp } from "@tslib/time";
import { DAY, timeSpan, TimespanUnit, Timestamp } from "@tslib/time";
export let stats: CardStatsResponse;
@ -58,7 +58,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
if (stats.memoryState) {
let stability = timeSpan(stats.memoryState.stability * 86400, false, false);
if (stats.memoryState.stability > 31) {
const nativeStability = stats.memoryState.stability.toFixed(0);
const nativeStability = timeSpan(
stats.memoryState.stability * 86400,
false,
false,
TimespanUnit.Days,
);
stability += ` (${nativeStability})`;
}
statsRows.push({

View file

@ -8,7 +8,7 @@
import type { GraphsResponse } from "@generated/anki/stats_pb";
import * as tr from "@generated/ftl";
import { localizedNumber } from "@tslib/i18n";
import { dayLabel, timeSpan } from "@tslib/time";
import { dayLabel, timeSpan, TimespanUnit } from "@tslib/time";
import type { Bin, ScaleSequential } from "d3";
import {
area,
@ -141,7 +141,7 @@ export function renderReviews(
const yTickFormat = (n: number): string => {
if (showTime) {
return timeSpan(n / 1000, true);
return timeSpan(n / 1000, true, false, TimespanUnit.Hours);
} else {
if (Math.round(n) != n) {
return "";
@ -205,7 +205,7 @@ export function renderReviews(
function valueLabel(n: number): string {
if (showTime) {
return timeSpan(n / 1000);
return timeSpan(n / 1000, false, true, TimespanUnit.Hours);
} else {
return tr.statisticsReviews({ reviews: n });
}
@ -340,7 +340,7 @@ export function renderReviews(
averageAnswerTime: string,
averageAnswerTimeLabel: string;
if (showTime) {
totalString = timeSpan(total / 1000, false);
totalString = timeSpan(total / 1000, false, true, TimespanUnit.Hours);
averageForDaysStudied = tr.statisticsMinutesPerDay({
count: Math.round(studiedAvg / 1000 / 60),
});