İçeriğe geç

/calendar

docs/src/snippets/modules.test.ts
// import { LIFETIME, cycleKey, cycleWindow, monthKey, monthWindow } from "pricemeter/calendar";
const t = Date.parse("2026-09-30T22:30:00Z"); // already October 1st in Istanbul (UTC+3)
monthKey({ at: t }); // "2026-09"
monthKey({ at: t, tz: "Europe/Istanbul" }); // "2026-10"
monthWindow({ at: t, tz: "Europe/Istanbul" }); // { from: 2026-09-30T21:00Z, to: 2026-10-31T21:00Z }
// A subscription started on Jan 31st renews on the last day of shorter months.
const anchor = Date.parse("2026-01-31T00:00:00Z");
cycleKey({ at: Date.parse("2026-03-10T00:00:00Z"), anchor }); // "2026-02-28"
cycleWindow({ at: Date.parse("2026-03-10T00:00:00Z"), anchor }); // { from: 2026-02-28, to: 2026-03-31 }
LIFETIME; // "*" — the period key for counters that never reset

Çekirdek takvim okumaz (A72); “bu dönem” nedir sorusu uygulamanındır. pricemeter/calendar bu soruyu yanıtlamanın saf yardımcılarını verir: usedSoFar sayacının hangi döneme ait olduğunu seçen anahtarlar ve integrate() ile sayaç sorguları için pencereler.

API Döner
monthKey({ at, tz? }) "2026-09": tz saat dilimindeki takvim ayı (varsayılan UTC)
monthWindow({ at, tz? }) O ayın yarı açık [from, to) penceresi, epoch ms
cycleKey({ at, anchor, tz? }) "2026-09-15": anchor’ın ayın gününe bağlı döngünün yerel başlangıç tarihi
cycleWindow({ at, anchor, tz? }) O döngünün [from, to) penceresi. 31’inde başlayan döngü kısa aylarda ayın son günü yenilenir
LIFETIME "*": hiç sıfırlanmayan sayaçların dönem anahtarı (B16)

Saat dilimleri Intl ile çözülür; yerel gece yarısının var olmadığı günler (00:00’da DST) doğru işlenir. Pencereler yarı açıktır: dönem sonu gözlemi at: window.to − 1 ile yapılır, çünkü window.to bir sonraki dönemi seçer (A98).

docs/src/snippets/modules.test.ts
const mem = memoryAdapters({ rates: { sms: flat(0) }, periodOf: (at) => monthKey({ at, tz: "Europe/Istanbul" }) });
const metering = buildMetering()
.context<{ accountId: string; tz: string }>()
.refs(["send"])
.meter("sms")
.getRate(async (meter, _dims, ctx, at) => ({
rate: { model: "graduated", tiers: [{ from: 0, unitPriceMicroUsd: 0 }, { from: 500, unitPriceMicroUsd: 20_000 }], policy: { tierPeriod: "month" } },
usedSoFar: mem.store.used(ctx.accountId, meter, monthKey({ at, tz: ctx.tz })), // this local month's counter
}))
.commit(mem.commit);

Sayaç deposu ve getRate aynı anahtarı kullanmalıdır: burada memoryAdapters({ periodOf }) satırları yerel aya göre sayar, getRate de aynı ayın sayacını okur. policy.tierPeriod: "month" bu seçimi tarifeye not eder; lib onu okumaz. SQLite’ta anahtar yerine pencere kullanılır: usedSoFar({ account, meter, from, to }).

UTC takvim ayı için new Date(at).toISOString().slice(0, 7) yeterlidir. Modül şu durumlarda işe yarar: UTC dışı bir saat dilimi (İstanbul’da 30 Eylül 22:30 UTC zaten Ekim’dir), abonelik gününe bağlı döngüler (31 Ocak → 28 Şubat → 31 Mart) ve DST geçişleri. Zaten bir takvim kütüphanen (Temporal, date-fns-tz, Luxon) varsa onunla aynı anahtarı üret; kütüphane anahtarın nereden geldiğini bilmez.