fix: monthly schedule label shows 21th/22th/31th (ordinal suffix for days >20) (#1577)
This commit is contained in:
@@ -7,6 +7,7 @@ import markdownModule from './markdown.js';
|
|||||||
import * as spinnerModule from './spinner.js';
|
import * as spinnerModule from './spinner.js';
|
||||||
import { makeWindowDraggable } from './windowDrag.js';
|
import { makeWindowDraggable } from './windowDrag.js';
|
||||||
import { sortModelIds } from './modelSort.js';
|
import { sortModelIds } from './modelSort.js';
|
||||||
|
import { ordinalSuffix } from './util/ordinal.js';
|
||||||
|
|
||||||
const API_BASE = window.location.origin;
|
const API_BASE = window.location.origin;
|
||||||
let _open = false;
|
let _open = false;
|
||||||
@@ -244,7 +245,7 @@ function _scheduleLabel(task) {
|
|||||||
}
|
}
|
||||||
if (task.schedule === 'monthly') {
|
if (task.schedule === 'monthly') {
|
||||||
const d = task.scheduled_day ?? 1;
|
const d = task.scheduled_day ?? 1;
|
||||||
const suffix = d === 1 ? 'st' : d === 2 ? 'nd' : d === 3 ? 'rd' : 'th';
|
const suffix = ordinalSuffix(d);
|
||||||
return `Monthly on ${d}${suffix} at ${localTime}`;
|
return `Monthly on ${d}${suffix} at ${localTime}`;
|
||||||
}
|
}
|
||||||
return task.schedule || '—';
|
return task.schedule || '—';
|
||||||
|
|||||||
13
static/js/util/ordinal.js
Normal file
13
static/js/util/ordinal.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// Pure (browser-free) English ordinal suffix, e.g. 1 -> "st", 21 -> "st",
|
||||||
|
// 22 -> "nd", 23 -> "rd", 11/12/13 -> "th". Extracted so it can be unit-tested.
|
||||||
|
export function ordinalSuffix(n) {
|
||||||
|
const a = Math.abs(Math.trunc(Number(n) || 0));
|
||||||
|
const mod100 = a % 100;
|
||||||
|
if (mod100 >= 11 && mod100 <= 13) return 'th';
|
||||||
|
switch (a % 10) {
|
||||||
|
case 1: return 'st';
|
||||||
|
case 2: return 'nd';
|
||||||
|
case 3: return 'rd';
|
||||||
|
default: return 'th';
|
||||||
|
}
|
||||||
|
}
|
||||||
35
tests/test_ordinal_suffix_js.py
Normal file
35
tests/test_ordinal_suffix_js.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
"""Pin the ordinal-suffix helper used by the monthly-schedule label in tasks.js.
|
||||||
|
|
||||||
|
_scheduleLabel built the suffix with `d === 1 ? 'st' : d === 2 ? 'nd' : ...`,
|
||||||
|
which only handles single digits, so a monthly task on day 21/22/23/31 rendered
|
||||||
|
"Monthly on 21th"/"22th"/"23th"/"31th". The shared ordinalSuffix() fixes this.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
_REPO = Path(__file__).resolve().parent.parent
|
||||||
|
_HELPER = _REPO / "static" / "js" / "util" / "ordinal.js"
|
||||||
|
_HAS_NODE = shutil.which("node") is not None
|
||||||
|
|
||||||
|
|
||||||
|
def _suffixes(nums):
|
||||||
|
arr = json.dumps(nums)
|
||||||
|
js = f"""
|
||||||
|
import {{ ordinalSuffix }} from '{_HELPER.as_posix()}';
|
||||||
|
console.log(JSON.stringify({arr}.map(n => n + ordinalSuffix(n))));
|
||||||
|
"""
|
||||||
|
proc = subprocess.run(["node", "--input-type=module"], input=js,
|
||||||
|
capture_output=True, text=True, cwd=str(_REPO), timeout=30)
|
||||||
|
assert proc.returncode == 0, proc.stderr
|
||||||
|
return json.loads(proc.stdout.strip())
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.skipif(not _HAS_NODE, reason="node binary not on PATH")
|
||||||
|
def test_ordinal_suffixes_for_days_of_month():
|
||||||
|
assert _suffixes([1, 2, 3, 4, 11, 12, 13, 21, 22, 23, 31]) == [
|
||||||
|
"1st", "2nd", "3rd", "4th", "11th", "12th", "13th", "21st", "22nd", "23rd", "31st",
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user