---
title: "Time and sessions"
description: "Read the current bar's clock and trading session from the time source: hour, day of week, day of month, month, year, and session predicates, all in UTC, all as…"
order: 13
section: "core-concepts"
---

<!-- source: docs/indicators/core-concepts/time-and-sessions.md; generated by packages/cli/scripts/gen-indicator-docs.ts, do not edit -->

# Time and sessions

Read the current bar's clock and trading session from the `time` source:
hour, day of week, day of month, month, year, and session predicates, all
in UTC, all as small functions you paste into the file. A wrun indicator
reads one number per bar, the bar's open time in epoch seconds, and
derives everything from it with integer math. The `./sdk/clock` module
does this for you
([Clock and sessions kit](../functions/time-and-sessions-kit.md)); this
page shows the math underneath.

## Introduction

Every bar happens at a moment in time, and that moment is often a signal in
itself: London opening, the Asian session winding down, a fresh day, a
weekend gap. An indicator reads that moment with one call:

```text
bar.time()   // the bar's open time, seconds since 1970-01-01 00:00 UTC
```

Three things to keep in mind:

- **It is seconds, not milliseconds.** `bar.time()` is seconds.
  Multiply by 1000 for a millisecond value, and keep the math in `i64` (a
  32-bit integer cannot hold an epoch second past 2038 comfortably, and it
  cannot hold milliseconds at all).
- **It is the bar's open.** Not the close, not "now". There is no wall
  clock inside the module: the newest bar is the live edge, and the host
  tells you nothing about the clock on the wall.
- **Times are UTC.** Every clock value and every session boundary below is
  in UTC. There is no local-timezone surprise, but it does mean you compare
  against UTC hours when you write a rule, and daylight-saving shifts in a
  venue's local session are yours to encode. A session the user should
  pick is the one exception: `param.session("rth", "09:30-16:00", { tz:
  "America/New_York" })` draws the window in the settings dialog, and
  `inSession(barOpenSec, start, end, zone)` applies the zone's
  daylight-saving rule per bar for you
  ([Sessions and units](../settings/sessions-and-units.md)).

Everything on this page is that integer math, pasted into your file: it
is what the kit computes underneath, shown so you can read it, and it is
enough on its own for a rule stated in UTC. When the rule lives in
a venue's own clock (the 09:30 cash open in New York, a daylight-saving
shift, the first bar of the week in Sydney), the `./sdk/clock` module on
the [Clock and sessions kit](../functions/time-and-sessions-kit.md) page keeps the
calendar for you: a `Clock` constructed with a zone name answers hour,
weekday, day of month and is-new-day, -week, -month with the zone's
daylight rule applied, and a `Session` answers whether the bar sits
inside an `0930-1600` window on the weekdays you list, half-open and
wrapping midnight. The same `Clock` counts the bar index and infers the
interval from the gap between opens, so the previous-`bar.time()`
subtraction below becomes one `intervalSec()` call.

## Lead example: the clock and the session

This file computes every clock value from the bar's open time, classifies
the bar into a session, and tints the close line by session so you can
sanity-check the math against the chart's time axis.

```typescript sample=cc-time-sessions
// The close, tinted by session: 0 off-hours grey, 1 Asia blue, 2 Europe amber, 3 America green.
output("close", line, overlay, { width: 2, color_by: "session", colors: ["#94a3b8", "#3b82f6", "#f59e0b", "#22c55e"], description: "Close, colored by the session the bar opened in" });
output("session", none, overlay, { description: "0 off-hours, 1 Asian, 2 European, 3 American (UTC)" });
output("hour", line, lower, { color: "#2563eb", description: "Hour of the bar, 0 to 23 UTC" });
output("day_of_week", line, lower, { color: "#7c3aed", description: "0 Sunday to 6 Saturday" });
output("day_of_month", line, lower, { color: "#16a34a", description: "Day of the month, 1 to 31" });
output("month", line, lower, { color: "#f59e0b", description: "Month, 1 to 12" });
output("year", line, lower, { color: "#94a3b8", description: "Four-digit year" });

// ---- Clock helpers over epoch seconds (UTC). Paste these into any file. ----
const DAY: i64 = 86400;

function hourOf(t: i64): i32 {
  return i32((t % DAY) / 3600);
}

function minuteOf(t: i64): i32 {
  return i32((t % 3600) / 60);
}

function dayIndex(t: i64): i64 {
  return t / DAY; // days since 1970-01-01
}

function dayOfWeek(t: i64): i32 {
  return i32((dayIndex(t) + 4) % 7); // 1970-01-01 was a Thursday: 0 Sunday .. 6 Saturday
}

// Civil date from a day index (integer math only; exact for every date after 1970).
let civilYear: i32 = 0;
let civilMonth: i32 = 0;
let civilDay: i32 = 0;

function civilFromDays(days: i64): void {
  const z = days + 719468;
  const era = (z >= 0 ? z : z - 146096) / 146097;
  const doe = z - era * 146097;
  const yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
  const y = yoe + era * 400;
  const doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
  const mp = (5 * doy + 2) / 153;
  const d = doy - (153 * mp + 2) / 5 + 1;
  const m = mp < 10 ? mp + 3 : mp - 9;
  civilYear = i32(m <= 2 ? y + 1 : y);
  civilMonth = i32(m);
  civilDay = i32(d);
}

// ---- Sessions (UTC). Edit the boundaries to taste; they are ordinary comparisons. ----
function isAsianSession(h: i32): bool {
  return h >= 0 && h < 8;
}

function isEuropeanSession(h: i32): bool {
  return h >= 7 && h < 16;
}

function isAmericanSession(h: i32): bool {
  return h >= 13 && h < 21;
}

function currentSession(h: i32): f64 {
  if (isAmericanSession(h)) return 3.0; // overlaps resolve to the later session
  if (isEuropeanSession(h)) return 2.0;
  if (isAsianSession(h)) return 1.0;
  return 0.0;
}

function onBar(): void {
  const t = i64(bar.time());
  const hour = hourOf(t);
  const dow = dayOfWeek(t);
  civilFromDays(dayIndex(t));
  const session = currentSession(hour);
  out_close(bar.close());
  out_session(session);
  out_hour(f64(hour));
  out_day_of_week(f64(dow));
  out_day_of_month(f64(civilDay));
  out_month(f64(civilMonth));
  out_year(f64(civilYear));
}
```

Each session predicate is a plain `bool` over the hour, so it drops
straight into an `if` or a gate. The close line picks up a blue, amber, or
green tint depending on which session the bar opened in, and the lower
pane shows the raw clock values so you can check them against the axis.

## Function reference

Every helper takes the bar's open time as an `i64` of epoch seconds and
returns the value for the **current bar**.

### Clock

| Helper | Returns |
| --- | --- |
| `hourOf(t)` | Hour of the bar, `0`..`23` (UTC) |
| `minuteOf(t)` | Minute of the bar, `0`..`59` |
| `i32(t % 60)` | Second of the bar, `0`..`59` (always `0` on candle grids) |
| `dayOfWeek(t)` | `0` Sunday .. `6` Saturday |
| `civilDay` after `civilFromDays(dayIndex(t))` | Day of the month, `1`..`31` |
| `civilMonth` | Month, `1`..`12` |
| `civilYear` | Four-digit year |

`civilFromDays` is the standard days-to-civil algorithm in integer math,
exact for every date from 1970 onward; it writes three module-level
variables because a function returns one value. Prefer `dayIndex(t)` for
"is this a new day" tests; the civil fields are for calendar rules and
labels.

### Timestamps

| Need | wrun |
| --- | --- |
| the bar's open as a timestamp | `bar.time()` (seconds); `i64(bar.time()) * 1000` for milliseconds |
| the wall clock | none: no wall clock reaches the module. The newest bar is the live edge, and a run-level renderer or drawing evaluates it on its own |

The bar's interval is the difference between consecutive open times: keep
the previous `bar.time()` in a module-level variable and subtract.

### Sessions

| Helper | Returns |
| --- | --- |
| `isAsianSession(h)` | `true` in the Asian session (00:00 to 08:00 UTC in the example) |
| `isEuropeanSession(h)` | `true` in the European session (07:00 to 16:00 UTC) |
| `isAmericanSession(h)` | `true` in the American session (13:00 to 21:00 UTC) |
| `currentSession(h)` | `0` off-hours, `1`, `2`, `3`, as a number an output or a `color_by` ladder can carry |

The boundaries are ordinary comparisons in your file, so they are yours:
an indicator states them where you can read and change them. Sessions
overlap (London and New York share the
13:00 to 16:00 UTC hours), which is why the predicates are independent and
`currentSession` picks one by priority.

## The market's own calendar

The integer math above answers questions in UTC. Two more `time` fields
carry the market's own calendar, so an indicator can ask what the venue
says about a bar without knowing its schedule:

```text
input("day", time.trade_date);   // the bar's trade date: epoch seconds at 00:00 UTC of that date
input("phase", time.session);    // 1 regular, 2 pre-market, 3 after-hours, 0 closed
```

The trade date is the exchange's: on CME futures it starts at the 17:00
Chicago open, so an evening bar already belongs to the next date; on US
stocks it is the New York date; on markets without sessions (crypto,
forex, HIP-3, Polymarket) it is the UTC date and every bar reads `1`,
regular. On a chart of one day or longer every bar takes its own stamp's
date and reads `1` on every market, since the chart stamps daily, weekly
and monthly bars at 00:00 UTC of their trade date. Both are facts of the
market, the bar and its interval alone, so switching the chart to extended
hours never changes them ([Data sources](data-sources.md)).

`MarketSession` from `./sdk/clock` folds the two facts once per bar and
answers the checks a Pine script reads from `session.*`
([Clock and sessions kit](../functions/time-and-sessions-kit.md)):

```text
const ms = new MarketSession();   // made once, at module level or in onStart()
// onBar():
ms.update(in_day(), in_phase());
if (ms.isFirstRegularBar()) {
  // the cash open: anchor a VWAP, start the day's range
}
```

Group daily, weekly and monthly views by `tradingDayKey()`,
`tradingWeekKey()` and `tradingMonthKey()` instead of the UTC day index:
on a CME chart the UTC day cuts the evening session off the trade date it
belongs to.

### Pine equivalents

| Pine | wrun |
| --- | --- |
| `session.ismarket` | `ms.isRegular()` |
| `session.ispremarket` | `ms.isPremarket()` |
| `session.ispostmarket` | `ms.isAfterHours()` |
| `session.isfirstbar` | `ms.isNewTradingDay()` |
| `session.isfirstbar_regular` | `ms.isFirstRegularBar()` |
| `time_tradingday` | `in_day()` from a `time.trade_date` input, in seconds (Pine's is milliseconds: multiply by 1000) |
| `timeframe.change("D")` | `ms.isNewTradingDay()` |

`ms.isExtended()` is pre-market or after-hours, and `ms.isClosed()` marks
a bar printed while the market is closed. Like every check on this page,
they read the bar the module is on: on the first bar the chart loads, a
session already running counts as new.

## Common patterns

**Only signal during a session.** Wrap your trigger in a session predicate
so it can only fire when the market you care about is active. The predicate
becomes a data-only gate and the mark's `shape_where`:

```text
const brokeOut = close > prevHigh && isEuropeanSession(hour);
out_london_break(high);                          // a shape output at the bar's high
out_is_london_break(brokeOut ? 1.0 : 0.0);       // its shape_where gate
```

**Gate by session instead of weekday.** `dayOfWeek(t)` works here, so both
are available: `dow >= 1 && dow <= 5` is weekdays, and
`isEuropeanSession(hour) || isAmericanSession(hour)` is "skip the quiet
Asian hours." Combine them freely.

**Once-per-day reset.** Detect a new day by watching the day index change
from the previous bar, then reset whatever daily accumulator you keep. The
cookbook's anchored VWAP and key levels are built on exactly this:

```text
const day = dayIndex(t);
if (day != dayIdx) {
  // the day that just closed is complete; the running sums start over
  dayIdx = day;
  dayPv = 0.0;
  dayVol = 0.0;
}
```

The same idea works for a new week (`(dayIndex(t) + 3) / 7` changes; the
`+ 3` makes weeks start on Monday 00:00 UTC), a new month (`civilMonth`
changes), or a new year (`civilYear` changes). Hold the previous index in
a module-level variable so you can compare, and remember that a period
already running when the loaded history starts is incomplete: keep its
values `NaN` until the first boundary passes.

**Weekend gaps.** Crypto trades through; a venue with a session calendar
simply has no bars off-hours, and the `time` source follows the primary
grid, so there is nothing to skip. The bucket fold that turns these day and
hour indexes into higher-timeframe views is `multi-timeframe.md`; the
per-bar candle tint is `render.barcolor`, and
the background tint beside it is `render.bgcolor`
(`presentation/plotting.md`).
