---
url: https://jer-k.github.io/effect-prophet/examples/events.md
description: >-
  Model holidays and one-off events with custom event calendars and country
  holidays.
---

# Holidays and events

Some days don't follow the usual pattern: holidays, sales, launches, outages. List them as
**events**, and the model learns how much each one moves your data.

::: tip Verified against Python Prophet
Events match Python Prophet 1.4.0 on all three trend types. The
[seasonality, events and regressors benchmarks](/benchmarks/features) show every case side by side.
:::

```ts
import { Effect } from "effect";
import { fit, predict, prophetFittingBackendLayer } from "effect-prophet";

const date = (day: number) => new Date(Date.UTC(2024, 0, 1 + day)).toISOString();

// Sales jump on sale days and stay a little higher the day after.
const saleDays = new Set(["2024-03-15", "2024-06-14", "2024-09-13"]);

const history = Array.from({ length: 300 }, (_, day) => {
  const today = date(day).slice(0, 10);
  const yesterday = date(day - 1).slice(0, 10);

  return {
    timestamp: date(day),
    value: 500 + day * 0.5 + (saleDays.has(today) ? 300 : 0) + (saleDays.has(yesterday) ? 100 : 0),
  };
});

const model = fit(history, {
  // List every occurrence: past ones to learn from, future ones to forecast.
  events: [
    { name: "flash-sale", date: "2024-03-15", upperWindowDays: 1 },
    { name: "flash-sale", date: "2024-06-14", upperWindowDays: 1 },
    { name: "flash-sale", date: "2024-09-13", upperWindowDays: 1 },
    { name: "flash-sale", date: "2024-12-13", upperWindowDays: 1 },
  ],
});

const forecast = await Effect.runPromise(
  Effect.gen(function* () {
    return yield* predict(yield* model, ["2024-12-12T00:00:00.000Z", "2024-12-13T00:00:00.000Z"]);
  }).pipe(Effect.provide(prophetFittingBackendLayer)),
);

for (const row of forecast) {
  const sale = row.events.find((event) => event.name === "flash-sale");
  const effect = sale?.mode === "additive" ? sale.value : 0;

  console.log(
    new Date(row.timestamp).toISOString().slice(0, 10),
    row.value.toFixed(0),
    effect.toFixed(0),
  );
}

```

```txt [Output]
2024-12-12 673 0
2024-12-13 974 300
```

The model learned that a flash sale adds about 300, and applied it to the upcoming sale on
December 13.

## Things to know

* **List every date, past and future.** The model learns from past dates and only applies the
  effect to future dates you list. Nothing is assumed to repeat on its own.
* **Use the same `name` for repeats.** All occurrences with the same name share one learned
  effect.
* **Dates are calendar days** in `YYYY-MM-DD` form.
* **Windows:** `lowerWindowDays: -2` also covers the two days before, and `upperWindowDays: 1`
  also covers the day after. Each day in the window gets its own learned effect.
* **`priorScale`** limits how large an event's effect can be. Lower it if an event has only a few
  past occurrences. It defaults to the model's `holidaysPriorScale`, which defaults to `10`.
* Set `holidaysMode: "multiplicative"` if event effects should be percentages instead of fixed
  amounts. It applies to every event, and defaults to `seasonalityMode`. Individual events don't
  accept their own `mode`.

## Country holidays

Add `countryHolidays: { country: "US" }` to fitting options to supplement custom events with
an offline country calendar. It works with linear, flat and logistic growth and uses `holidaysMode`.
Country dates have zero-day windows and a default prior of 10. Shared country/custom names must
use the same prior.

Fitting freezes the represented training years' feature layout; future predictions and uncertainty
use the selected country's embedded snapshot without changing columns. Saved models retain that
snapshot, so installing updated calendars never silently changes restored predictions.

Use `inspectCountryHolidays` to inspect known limitations before fitting. Known gaps are rejected
by default; `coverage: "allow-known-gaps"` explicitly accepts the provider's partial/empty result,
not a completeness promise. See [Country holiday calendars](/guide/holiday-calendars)
for the runnable example, pinned provider versions and 1995–2060 outer horizon.
