All free tools

Pine Script tool

Pine Script Session & Timezone Tool

Build a valid Pine Script session string, see what those exchange hours look like in your own timezone, and get the time filter to match. The day digits and the timezone default are the two things that quietly break most intraday scripts.

Days of the weekPine numbers days 1 to 7 starting at Sunday, which is off by one from most other systems. The digit is shown under each day.
Session string
0930-1600:23456
Days 23456 only.
In your timezone
13:30–20:00
America/New_York to UTC.
Exchange timeYour time
Session opens09:30 GMT-413:30 GMT+0
Session closes16:00 GMT-420:00 GMT+0

How Pine Script reads a session string

A session string has two segments. The first is a pair of times in twenty-four hour form with no colon, separated by a hyphen, so half past nine in the morning until four in the afternoon is written 0930-1600. The second segment is optional and lists which days the session applies to, separated from the times by a colon. Weekdays only becomes 0930-1600:23456.

The day numbering is the part that catches everyone. Pine counts from one to seven starting at Sunday, so Monday is 2 and Friday is 6. Most other systems either start at Monday or count from zero, which means a session string copied from another platform or written from memory is very often shifted by one day. The symptom is subtle: the filter works, it just admits Sunday and excludes Friday, and on a market that does not trade Sunday you may not notice for a long time.

When the times are equal, Pine treats the session as covering the whole day, which is how a twenty-four hour crypto session is expressed. When the end time is earlier than the start time, the session runs through midnight into the following day, which is normal for futures and worth being deliberate about, because a session that spans midnight also spans a day boundary in the day segment.

The timezone default is the real trap

Session strings are not evaluated in your timezone. Unless you say otherwise, they are interpreted in the exchange's timezone, which is what syminfo.timezone reports for the current symbol. This is almost always the behaviour you want, because market hours are a property of the market rather than of whoever is looking at the chart. It is also why a filter that appears to be an hour or five hours out is usually not broken at all: it is doing exactly what you asked, in a timezone you did not think about.

The conversion table above exists to make that concrete. If you are in London looking at a US equity, the regular session opens at half past two in the afternoon your time, and your intuition about what a nine-thirty filter means is not going to help you read the chart. Worse, the offset is not constant: the US and Europe change their clocks on different dates, so for a couple of weeks each year the gap is an hour different from the rest of the year. Writing the filter against exchange time sidesteps the whole problem, because the exchange is the thing whose hours you actually care about.

Passing syminfo.timezone explicitly as the third argument to time() is worth doing even though it matches the default. It documents the decision, and it means a reader does not have to know the default to understand the code. Hardcoding a specific zone such as America/New_York is occasionally correct, but only when you genuinely mean that zone regardless of the symbol on the chart, which is rarer than it seems.

What the filter is actually doing

The mechanism is easy to miss because it hides inside a function that looks like it returns a time. time(timeframe.period, session, timezone) returns the bar's timestamp when the bar falls inside the session, and na when it does not. The filter is therefore not a comparison but a test for absence: not na(...) is what turns that into a boolean you can combine with your own conditions.

Once you have that boolean, the transitions come for free. A bar where the session is active and the previous bar's was not is the session open, and the reverse is the close. Those two are usually what an intraday rule actually wants, because entering on every bar inside a window is a different rule from entering when the window begins.

One caveat about timeframes. A session filter is meaningful on intraday charts. On a daily or higher timeframe, a bar covers the whole session, so filtering by hours has no useful effect and a script that depends on it will behave in ways that look like a bug. If a filtered strategy produces identical results on daily bars regardless of the session, that is why.

The session filter in Pine Script

The code above is the whole pattern: an input.session() so the window is adjustable without editing the script, the time() call with the timezone stated explicitly, the na test that turns it into a boolean, and the two transition bars. Combining it with your own condition is then a single and.

Using input.session() rather than a plain string is a small decision with a real payoff. It gives you a session picker in the settings dialog, so you can test a different window without recompiling, and it makes the session visible to anyone using the script rather than buried in the source. Our guide to session and time filters covers the cases this page does not, including date-range filters and what happens across daylight saving changes.

Pine Script v6
//@version=6
indicator("Session filter", overlay = true)

// The session string built above. Times are in the exchange's timezone
// unless you pass a timezone argument to time().
sessionInput = input.session("0930-1600:23456", "Trading session")

// time() returns the bar's timestamp when the bar falls inside the
// session, and na when it does not. That na is the whole filter.
inSessionTime = time(timeframe.period, sessionInput, syminfo.timezone)
inSession     = not na(inSessionTime)

// Bars where the session has just started or is about to end.
sessionStarted = inSession and not inSession[1]
sessionEnding  = not inSession and inSession[1]

bgcolor(inSession ? color.new(color.teal, 92) : na, title = "In session")
plotshape(sessionStarted, "Session open", style = shape.triangleup, location = location.bottom)

// Combine the filter with your own condition. Both must hold.
longSignal = ta.crossover(ta.sma(close, 10), ta.sma(close, 30))
plotshape(longSignal and inSession, "Filtered signal", style = shape.circle, location = location.abovebar)

PineScripter is an AI built specifically for Pine Script. You describe what you want in plain English and it writes TradingView-ready v6 code. Because it is specialized on the Pine Script language and its exact function signatures, it tends to produce code that compiles far more reliably than general-purpose models like ChatGPT, which often invent functions that do not exist in Pine Script.

Related free calculators

From the blog

PineScripter is an AI developer tool that helps you write Pine Script code. It is not a financial advisor and will never offer financial, investment, or trading advice. Everything on this page, including the calculator and the explanations, is provided purely for educational and informational purposes. Any decision about how to interpret an indicator or trade a market is entirely your own. See our full disclaimer for more.