mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-18 12:59:12 +00:00
Compare commits
2
Commits
2f64dbc48c
...
5a6dfbfc9a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a6dfbfc9a | ||
|
|
dcba6120f9 |
@@ -119,7 +119,12 @@ def test_timezone_step_carries_the_configured_zone(timezone):
|
||||
|
||||
def test_city_does_not_influence_the_timezone_step():
|
||||
"""The coupling this change removes: city said nothing about the timezone,
|
||||
and OR-ing it let a saved city tick the step off with the zone still wrong."""
|
||||
and OR-ing it let a saved city tick the step off with the zone still wrong.
|
||||
|
||||
timezone_step() returns the opening tag only, so this compares the state
|
||||
the step is in -- data-done and data-tz -- and not the label, which does
|
||||
still show the configured city as context and so differs between the two.
|
||||
"""
|
||||
tampa = timezone_step(render(config_with(
|
||||
location={"city": "Tampa", "state": "Florida", "country": "US"})))
|
||||
seattle = timezone_step(render(config_with(
|
||||
@@ -135,6 +140,90 @@ def test_missing_timezone_leaves_the_step_open():
|
||||
assert 'data-done="0"' in step
|
||||
|
||||
|
||||
def test_zone_comparison_asks_for_the_time_of_day():
|
||||
"""Guard on the Intl options, which look like a stylistic choice.
|
||||
|
||||
dateStyle/timeStyle are late additions (Firefox shipped them in 91). An
|
||||
implementation that does not know them ignores them and formats the date
|
||||
alone -- which compares New York, Chicago and Madrid as equal and ticks
|
||||
the step for a timezone that is plainly wrong. Explicit numeric fields
|
||||
have been in Intl since ECMA-402 v1.
|
||||
"""
|
||||
template = (PROJECT_ROOT / "web_interface" / "templates" / "v3"
|
||||
/ "partials" / "overview.html").read_text()
|
||||
body = template[template.index("function sameZone"):]
|
||||
body = body[:body.index("}())")]
|
||||
# The comment above the options names dateStyle/timeStyle to explain why
|
||||
# they are not used, so match on code only.
|
||||
body = "\n".join(line for line in body.splitlines()
|
||||
if not line.lstrip().startswith("//"))
|
||||
assert "dateStyle" not in body and "timeStyle" not in body, (
|
||||
"zone comparison must not depend on dateStyle/timeStyle")
|
||||
for field in ("hour:", "minute:", "year:", "month:", "day:"):
|
||||
assert field in body, f"zone comparison dropped {field!r}"
|
||||
|
||||
|
||||
def test_zone_comparison_samples_both_sides_of_dst():
|
||||
"""One instant is not enough, and the shortfall is invisible for months.
|
||||
|
||||
America/New_York and America/Lima hold the same offset all winter, so a
|
||||
check against now alone ticks the step in January for a panel that runs an
|
||||
hour off from March. The comparison has to sample instants either side of
|
||||
DST -- mid-January and mid-July, which covers both hemispheres.
|
||||
"""
|
||||
template = (PROJECT_ROOT / "web_interface" / "templates" / "v3"
|
||||
/ "partials" / "overview.html").read_text()
|
||||
body = template[template.index("function sameZone"):]
|
||||
body = body[:body.index("}())")]
|
||||
code = "\n".join(line for line in body.splitlines()
|
||||
if not line.lstrip().startswith("//"))
|
||||
assert "Date.UTC" in code, (
|
||||
"zone comparison samples only the current instant, so zones that "
|
||||
"coincide seasonally would read as equal")
|
||||
assert code.count("Date.UTC") >= 2, "expected an instant either side of DST"
|
||||
|
||||
|
||||
def _stamp(zone, instant):
|
||||
"""The JS comparison's algorithm, for pinning what it must decide.
|
||||
|
||||
There is no JS runtime here (and the repo has no JS test infra), so this
|
||||
mirrors sameZone rather than executing it: same instants, same wall-clock
|
||||
equality. It records the verdicts the shipped code has to reach.
|
||||
"""
|
||||
from zoneinfo import ZoneInfo
|
||||
return instant.astimezone(ZoneInfo(zone)).strftime("%m/%d/%Y %H:%M")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"left,right,equivalent",
|
||||
[
|
||||
# Aliases: one zone under two names.
|
||||
("Asia/Calcutta", "Asia/Kolkata", True),
|
||||
("Europe/Kiev", "Europe/Kyiv", True),
|
||||
# Same rules year-round: either renders the same times, so a panel set
|
||||
# to one and browsed from the other is correctly configured.
|
||||
("America/New_York", "America/Toronto", True),
|
||||
# Coincide in winter only -- the case a single-instant check gets wrong.
|
||||
("America/New_York", "America/Lima", False),
|
||||
("America/Phoenix", "America/Los_Angeles", False),
|
||||
("Australia/Sydney", "Pacific/Guadalcanal", False),
|
||||
# Plainly different.
|
||||
("America/New_York", "America/Chicago", False),
|
||||
("America/New_York", "Europe/Madrid", False),
|
||||
],
|
||||
)
|
||||
def test_which_zone_pairs_must_count_as_the_same(left, right, equivalent):
|
||||
from datetime import datetime
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
year = 2026
|
||||
instants = [datetime(year, 1, 15, 12, tzinfo=ZoneInfo("UTC")),
|
||||
datetime(year, 7, 15, 12, tzinfo=ZoneInfo("UTC"))]
|
||||
matched = all(_stamp(left, at) == _stamp(right, at) for at in instants)
|
||||
assert matched is equivalent, (
|
||||
f"{left} vs {right}: sampling both seasons gave {matched}")
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"hardware,expected",
|
||||
[
|
||||
|
||||
@@ -184,18 +184,44 @@
|
||||
// the old test ticked it the moment the value stopped being the default.
|
||||
function sameZone(a, b) {
|
||||
if (a === b) return true;
|
||||
// Compare the wall-clock time each zone yields for one instant, not
|
||||
// the identifiers: aliases (Asia/Calcutta vs Asia/Kolkata,
|
||||
// Europe/Kiev vs Europe/Kyiv) name one zone and must not read as a
|
||||
// mismatch.
|
||||
// Compare the wall-clock time each zone yields, not the identifiers:
|
||||
// aliases (Asia/Calcutta vs Asia/Kolkata, Europe/Kiev vs Europe/Kyiv)
|
||||
// name one zone and must not read as a mismatch.
|
||||
//
|
||||
// Sampled at three instants, all of which have to agree. Checking only
|
||||
// now is not enough: America/New_York and America/Lima hold the same
|
||||
// offset all winter, so a panel set to the wrong one of those would
|
||||
// tick in January and then run an hour off from March. Mid-January and
|
||||
// mid-July sit either side of DST in both hemispheres, so only zones
|
||||
// that agree year-round match -- while Toronto still matches New York,
|
||||
// which is right, since either renders the same times.
|
||||
try {
|
||||
var now = new Date();
|
||||
var stamp = function (tz) {
|
||||
var year = now.getUTCFullYear();
|
||||
var instants = [now,
|
||||
new Date(Date.UTC(year, 0, 15, 12)),
|
||||
new Date(Date.UTC(year, 6, 15, 12))];
|
||||
var stamp = function (tz, at) {
|
||||
// Explicit numeric fields rather than dateStyle/timeStyle:
|
||||
// those are late additions to Intl (Firefox shipped them in
|
||||
// 91), and an implementation that does not know them ignores
|
||||
// them and formats the date alone. That would compare
|
||||
// New York, Chicago and Madrid as equal and tick the step for
|
||||
// a timezone that is plainly wrong -- the exact failure this
|
||||
// check exists to catch. These options have been in Intl
|
||||
// since ECMA-402 v1.
|
||||
return new Intl.DateTimeFormat('en-US', {
|
||||
timeZone: tz, dateStyle: 'short', timeStyle: 'short'
|
||||
}).format(now);
|
||||
timeZone: tz, year: 'numeric', month: '2-digit',
|
||||
day: '2-digit', hour: '2-digit', minute: '2-digit',
|
||||
hour12: false
|
||||
}).format(at);
|
||||
};
|
||||
return stamp(a) === stamp(b);
|
||||
for (var i = 0; i < instants.length; i++) {
|
||||
if (stamp(a, instants[i]) !== stamp(b, instants[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (e) {
|
||||
// An unparseable zone in the config is worth surfacing, not hiding.
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user