Skip to main content
Glama
README.md
# Date Math

**Exact date and time math for AI agents, paid per call.** LLMs are unreliable at business days, DST, recurring schedules and holiday calendars. Date Math gives them deterministic answers over HTTP or MCP.

- **Price:** $0.001 per call in USDC on Base, via [x402](https://x402.org)
- **Access:** no signup, no API keys
- **Base URL:** `https://date-math.datemath.workers.dev`
- **MCP endpoint:** `https://date-math.datemath.workers.dev/mcp` (Streamable HTTP)
- **Docs for agents:** [`/llms.txt`](https://date-math.datemath.workers.dev/llms.txt) · [`/openapi.json`](https://date-math.datemath.workers.dev/openapi.json)

## Tools

| HTTP (`POST`, JSON body) | MCP tool | What it does |
|---|---|---|
| `/v1/diff` | `date_diff` | Days, weeks, months and business days between two dates. Pass `country` to skip its public holidays. |
| `/v1/add` | `date_add` | The date N days, weeks, months or business days before or after a date |
| `/v1/convert` | `timezone_convert` | Converts a time between IANA time zones (DST-aware) |
| `/v1/info` | `date_info` | Weekday, ISO week, quarter, day of year, leap year |
| `/v1/holidays` | `public_holidays` | Public holidays for 207 countries, 2000–2050 |
| `/v1/overlap` | `working_hours_overlap` | When working hours overlap across time zones (meeting slots) |
| `/v1/cron` | `cron_schedule` | Explains a cron expression and lists its next runs in any zone |
| `/v1/parse` | `parse_date_text` | "next Friday at 3pm" → an exact ISO datetime |
| `/v1/city` | `city_time` | City name → time zone and local time (7,000+ cities) |
| `/v1/recurrence` | `recurring_dates` | Expands an iCal RRULE ("every 2nd Tuesday") into dates |
| `/v1/business_hours` | `business_hours_deadline` | SLA deadline N working hours from now, or working time elapsed |

Every response is a JSON object. Errors look like `{"error":{"code","message"}}`. Full schemas and examples are in [`/openapi.json`](https://date-math.datemath.workers.dev/openapi.json).

## Examples

```bash
# Unpaid call → HTTP 402 with payment terms in the PAYMENT-REQUIRED header
curl -i -X POST https://date-math.datemath.workers.dev/v1/business_hours \
  -H "Content-Type: application/json" \
  -d '{"start":"2026-11-06T16:00","hours":2,"timezone":"Asia/Manila","country":"PH"}'
```

| Request body | Result |
|---|---|
| `/v1/business_hours` `{"start":"2026-11-06T16:00","hours":2,"timezone":"Asia/Manila"}` | `"deadline":"2026-11-09T10:00:00+08:00"` (Monday) |
| `/v1/add` `{"date":"2026-12-22","business_days":5,"country":"PH"}` | `"result":"2026-12-31"` (skips Christmas and Rizal Day) |
| `/v1/overlap` `{"timezones":["Asia/Manila","Europe/London"],"date":"2026-11-10","work_end":"18:00"}` | One window: `09:00–10:00Z` |
| `/v1/recurrence` `{"rrule":"FREQ=MONTHLY;BYDAY=2TU","start":"2026-01-01T10:00","timezone":"Asia/Manila","count":3}` | Jan 13, Feb 10, Mar 10 at 10:00 +08:00 |

## Paying from code

Any x402 client pays automatically. With the official TypeScript client:

```ts
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";

const pay = wrapFetchWithPaymentFromConfig(fetch, {
  schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(privateKeyToAccount(process.env.KEY as `0x${string}`)) }],
});

const res = await pay("https://date-math.datemath.workers.dev/v1/city", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ cities: ["Cebu", "London"] }),
});
console.log(await res.json());
```

The wallet needs USDC on Base. No ETH is required, because the facilitator pays gas.

## Using it over MCP

- **Endpoint:** `https://date-math.datemath.workers.dev/mcp` (Streamable HTTP, stateless)
- **Listing tools** (`tools/list`) is free.
- **Each tool call** costs $0.001 via x402 MCP. Payment travels in the tool call's `_meta`, and x402-aware clients handle it automatically:

```ts
import { createx402MCPClient } from "@x402/mcp";
import { ExactEvmScheme } from "@x402/evm/exact/client";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

const client = createx402MCPClient({
  name: "my-agent", version: "1.0.0",
  schemes: [{ network: "eip155:8453", client: new ExactEvmScheme(account) }],
  autoPayment: true,
});
await client.connect(new StreamableHTTPClientTransport(new URL("https://date-math.datemath.workers.dev/mcp")));
const r = await client.callTool("public_holidays", { country: "US", year: 2026 });
```

MCP clients without x402 support can list the tools, but calls return "Payment required".

## Run your own copy

The service is a single Cloudflare Worker and fits the free plan: every tool uses 0–6 ms of CPU per call.

```bash
cd worker
npm install
npm test                 # 99 tests
npx wrangler login
npx wrangler deploy
```

Set your own wallet and price in [`worker/wrangler.toml`](worker/wrangler.toml):

| Setting | Meaning |
|---|---|
| `PAY_TO` | Your wallet address. Leave empty to make every endpoint free. |
| `PRICE` | Price per call, e.g. `$0.001` |
| `X402_NETWORK` | `eip155:8453` (Base) or `eip155:84532` (Base Sepolia, for testing) |
| `FACILITATOR_URL` | Defaults to PayAI: no key needed, and it pays gas |

Optional secrets (set with `npx wrangler secret put`):
- **`CDP_API_KEY_ID`, `CDP_API_KEY_SECRET`:** settle through Coinbase's facilitator first.
- **`POCKET_PREFIX`:** a private, unpaid path for trusted relays.

To check your deployment against x402scan's discovery rules, run `node tools/x402scan-audit.mjs https://your-worker.workers.dev`.

## How it's built

- **Stack:** [Hono](https://hono.dev) on Cloudflare Workers, plus [`@x402/hono`](https://www.npmjs.com/package/@x402/hono) and [`@x402/mcp`](https://www.npmjs.com/package/@x402/mcp).
- **Discovery:** OpenAPI with `x-payment-info`, `/.well-known/x402`, x402 Bazaar metadata on every 402, and `llms.txt`.
- **Precomputed data:** the holiday and city tables are built at build time (`worker/scripts/build-*.mjs`), so lookups are O(1) per request.
- **Time zones:** UTC offsets come from cached `Intl` formatters and a week-segment cache. They're tested against direct lookups for tricky zones (Morocco, Lord Howe, Chatham).
- **Cron:** a built-in engine (`worker/src/cron.ts`), checked against `cron-parser` on 400 random expressions.
- **Workers-specific shim:** x402's Bazaar route validator uses Ajv code generation (`new Function`), which Workers blocks. `wrangler.toml` aliases it to a no-op shim (`worker/src/shims/bazaar.ts`).

## Data and license

- **Code:** MIT, see [LICENSE](LICENSE).
- **Holiday data:** derived from [date-holidays](https://github.com/commenthol/date-holidays) (CC BY 3.0). Lunar or government-proclaimed dates are rule-based estimates.
- **City data:** derived from [city-timezones](https://github.com/kevinroberts/city-timezones) (MIT), based on Natural Earth (public domain).

Contact: see `info.contact` in [`/openapi.json`](https://date-math.datemath.workers.dev/openapi.json).