> For the complete documentation index, see [llms.txt](https://docs.membercounter.app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.membercounter.app/counters-reference/countdown.md).

# Countdown

Create a countdown timer with a format tailored to your needs.

Shows how much time is left until a date you set. Write the format yourself, for example `%d days left`.

**Availability:** every bot, no API key. Only the update cadence differs: every 10 minutes on the public bot, every 5 minutes on a [legacy premium](/custom-bot/legacy-premium-vs-custom-bot.md), [custom](/custom-bot/custom-bot.md) or [self-hosted](/self-hosting/self-hosting.md) bot. See [Configuration](/self-hosting/configuration.md).

<figure><img src="https://1582170640-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lu2S5r7lKtu4IK5ucu9%2Fuploads%2Fgit-blob-8e227f45760314b8ae7230448c85cdb08dfd1f38%2Fcounter-countdown-options.png?alt=media" alt="Countdown options"><figcaption><p>The two options of the Countdown counter, with the <strong>Live preview</strong> underneath</p></figcaption></figure>

## Options

| Option            | What it does                                                          | Notes                                                                                                                                                                                                                                                                                                                             |
| ----------------- | --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Target date**   | The moment the countdown runs to.                                     | A date-and-time field, or a [nested counter](/using-the-dashboard/nested-counters.md) picked in the box underneath. A new counter starts one hour ahead. There is no timezone option: the field is read in your browser's timezone and stored as one absolute moment, so everybody in the server counts down to the same instant. |
| **Format**        | The text the counter prints, with the placeholders below substituted. | Free text. The default is `%d days, %h hours and %m minutes left`. Also takes a nested counter that returns a formatting string.                                                                                                                                                                                                  |
| **Display label** | Renames the counter's chip in the editor.                             | Cosmetic. See [Formatting counters](/using-the-dashboard/customizing-counters.md#display-label).                                                                                                                                                                                                                                  |

{% hint style="warning" %}
A counter feeding **Target date** must return a UNIX timestamp in **milliseconds**. A seconds-based timestamp resolves to 1970, so the countdown shows all zeros; multiply it by 1000 with a [Math](/counters-reference/math.md) counter first.
{% endhint %}

### Placeholders

| Placeholder | What it does        | Example   |
| ----------- | ------------------- | --------- |
| `%d`        | Days left           | `12`      |
| `%h`        | Hours left (0-23)   | `3`       |
| `%m`        | Minutes left (0-59) | `25`      |
| `%s`        | Seconds left (0-59) | `0`       |
| `%D`        | Total days left     | `12`      |
| `%H`        | Total hours left    | `291`     |
| `%M`        | Total minutes left  | `17485`   |
| `%S`        | Total seconds left  | `1049100` |

The table shows one example duration. The lowercase placeholders are the parts of one duration; the uppercase ones give the whole time in a single unit. **Format** prints the rest as you type it, with no zero padding: three hours prints as `3`, never `03`. Removing **Format** with its **X** button does not put the default back: a counter with no format prints `%d:%h:%m`, so `12:3:25`. Type the default in again if you want it.

## What it returns

Text: your **Format** string with every placeholder replaced by a plain whole number. Once the target date has passed the counter holds at `0 days, 0 hours and 0 minutes left`. It does not count up and it does not remove itself, so change the date or delete the counter when the event is over. A voice or category channel name still has to end up at least 2 characters long; see [Channel settings](/using-the-dashboard/channel-settings.md).

## Formatting

**Format settings** does nothing here: the counter returns plain digits, whatever the locale. To write it in another language, type the words into **Format**, for example `quedan %d días`.

## Refresh and caching

Nothing is cached, so the countdown uses the exact current time. What limits it is the update cycle: the value jumps in 5- or 10-minute steps, which is why `%s` and `%S` are not much use in a channel. See [How counters update](/using-the-dashboard/how-counters-update.md).

## Examples

{% tabs %}
{% tab title="Picking a format" %}
**Target date** 1 October 2026 at 18:00 with the default **Format** gives `12 days, 3 hours and 25 minutes left`.

<figure><img src="https://1582170640-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lu2S5r7lKtu4IK5ucu9%2Fuploads%2Fgit-blob-1590da0d49de808749a7fbf1e1e4fd289ad50079%2Fcounter-countdown-example-default.png?alt=media" alt="A channel topic with the default countdown format"><figcaption><p>The default format in a text channel topic</p></figcaption></figure>

Delete the placeholders and words you do not want. `🎉 Event in %d days` gives `🎉 Event in 12 days`: short enough for a voice channel name, and it only changes once a day. For a single unit in full, `%H hours to launch` gives `291 hours to launch`.

<figure><img src="https://1582170640-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lu2S5r7lKtu4IK5ucu9%2Fuploads%2Fgit-blob-42e91d2d6194ba40339ceb316895f9f0e288b5ae%2Fcounter-countdown-example-days-only.png?alt=media" alt="A voice channel counting down in days"><figcaption><p>A days-only countdown in a voice channel name</p></figcaption></figure>
{% endtab %}

{% tab title="Date from another counter" %}
Pick a counter in the box under **Target date** instead of typing a date. An [HTTP](/counters-reference/http.md) counter that returns the next event time keeps the countdown correct without anyone editing the template. If your endpoint returns seconds, put a [Math](/counters-reference/math.md) counter in between and multiply by `1000`. See [Nested counters](/using-the-dashboard/nested-counters.md).

<figure><img src="https://1582170640-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-Lu2S5r7lKtu4IK5ucu9%2Fuploads%2Fgit-blob-d689cab25dbb3a90942dd284bae3c37467706b55%2Fcounter-countdown-example-nested-date.png?alt=media" alt="A Countdown counter taking its date from another counter"><figcaption><p>The date comes from a nested counter, so the date field is replaced</p></figcaption></figure>
{% endtab %}
{% endtabs %}

## Errors

Countdown has no fallback. A missing target date, or one that has passed, counts down to zero and stays there rather than failing. More in [Troubleshooting](/using-the-dashboard/troubleshooting.md).

| Message                                                         | What to do                                                                                                                                                                                                            |
| --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `NaN days, NaN hours and NaN minutes left`                      | The counter in **Target date** returned something other than a number, such as an ISO date or an empty response. Feed it milliseconds and nothing else.                                                               |
| The result for the channel name is less than 2 characters long. | Put some text around the counter.                                                                                                                                                                                     |
| **ERROR!** in the channel name                                  | A nested counter failed outright, for example an [HTTP](/counters-reference/http.md) counter pointed at a dead URL. The channel page names the reason under **The template had an error when it was last processed**. |

<details>

<summary>Technical details</summary>

* Evaluator: `packages/services/src/DataSource/DataSourceEvaluator/evaluators/countdown.ts`. It computes `new Date(date - Date.now())` and replaces it with `new Date(0)` once the target has passed, which is what holds every placeholder at zero.
* Unlike [Clock](/counters-reference/clock.md) and [Date](/counters-reference/date.md) it does not round the instant: it reads `Date.now()` straight.
* Every placeholder is a whole number, rounded down. `%d` and `%D` are the same value by design: the days left. `%h`, `%m` and `%s` are the hours, minutes and seconds left after the larger units are taken out; `%H`, `%M` and `%S` are the whole remainder in hours, minutes and seconds.
* The numbers are interpolated as plain strings, never through `Intl.NumberFormat`, so the locale, **Use compact notation** and **Override default digits** have nothing to act on.
* `options.date` defaults to `0` when absent, which is 1 January 1970, an already-expired countdown. A nested counter that returns anything but a number makes `date - Date.now()` evaluate to `NaN`, and every placeholder prints `NaN`.
* The stored value is a millisecond epoch timestamp. The date-and-time field is converted with the browser's offset *now*, not the one in force on the target date, so a target across a daylight-saving change lands an hour away from what was typed. The chip shows the stored moment converted back, so compare the two.
* The editor writes the default format into the counter on the first change made in the panel. A counter carrying no format at all, such as one migrated from the old bot, hits the evaluator's `options.format ?? "%d:%h:%m"`.
* Each top-level counter in a template has a 60 second timeout (`EVALUATION_TIMEOUT`), covering its nested counters too. The result is cut to 99 characters for voice and category names, 1023 for text and announcement topics, and a name under 2 characters throws `EVALUATION_RESULT_FOR_CHANNEL_NAME_IS_LESS_THAN_2_CHARACTERS`.
* On a failed evaluation the update job writes **ERROR!** into the channel and stores the message as the channel's `LastTemplateComputeError`.
* The `Update channels` job runs on cron `0 */5 * * * *` when `DISCORD_BOT_INSTANCE_IS_PREMIUM` is `true`, and on `0 */10 * * * *` when it is `false`. The shipped `.env.example` sets it to `true`. No other environment variable affects Countdown.
* There is no live preview while **Target date** or **Format** is empty. There is none either while one of them is filled by a counter, and then the whole template has to be previewed instead.

</details>
