fix: stop reporting a timezone's name as the user's city
get_location printed "City: Manila", derived from the last segment of the IANA zone name. That is the timezone database's representative place, not the user's: Asia/Manila is one zone covering the entire Philippines, Asia/Kolkata covers all of India, Europe/Oslo all of Norway. It was wrong for anyone outside the zone's namesake, and confidently so, because a model asked about the weather uses it without hesitating. The README claimed the value was region-level, so the output also contradicted its own docs. - The zone's label is still reported, as `Timezone area`, and named for what it is. Both the field and the tool note say outright that it is not the user's city, and the tool description repeats it. - The locale region subtag is now used, which is a declaration rather than an inference: a locale of en-PH is the Philippines because the user set it. - A Location preference was added. It is the only exact place this extension has, it is reported verbatim and first, and when it is set the note tells the model to prefer it over anything inferred. - The continent grouping from the zone name was dropped: "Asia" next to a region of "Philippines" reads as two answers to the same question. - ToolContext takes the place as an injected value, keeping tools.ts free of Vicinae imports as the module wiring test requires. Verified: 316 tests, tsc, eslint, prettier, and vici build.
This commit is contained in:
@@ -1,21 +1,53 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { describeTimezone, getLocationContext, utcOffsetLabel, weekdayName } from "../lib/location";
|
||||
import {
|
||||
describeTimezoneArea,
|
||||
getLocationContext,
|
||||
localeRegion,
|
||||
utcOffsetLabel,
|
||||
weekdayName,
|
||||
} from "../lib/location";
|
||||
|
||||
describe("describeTimezone", () => {
|
||||
it("splits a simple zone into region and city", () => {
|
||||
expect(describeTimezone("Asia/Manila")).toEqual({ region: "Asia", city: "Manila" });
|
||||
describe("describeTimezoneArea", () => {
|
||||
it("keeps the zone's own label", () => {
|
||||
expect(describeTimezoneArea("Asia/Manila")).toEqual({ area: "Manila" });
|
||||
});
|
||||
|
||||
it("handles zones with more than two segments", () => {
|
||||
expect(describeTimezone("America/Argentina/Salta")).toEqual({ region: "America", city: "Salta" });
|
||||
expect(describeTimezoneArea("America/Argentina/Salta")).toEqual({ area: "Salta" });
|
||||
});
|
||||
|
||||
it("underscores become spaces", () => {
|
||||
expect(describeTimezone("America/New_York")).toEqual({ region: "America", city: "New York" });
|
||||
it("turns underscores into spaces", () => {
|
||||
expect(describeTimezoneArea("America/New_York")).toEqual({ area: "New York" });
|
||||
});
|
||||
|
||||
it("drops the continent grouping, which answers a question nobody asked", () => {
|
||||
// "Asia" beside a locale region of "Philippines" reads as two answers to
|
||||
// the same question.
|
||||
expect(describeTimezoneArea("Asia/Manila")).not.toHaveProperty("region");
|
||||
});
|
||||
|
||||
it("returns nothing for a bare zone like UTC", () => {
|
||||
expect(describeTimezone("UTC")).toEqual({});
|
||||
expect(describeTimezoneArea("UTC")).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe("localeRegion", () => {
|
||||
it("reads the region the user configured, rather than inferring one", () => {
|
||||
expect(localeRegion("en-PH")).toEqual({ code: "PH", name: "Philippines" });
|
||||
});
|
||||
|
||||
it("handles other regions", () => {
|
||||
expect(localeRegion("de-DE")?.name).toBe("Germany");
|
||||
expect(localeRegion("en-US")?.code).toBe("US");
|
||||
});
|
||||
|
||||
it("has nothing to say for a locale with no region", () => {
|
||||
expect(localeRegion("en")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("does not throw on a malformed locale", () => {
|
||||
expect(localeRegion("not a locale")).toBeUndefined();
|
||||
expect(localeRegion("")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -54,10 +86,11 @@ describe("getLocationContext", () => {
|
||||
const context = getLocationContext(new Date("2026-01-02T03:04:05Z"), "Asia/Manila");
|
||||
|
||||
expect(context.timezone).toBe("Asia/Manila");
|
||||
expect(context.city).toBe("Manila");
|
||||
expect(context.region).toBe("Asia");
|
||||
expect(context.utcOffset).toContain("+08:00");
|
||||
expect(context.locale.length).toBeGreaterThan(0);
|
||||
// The zone's label is reported as such, never as the user's city.
|
||||
expect(context.timezoneArea).toBe("Manila");
|
||||
expect(context).not.toHaveProperty("city");
|
||||
});
|
||||
|
||||
it("still returns a usable context for an unknown zone", () => {
|
||||
@@ -65,4 +98,15 @@ describe("getLocationContext", () => {
|
||||
expect(context.timezone).toBe("Not/AReal_Zone");
|
||||
expect(context.utcOffset).toBe("UTC");
|
||||
});
|
||||
|
||||
it("carries the configured place through verbatim", () => {
|
||||
const context = getLocationContext(new Date(), "Asia/Manila", " Cebu City, Philippines ");
|
||||
expect(context.place).toBe("Cebu City, Philippines");
|
||||
});
|
||||
|
||||
it("treats an empty or whitespace place as unset", () => {
|
||||
expect(getLocationContext(new Date(), "Asia/Manila", "").place).toBeUndefined();
|
||||
expect(getLocationContext(new Date(), "Asia/Manila", " ").place).toBeUndefined();
|
||||
expect(getLocationContext(new Date(), "Asia/Manila").place).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user