Rebasely Beta: data may be purged

examples/MCP-tools Public

TypeScript · 103 lines · 4.4 KiB · at d0ed32f

import { z } from "zod";import { ToolError } from "./errors.ts";import { defineTool } from "./types.ts";// The canonical IANA names this runtime knows. Intl accepts more than it lists (UTC, links such// as US/Pacific), so this is for matching city names, not for deciding what is valid.const ZONES = Intl.supportedValuesOf("timeZone");// An ISO 8601 instant with an explicit offset. Without one, Date.parse reads the time in the// Worker's own zone, which is UTC in production and something else on a laptop.const INSTANT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/i;export function resolveZone(text: string): string {  const given = text.trim();  const key = given.toLowerCase().replace(/\s+/g, "_");  const listed = ZONES.find((z) => z.toLowerCase() === key);  if (listed) return listed;  if (isZone(given)) return given.includes("/") ? given : given.toUpperCase();  const cities = ZONES.filter((z) => z.toLowerCase().split("/").pop() === key);  if (cities.length === 1) return cities[0];  throw new ToolError(`Unknown time zone "${text}". Use an IANA name such as Europe/Paris, Asia/Tokyo or America/New_York.`);}function isZone(name: string): boolean {  try {    new Intl.DateTimeFormat("en-US", { timeZone: name });    return true;  } catch {    return false;  }}function parseInstant(at: string | undefined, now: Date): Date {  if (at === undefined) return now;  const date = INSTANT.test(at.trim()) ? new Date(at.trim()) : null;  if (!date || Number.isNaN(date.getTime())) {    throw new ToolError(`"${at}" is not an ISO 8601 instant with an offset. Use a form such as 2026-10-16T14:00:00Z or 2026-10-16T09:00:00-05:00.`);  }  return date;}// "GMT+05:30" -> "+05:30" and 330; "GMT" -> "+00:00" and 0.function offsetOf(date: Date, zone: string): { offset: string; minutes: number } {  const name = new Intl.DateTimeFormat("en-US", { timeZone: zone, timeZoneName: "longOffset" }).formatToParts(date).find((p) => p.type === "timeZoneName")?.value ?? "GMT";  const m = /([+-])(\d{2}):(\d{2})/.exec(name);  if (!m) return { offset: "+00:00", minutes: 0 };  const minutes = (m[1] === "-" ? -1 : 1) * (Number(m[2]) * 60 + Number(m[3]));  return { offset: `${m[1]}${m[2]}:${m[3]}`, minutes };}function localParts(date: Date, zone: string): Record<string, string> {  const parts = new Intl.DateTimeFormat("en-US", {    timeZone: zone,    hourCycle: "h23",    weekday: "long",    year: "numeric",    month: "2-digit",    day: "2-digit",    hour: "2-digit",    minute: "2-digit",    second: "2-digit",  }).formatToParts(date);  return Object.fromEntries(parts.map((p) => [p.type, p.value]));}export const timeInZone = defineTool({  name: "time_in_zone",  title: "Time in a time zone",  description:    "Give the local date, time, weekday and UTC offset in an IANA time zone, for now or for a given instant. " +    "Use it for questions like \"what time is it in Tokyo\" or \"when is 15:00 UTC in Chicago\"; " +    "your own sense of the current time may be wrong. Daylight saving time is applied.",  input: z.object({    zone: z.string().min(1).describe("An IANA time zone such as Europe/Paris or America/New_York. A city that names exactly one zone, such as tokyo, also works."),    at: z.string().optional().describe("The instant to convert, in ISO 8601 with Z or an offset, such as 2026-10-16T14:00:00Z. Leave it out for the current time."),  }),  output: z.object({    zone: z.string(),    instant: z.string().describe("The instant in UTC, ISO 8601."),    date: z.string().describe("The local date, YYYY-MM-DD."),    time: z.string().describe("The local time, HH:MM:SS on a 24-hour clock."),    weekday: z.string(),    utc_offset: z.string().describe("The offset from UTC at that instant, such as +09:00."),    utc_offset_minutes: z.number().int(),    formatted: z.string().describe("The local time written out in English."),  }),  run({ zone, at }, { now }) {    const name = resolveZone(zone);    const date = parseInstant(at, now);    const p = localParts(date, name);    const { offset, minutes } = offsetOf(date, name);    return {      zone: name,      instant: date.toISOString(),      date: `${p.year}-${p.month}-${p.day}`,      time: `${p.hour}:${p.minute}:${p.second}`,      weekday: p.weekday,      utc_offset: offset,      utc_offset_minutes: minutes,      formatted: new Intl.DateTimeFormat("en-US", { timeZone: name, dateStyle: "full", timeStyle: "long" }).format(date),    };  },});