Time
Introduction
In Pine Script™, the following key aspects apply when working with date and time values:
- UNIX timestamp: The native format for time values in Pine, representing the absolute number of milliseconds elapsed since midnight UTC on 1970-01-01. Several built-ins return UNIX timestamps directly, which users can format into readable dates and times. See the UNIX timestamps section below for more information.
- Exchange time zone: The time zone of the instrument’s exchange. All calendar-based variables hold values expressed in the exchange time zone, and all built-in function overloads that have a
timezone
parameter use this time zone by default. - Chart time zone: The time zone the chart and Pine Logs message prefixes use to express time values. Users can set the chart time zone using the “Timezone” input in the “Symbol” tab of the chart’s settings. This setting only changes the display of dates and times on the chart and the times that prefix logged messages. It does not affect the behavior of Pine scripts because they cannot access a chart’s time zone information.
timezone
parameter: A “string” parameter of time-related functions that specifies the time zone used in their calculations. For calendar-based functions, such as dayofweek(), thetimezone
parameter determines the time zone of the returned value. For functions that return UNIX timestamps, such as time(), the specifiedtimezone
defines the time zone of other applicable parameters, e.g.,session
. See the Time zone strings section to learn more.
UNIX timestamps
UNIX time is a standardized date and time representation that measures the number of non-leap seconds elapsed since January 1, 1970 at 00:00:00 UTC (the UNIX Epoch), typically expressed in seconds or smaller time units. A UNIX time value in Pine Script™ is an “int” timestamp representing the number of milliseconds from the UNIX Epoch to a specific point in time.
Because a UNIX timestamp represents the number of consistent time units elapsed from a fixed historical point (epoch), its value is time zone-agnostic. A UNIX timestamp in Pine always corresponds to the same distinct point in time, accurate to the millisecond, regardless of a user’s location.
For example, the UNIX timestamp 1723472500000
always represents the time 1,723,472,500,000 milliseconds (1,723,472,500 seconds) after the UNIX Epoch. This timestamp’s meaning does not change relative to any time zone.
To format an “int” UNIX timestamp into a readable date/time “string” expressed in a specific time zone, use the str.format_time() function. The function does not modify UNIX timestamps. It simply represents timestamps in a desired human-readable format.
For instance, the function can represent the UNIX timestamp 1723472500000
as a “string” in several ways, depending on its format
and timezone
arguments, without changing the absolute point in time that it refers to. The simple script below calculates three valid representations of this timestamp and displays them in the Pine Logs pane:
Note that:
- The value enclosed within square brackets in the logged message is an automatic prefix representing the historical time of the log.info() call in ISO 8601 format, expressed in the chart time zone.
See the Formatting dates and times section to learn more about representing UNIX timestamps with formatted strings.
Time zones
A time zone is a geographic region with an assigned local time. The specific time within a time zone is consistent throughout the region. Time zone boundaries typically relate to a location’s longitude. However, in practice, they tend to align with administrative boundaries rather than strictly following longitudinal lines.
The local time within a time zone depends on its defined offset from Coordinated Universal Time (UTC), which can range from UTC-12:00 (12 hours behind UTC) to UTC+14:00 (14 hours ahead of UTC). Some regions maintain a consistent offset from UTC, and others have an offset that changes over time due to daylight saving time (DST) and other factors.
Two primary time zones apply to data feeds and TradingView charts: the exchange time zone and the chart time zone.
The exchange time zone represents the time zone of the current symbol’s exchange, which Pine scripts can access with the syminfo.timezone variable. Calendar-based variables, such as month, dayofweek, and hour, always hold values expressed in the exchange time zone, and all time function overloads that have a timezone
parameter use this time zone by default.
The chart time zone is a visual preference that defines how the chart and the time prefixes of Pine Logs represent time values. To set the chart time zone, use the “Timezone” input in the “Symbol” tab of the chart’s settings or click on the current time shown below the chart. The specified time zone does not affect time calculations in Pine scripts because they cannot access this chart information. Although scripts cannot access a chart’s time zone, programmers can provide inputs that users can adjust to match the time zone.
For example, the script below uses str.format_time() to represent the UNIX timestamps of the last historical bar’s opening time and closing time as date-time strings, expressed in the function’s default time zone, the exchange time zone, UTC-0, and a user-specified time zone. It displays all four representations for comparison within a table in the bottom-right corner of the chart:
Note that:
- The label on the chart signifies which bar’s times the displayed strings represent.
- The “Default” and “Exchange” rows in the table show identical results because syminfo.timezone is the str.format_time() function’s default
timezone
argument. - The exchange time zone on our example chart appears as
"America/New_York"
, the IANA identifier for the NASDAQ exchange’s time zone. It represents UTC-4 or UTC-5, depending on the time of year. See the next section to learn more about time zone strings.
Time zone strings
All built-in functions with a timezone
parameter accept a “string” argument specifying the time zone they use in their calculations. These functions can accept time zone strings in either of the following formats:
- UTC (or GMT) offset notation, e.g.,
"UTC-5"
,"UTC+05:30"
,"GMT+0100"
- IANA database notation, e.g.,
"America/New_York"
,"Asia/Calcutta"
,"Europe/Paris"
The IANA time zone database reference page lists possible time zone identifiers and their respective UTC offsets. The listed identifiers are valid as timezone
arguments.
Note that various time zone strings expressed in UTC or IANA notation can represent the same offset from Coordinated Universal Time. For instance, these strings all represent a local time three hours ahead of UTC:
"UTC+3"
"GMT+03:00"
"Asia/Kuwait"
"Europe/Moscow"
"Africa/Nairobi"
For the str.format_time() function and the functions that calculate calendar-based values from a UNIX timestamp, including month(), dayofweek(), and hour(), the “string” passed to the timezone
parameter changes the returned value’s calculation to express the result in the specified time zone. See the Formatting dates and times and Calendar-based functions sections for more information.
The example below shows how time zone strings affect the returned values of calendar-based functions. This script uses three hour() function calls to calculate “int” values representing the opening hour of each bar in the exchange time zone, UTC-0, and a user-specified UTC offset. It plots all three calculated hours in a separate pane for comparison:
Note that:
- The
exchangeHour
value is four or five hours behind theutcHour
because the NASDAQ exchange is in the “America/New_York” time zone. This time zone has a UTC offset that changes during the year due to daylight saving time (DST). The script’s defaultcustomOffsetHour
is consistently four hours ahead of theutcHour
because its time zone is UTC+4. - The call to the hour() function without a specified
timezone
argument returns the same value that the hour variable holds because both represent the bar’s opening hour in the exchange time zone (syminfo.timezone).
For functions that return UNIX timestamps directly, such as time() and timestamp(), the timezone
parameter defines the time zone of the function’s calendar-based parameters, including session
, year
, month
, day
, hour
, minute
, and second
. The parameter does not determine the time zone of the returned value, as UNIX timestamps are time zone-agnostic. See the Testing for sessions and `timestamp()` sections to learn more.
The following script calls the timestamp() function to calculate the UNIX timestamp of a specific date and time, and it draws a label at the timestamp’s corresponding bar location. The user-selected timezone
argument (timezoneInput
) determines the time zone of the call’s calendar-based arguments. Consequently, the calculated timestamp varies with the timezoneInput
value because identical local times in various time zones correspond to different amounts of time elapsed since the UNIX Epoch:
Note that:
"Etc/UTC"
is the IANA identifier for the UTC+0 time zone.- The label.new() call uses xloc.bar_time as its
xloc
argument, which is required to anchor the drawing to an absolute time value. Without this argument, the function treats theunixTimestamp
as a relative bar index, leading to an incorrect location. - The label’s
y
value is the close of the bar where the opening time crosses theunixTimestamp
. If the timestamp represents a future time, the label uses the last historical bar’s price.
Although time zone strings can use either UTC or IANA notation, we recommend using IANA notation for timezone
arguments in most cases, especially if a script’s time calculations must align with the observed time offset in a specific country or subdivision. When a time function call uses an IANA time zone identifier for its timezone
argument, its calculations adjust automatically for historical and future changes to the specified region’s observed time, such as daylight saving time (DST) and updates to time zone boundaries, instead of using a fixed offset from UTC.
The following script demonstrates how UTC and IANA time zone strings can affect time calculations differently. It uses two calls to the hour() function to calculate the hour from the current bar’s opening timestamp using "UTC-4"
and "America/New_York"
as timezone
arguments. The script plots the results of both calls for comparison and colors the main pane’s background when the returned values do not match. Although these two hour() calls may seem similar because UTC-4 is an observed UTC offset in New York, they do not always return the same results, as shown below:
The plots in the chart above diverge periodically because New York observes daylight saving time, meaning its UTC offset changes at specific points in a year. During DST, New York’s local time follows UTC-4. Otherwise, it follows UTC-5. Because the script’s first hour() call uses "UTC-4"
as its timezone
argument, it returns the correct hour in New York only during DST. In contrast, the call that uses the "America/New_York"
time zone string adjusts its UTC offset automatically to return the correct hour in New York at any time of the year.
Time variables
Pine Script™ has several built-in variables that provide scripts access to different forms of time information:
- The time and time_close variables hold UNIX timestamps representing the current bar’s opening and closing times, respectively.
- The time_tradingday variable holds a UNIX timestamp representing the starting time of the last UTC calendar day in a session.
- The timenow variable holds a UNIX timestamp representing the current time when the script executes.
- The year, month, weekofyear, dayofmonth, dayofweek, hour, minute, and second variables reference calendar values based on the current bar’s opening time, expressed in the exchange time zone.
- The last_bar_time variable holds a UNIX timestamp representing the last available bar’s opening time.
- The chart.left_visible_bar_time and chart.right_visible_bar_time variables hold UNIX timestamps representing the opening times of the leftmost and rightmost visible chart bars.
- The syminfo.timezone variable holds a “string” value representing the time zone of the current symbol’s exchange in IANA database notation. All time-related function overloads with a
timezone
parameter use this variable as the default argument.
`time` and `time_close` variables
The time variable holds the UNIX timestamp of the current bar’s opening time, and the time_close variable holds the UNIX timestamp of the bar’s closing time.
These timestamps are unique, time zone-agnostic “int” values, which programmers can use to anchor drawing objects to specific bar times, calculate and inspect bar time differences, construct readable date/time strings with the str.format_time() function, and more.
The script below displays bar opening and closing times in different ways. On each bar, it formats the time and time_close timestamps into strings containing the hour, minute, and second in the exchange time zone, and it draws labels displaying the formatted strings at the open and close prices. Additionally, the script displays strings containing the unformatted UNIX timestamps of the last chart bar within a table in the bottom-right corner:
Note that:
- This script’s label.new() calls include xloc.bar_time as the
xloc
argument and time as thex
argument to anchor the drawings to bar opening times. - The formatted strings express time in the exchange time zone because we did not specify
timezone
arguments in the str.format_time() calls. NYSE, our chart symbol’s exchange, is in the “America/New_York” time zone (UTC-4/-5). - Although our example chart uses an hourly timeframe, the table and the labels at the end of the chart show that the last bar closes only 30 minutes (1,800,000 milliseconds) after opening. This behavior occurs because the chart aligns bars with session opening and closing times. A session’s final bar closes when the session ends, and a new bar opens when a new session starts. A typical session on our 60-minute chart with regular trading hours (RTH) spans from 09:30 to 16:00 (6.5 hours). The chart divides this interval into as many 60-minute bars as possible, starting from the session’s opening time, which leaves only 30 minutes for the final bar to cover.
It’s crucial to note that unlike the time variable, which has consistent behavior across chart types, time_close behaves differently on time-based and non-time-based charts.
Time-based charts have bars that typically open and close at regular, predictable times within a session. Thanks to this predictability, time_close can accurately represent the expected closing time of an open bar on a time-based chart, as shown on the last bar in the example above.
In contrast, the bars on tick charts and price-based charts (all non-standard charts excluding Heikin Ashi) cover irregular time intervals. Tick charts construct bars based on successive ticks in the data feed, and price-based charts construct bars based on significant price movements. The time it takes for new ticks or price changes to occur is unpredictable. As such, the time_close value is na on the realtime bars of these charts.
The following script uses the time and time_close variables with str.tostring() and str.format_time() to create strings containing bar opening and closing UNIX timestamps and formatted date-time representations, which it displays in labels at each bar’s high and low prices.
When applied to a Renko chart, which forms new bars based on price movements, the labels show correct results on all historical bars. However, the last bar has a time_close value of na because the future closing time is unpredictable. Consequently, the bar’s closing time label shows a timestamp of "NaN"
and an incorrect date and time:
Note that:
- The script draws up to 50 labels because we did not specify a
max_labels_count
argument in the indicator() declaration statement. - The str.format_time() function replaces na values with 0 in its calculations, which is why it returns an incorrect date-time “string” on the last bar. A timestamp of 0 corresponds to the UNIX Epoch (00:00:00 UTC on January 1, 1970). However, the str.format_time() call does not specify a
timezone
argument, so it expresses the epoch’s date and time in the exchange time zone, which was five hours behind UTC at that point in time. - The time_close() function, which returns the closing timestamp of a bar on a specified timeframe within a given session, also returns na on the realtime bars of tick-based and price-based charts.
`time_tradingday`
The time_tradingday variable holds a UNIX timestamp representing the starting time (00:00 UTC) of the last trading day in the current bar’s final session. It is helpful primarily for date and time calculations on time-based charts for symbols with overnight sessions that start and end on different calendar days.
On “1D” and lower timeframes, the time_tradingday timestamp corresponds to the beginning of the day when the current session ends, even for bars that open and close on the previous day. For example, the “Monday” session for “EURUSD” starts on Sunday at 17:00 and ends on Monday at 17:00 in the exchange time zone. The time_tradingday values of all intraday bars within the session represent Monday at 00:00 UTC.
On timeframes higher than “1D”, which can cover multiple sessions, time_tradingday holds the timestamp representing the beginning of the last calendar day of the bar’s final trading session. For example, on a “EURUSD, 1W” chart, the timestamp represents the start of the last trading day in the week, which is typically Friday at 00:00 UTC.
The script below demonstrates how the time_tradingday and time variables differ on Forex symbols. On each bar, it draws labels to display strings containing the variables’ UNIX timestamps and formatted dates and times. It also uses the dayofmonth() function to calculate the UTC calendar day from both timestamps, highlighting the background when the calculated days do not match.
When applied to the “FXCM:EURUSD” chart with the “3h” (“180”) timeframe, the script highlights the background of the first bar in each session, as each session opens on the previous calendar day. The dayofmonth() call that uses time calculates the opening day on the session’s first bar, whereas the call that uses time_tradingday calculates the day when the session ends:
Note that:
- The str.format_time() and dayofmonth() calls use
"UTC+0"
as thetimezone
argument, meaning the results represent calendar time values with no offset from UTC. In the screenshot, the first bar opens at 21:00 UTC, 17:00 in the exchange time zone (“America/New_York”). - The formatted strings show
"GMT"
as the acronym of the time zone, which is equivalent to"UTC+0"
in this context. - The time_tradingday value is the same for all three-hour bars within each session, even for the initial bar that opens on the previous UTC calendar day. The assigned timestamp changes only when a new session starts.
`timenow`
The timenow variable holds a UNIX timestamp representing the script’s current time. Unlike the values of other variables that hold UNIX timestamps, the values in the timenow series correspond to times when the script executes, not the times of specific bars or trading days.
A Pine script executes only once per historical bar, and all historical executions occur when the script first loads on the chart. As such, the timenow value is relatively consistent on historical bars, with only occasional millisecond changes across the series. In contrast, on realtime bars, a script executes once for each new update in the data feed, which can happen several times per bar. With each new execution, the timenow value updates on the latest bar to represent the current time.
This variable is most useful on realtime bars, where programmers can apply it to track the times of the latest script executions, count the time elapsed within open bars, control drawings based on bar updates, and more.
The script below inspects the value of timenow on the latest chart bars and uses it to analyze realtime bar updates. When the script first reaches the last chart bar, it declares three variables with the varip keyword to hold the latest timenow value, the total time elapsed between the bar’s updates, and the total number of updates. It uses these values to calculate the average number of milliseconds between updates, which it displays in a label along with the current execution’s timestamp, a formatted time and date in the exchange time zone, and the current number of bar updates:
Note that:
- When a bar is unconfirmed (open), the label is blue to signify that additional updates can occur. After the bar is confirmed (closed), the label turns gray.
- Although we’ve set the chart time zone to match the exchange time zone, the formatted time in the open bar’s label and the time shown below the chart do not always align. The script records a new timestamp only when a new execution occurs, whereas the time below the chart updates continuously.
- The varip keyword specifies that a variable does not revert to the last committed value in its series when new updates occur. This behavior allows the script to use variables to track changes in timenow on an open bar.
- Updates to timenow on open realtime bars do not affect the recorded timestamps on confirmed bars as the script executes. However, the historical series changes (repaints) after reloading the chart because timenow references the script’s current time, not the times of specific bars.
Calendar-based variables
The year, month, weekofyear, dayofmonth, dayofweek, hour, minute, and second variables hold calendar-based “int” values calculated from the current bar’s opening time, expressed in the exchange time zone. These variables reference the same values that calendar-based functions return when they use the default timezone
argument and time as the time
argument. For instance, the year variable holds the same value that a year(time) call returns.
Programmers can use these calendar-based variables for several purposes, such as:
- Identifying a bar’s opening date and time.
- Passing the variables to the timestamp() function to calculate UNIX timestamps.
- Testing when date/time values or ranges occur in a data feed.
One of the most common use cases for these variables is checking for date or time ranges to control when a script displays visuals or executes calculations. This simple example inspects the year variable to determine when to plot a visible value. If the year is 2022 or higher, the script plots the bar’s close. Otherwise, it plots na:
When using these variables in conditions that isolate specific dates or times rather than ranges, it’s crucial to consider that certain conditions might not detect some occurrences of the values due to a chart’s timeframe, the opening times of chart bars, or the symbol’s active session.
For instance, suppose we want to detect when the first calendar day of each month occurs on the chart. Intuitively, one might consider simply checking when the dayofmonth value equals 1. However, this condition only identifies when a bar opens on a month’s first day. The bars on some charts can open and close in different months. Additionally, a chart bar might not contain the first day of a month if the market is closed on that day. Therefore, we must create extra conditions that work in these scenarios to identify the first day in any month on the chart.
The script below uses the dayofmonth and month variables, and the month() function, to create three conditions that detect the first day of the month in different ways. The first condition detects if the bar opens on the first day, the second checks if the bar opens in one month and closes in another, and the third checks if the chart skips the date entirely. The script draws labels showing bar opening dates and highlights the background with different colors to visualize when each condition occurs:
Note that:
- The script calls the month() function with time_close as the
time
argument to calculate each bar’s closing month for thecontainsFirst
condition. - The
dayofweek.*
namespace contains variables that hold each possible dayofweek value, e.g., dayofweek.sunday has a constant value of 1 and dayofweek.saturday has a constant value of 7. The script compares dayofweek to these variables in a switch structure to determine the weekday name shown inside each label. - To detect the first opening time in a monthly timeframe, not strictly the first day in a calendar month, use
ta.change(time("1M")) > 0
ortimeframe.change("1M")
instead of conditions based on these variables. See the Testing for changes in higher timeframes section to learn more.
`last_bar_time`
The last_bar_time variable holds a UNIX timestamp representing the last available bar’s opening time. It is similar to last_bar_index, which references the latest bar index. On historical bars, last_bar_time consistently references the time value of the last bar available when the script first loads on the chart. The only time the variable’s value updates across script executions is when a new realtime bar opens.
The following script uses the last_bar_time variable to get the opening timestamp of the last chart bar during its execution on the first bar. It displays the UNIX timestamp and a formatted date and time in a single-cell table created only on that bar. When the script executes on the last available bar, it draws a label showing the bar’s time value and its formatted representation for visual comparison.
As the chart below shows, both drawings display identical times, verifying that last_bar_time correctly references the last bar’s time value on previous historical bars:
Note that:
- The script creates the label only on the first bar with the barstate.islast state because that bar’s time value is what last_bar_time equals on all historical bars. On subsequent bars, the last_bar_time value updates to represent the latest realtime bar’s opening time.
- Updates to last_bar_time on realtime bars do not affect the values on historical bars as the script executes. However, the variable’s series repaints when the script restarts because last_bar_time always references the latest available bar’s opening time.
- This script expresses dates using the
"dd/MM/yy"
format, meaning the two-digit day appears before the two-digit month, and the month appears before the two-digit representation of the year. See this section below for more information.
Visible bar times
The chart.left_visible_bar_time and chart.right_visible_bar_time variables reference the opening UNIX timestamps of the chart’s leftmost (first) and rightmost (last) visible bars on every script execution. When a script uses these variables, it responds dynamically to visible chart changes, such as users scrolling across bars or zooming in/out. Each time the visible window changes, the script re-executes automatically to update the variables’ values on all available bars.
The example below demonstrates how the chart.left_visible_bar_time and chart.right_visible_bar_time variables work across script executions. The script draws labels anchored to the visible bars’ times to display the UNIX timestamps. In addition, it draws two single-cell tables showing corresponding dates and times in the standard ISO 8601 format. The script creates these drawings only when it executes on the first bar. As the script continues to execute on subsequent bars, it identifies each bar whose time value equals either visible bars’ timestamp and colors it on the chart:
Note that:
- The chart.left_visible_bar_time and chart.right_visible_bar_time values are consistent across all executions, which allows the script to identify the visible bars’ timestamps on the first bar and check when the time value equals them. The script restarts on any chart window changes, updating the variables’ series to reference the new timestamps on every bar.
- The str.format_time() function uses ISO 8601 format by default when the call does not include a
format
argument because it is the international standard for expressing dates and times. See the Formatting dates and times section to learn more about time string formats.
`syminfo.timezone`
The syminfo.timezone variable holds a time zone string representing the current symbol’s exchange time zone. The “string” value expresses the time zone as an IANA identifier (e.g., "America/New_York"
). The overloads of time functions that include a timezone
parameter use syminfo.timezone as the default argument.
Because this variable is the default timezone
argument for all applicable time function overloads, it is unnecessary to use as an explicit argument, except for stylistic purposes. However, programmers can use the variable in other ways, such as:
- Displaying the “string” in Pine Logs or drawings to inspect the exchange time zone’s IANA identifier.
- Comparing the value to other time zone strings to create time zone-based conditional logic.
- Requesting the exchange time zones of other symbols with
request.*()
function calls.
The following script uses the timenow variable to retrieve the UNIX timestamp of its latest execution. It formats the timestamp into date-time strings expressed in the main symbol’s exchange time zone and a requested symbol’s exchange time zone, which it displays along with the IANA identifiers in a table on the last chart bar:
Note that:
- Pine scripts execute on realtime bars only when new updates occur in the data feed, and timenow updates only on script executions. As such, when no realtime updates are available, the timenow timestamp does not change. See this section above for more information.
- The default
symbolInput
value is"NSE:BANKNIFTY"
. NSE is in the “Asia/Kolkata” time zone, which is 9.5 hours ahead of the main symbol’s exchange time zone (“America/New_York”) at the time of the screenshot. Although the local time representations differ, both refer to the same absolute time that the timenow timestamp represents. - The indicator() declaration statement includes
dynamic_requests = true
, which allows the script to call request.security() dynamically inside the if structure’s local scope. See the Dynamic requests section of the Other timeframes and data page to learn more.
Time functions
Pine Script™ features several built-in functions that scripts can use to retrieve, calculate, and express time values:
- The time() and time_close() functions allow scripts to retrieve UNIX timestamps for the opening and closing times of bars within a session on a specified timeframe, without requiring
request.*()
function calls. - The year(), month(), weekofyear(), dayofmonth(), dayofweek(), hour(), minute(), and second() functions calculate calendar-based values, expressed in a specified time zone, from a UNIX timestamp.
- The timestamp() function calculates a UNIX timestamp from a specified calendar date and time.
- The str.format_time() function formats a UNIX timestamp into a human-readable date/time “string”, expressed in a specified time zone. The Formatting dates and times section below provides detailed information about formatting timestamps with this function.
- The input.time() function returns a UNIX timestamp corresponding to the user-specified date and time, and the input.session() function returns a valid session string corresponding to the user-specified start and end times. See the Time input and Session input sections of the Inputs page to learn more about these functions.
`time()` and `time_close()` functions
The time() and time_close() functions return UNIX timestamps representing the opening and closing times of bars on a specified timeframe. Both functions can filter their returned values based on a given session in a specific time zone. They each have the following signatures:
Where:
functionName
is the function’s identifier.- The
timeframe
parameter accepts a timeframe string. The function uses the script’s main timeframe if the argument is timeframe.period or an empty “string”. - The
session
parameter accepts a session string defining the session’s start and end times (e.g.,"0930-1600"
) and the days for which it applies (e.g.,":23456"
means Monday - Friday). If the value does not specify the days, the session applies to all weekdays automatically. The function returns UNIX timestamps only for the bars within the session. It returns na if a bar’s time is outside the session. If thesession
argument is an empty “string” or not specified, The function uses the symbol’s session information. - The
timezone
parameter accepts a valid time zone string that defines the time zone of the specifiedsession
. It does not change the meaning of returned UNIX timestamps, as they are time zone-agnostic. If thetimezone
argument is not specified, the function uses the exchange time zone (syminfo.timezone). - The
bars_back
parameter accepts an “int” value specifying which bar’s time the returned timestamp represents. If the value is positive, the function returns the timestamp from that number of bars back relative to the current bar. If the value is negative and greater than or equal to -500, the function returns the expected timestamp of a future bar. The default value is 0.
Similar to the time and time_close variables, these functions behave differently on time-based and non-time-based charts.
Time-based charts have bars that open and close at predictable times, whereas the bars on tick charts and all non-standard charts, excluding Heikin Ashi, open and close at irregular, unpredictable times. Consequently, time_close() cannot calculate the expected closing times of realtime bars on non-time-based charts, so it returns na on those bars. Similarly, the time() function with a negative bars_back
value cannot accurately calculate the expected opening time of a future realtime bar on these charts. See the second example in this section above. That example script exhibits the same behavior on a price-based chart if it uses a time_close("") call instead of the time_close variable.
Typical use cases for the time() and time_close() functions include:
- Testing for bars that open or close in specific sessions defined by the
session
andtimezone
parameters. - Testing for changes or measuring time differences on specified higher timeframes.
Testing for sessions
The time() and time_close() functions’ session
and timezone
parameters define the sessions for which they can return non-na values. If a call to either function references a bar that opens/closes within the defined session in a given time zone, it returns a UNIX timestamp for that bar. Otherwise, it returns na. Programmers can pass the returned values to the na() function to identify which bars open or close within specified intervals, which is helpful for session-based calculations and logic.
This simple script identifies when a bar on the chart’s timeframe opens at or after 11:00 and before 13:00 in the exchange time zone on any trading day. It calls time() with timeframe.period as the timeframe
argument and the "1100-1300"
session string as the session
argument, and then verifies whether the returned value is na with the na() function. When the value is not na, the script highlights the chart’s background to indicate that the bar opened in the session:
Note that:
- The
session
argument in the time() call represents an interval in the exchange time zone because syminfo.timezone is the defaulttimezone
argument. - The session string expresses the start and end times in the
"HHmm-HHmm"
format, where"HH"
is the two-digit hour and"mm"
is the two-digit minute. Session strings can also specify the weekdays a session applies to. However, the time() call’ssession
argument ("1100-1300"
) does not include this information, which is why it considers the session valid for every day. See the Sessions page to learn more.
When using session strings in time() and time_close() calls, it’s crucial to understand that such strings define start and end times in a specific time zone. The local hour and minute in one region may not correspond to the same point in UNIX time as that same hour and minute in another region. Therefore, calls to these functions with different timezone
arguments can return non-na timestamps at different times, as the specified time zone string changes the meaning of the local times represented in the session
argument.
This example demonstrates how the timezone
parameter affects the session
parameter in a time() function call. The script calculates an opensInSession
condition that uses a time() call with arguments based on inputs. The session input for the session
argument includes four preset options: "0000-0400"
, "0930-1400"
, "1300-1700"
, and "1700-2100"
. The string input that defines the timezone
argument includes four IANA time zone options representing different offsets from UTC: "America/Vancouver"
(UTC-7/-8), "America/New_York"
(UTC-4/-5), "Asia/Dubai"
(UTC+4), and "Austrailia/Sydney"
(UTC+10/+11).
For any chosen sessionInput
value, changing the timezoneInput
value changes the specified session’s time zone. The script highlights different bars with each time zone choice because, unlike UNIX timestamps, the absolute times that local hour and minute values correspond to varies across time zones:
Note that:
- This script uses IANA notation for all time zone strings because it is the recommended format. Using an IANA identifier allows the time() call to automatically adjust the session’s UTC offset based on a region’s local time policies, such as daylight saving time.
Testing for changes in higher timeframes
The timeframe
parameter of the time() and time_close() functions specifies the timeframe of the bars in the calculation, allowing scripts to retrieve opening/closing UNIX timestamps from higher timeframes than the current chart’s timeframe without requiring request.*()
function calls.
Programmers can use the opening/closing timestamps from higher-timeframe (HTF) bars to detect timeframe changes. One common approach is to call time() or time_close() with a consistent timeframe
argument across all executions on a time-based chart and measure the one-bar change in the returned value with the ta.change() function. The result is a nonzero value only when an HTF bar opens. One can also check whether the data has a time gap at that point by comparing the time() value to the previous bar’s time_close() value. A gap is present when the opening timestamp on the current bar is greater than the closing timestamp on the previous bar.
The script below calls time(“1M”) to get the opening UNIX timestamp of the current bar on the “1M” timeframe. It detects when bars on that timeframe open by checking when the ta.change() of the timestamp returns a value greater than 0. On each occurrence of the condition, the script detects whether the HTF bar opened after a gap by checking if the opening time is greater than the previous bar’s time_close(“1M”) value.
The script draws labels containing formatted “1M” opening times to indicate the chart bars that mark the start of monthly bars. If a monthly bar opens without a gap from the previous closing time, the script draws a blue label. If a monthly bar starts after a gap, it draws a red label. Additionally, if the “1M” opening time does not match the opening time of the chart bar, the script displays that bar’s formatted time in the label for comparison:
Note that:
- Using ta.change() on a time() or time_close() call’s result is not the only way to detect changes in a higher timeframe. The timeframe.change() function is an equivalent, more convenient option for scripts that do not need to use the UNIX timestamps from HTF bars in other calculations, as it returns a “bool” value directly without extra code.
- The detected monthly opening times do not always correspond to the first calendar day of the month. Instead, they correspond to the first time assigned to a “1M” bar, which can be after the first calendar day. For symbols with overnight sessions, such as “USDJPY” in our example chart, a “1M” bar can also open before the first calendar day.
- Sometimes, the opening time assigned to an HTF bar might not equal the opening time of any chart bar, which is why other conditions such as
time == time("1M")
cannot detect new monthly bars consistently. For example, on our “USDJPY” chart, the “1M” opening time2023-12-31T17:00:00-0500
does not match an opening time on the “1D” timeframe. The first available “1D” bar after that point opened at2024-01-01T17:00:00-0500
.
Calendar-based functions
The year(), month(), weekofyear(), dayofmonth(), dayofweek(), hour(), minute(), and second() functions calculate calendar-based “int” values from a UNIX timestamp. Unlike the calendar-based variables, which always hold exchange calendar values based on the current bar’s opening timestamp, these functions can return calendar values for any valid timestamp and express them in a chosen time zone.
Each of these calendar-based functions has the following two signatures:
Where:
functionName
is the function’s identifier.- The
time
parameter accepts an “int” UNIX timestamp for which the function calculates a corresponding calendar value. - The
timezone
parameter accepts a time zone string specifying the returned value’s time zone. If thetimezone
argument is not specified, the function uses the exchange time zone (syminfo.timezone).
In contrast to the functions that return UNIX timestamps, a calendar-based function returns different “int” results for various time zones, as calendar values represent parts of a local time in a specific region.
For instance, the simple script below uses two calls to dayofmonth() to calculate each bar’s opening day in the exchange time zone and the “Australia/Sydney” time zone. It plots the results of the two calls in a separate pane for comparison:
Note that:
- The first dayofmonth() call calculates the bar’s opening day in the exchange time zone because it does not include a
timezone
argument. This call returns the same value that the dayofmonth variable references. - Our example symbol’s exchange time zone is “America/New_York”, which follows UTC-5 during standard time and UTC-4 during daylight saving time (DST). The “Australia/Sydney” time zone follows UTC+10 during standard time and UTC+11 during DST. However, Sydney observes DST at different times of the year than New York. As such, its time zone is 14, 15, or 16 hours ahead of the exchange time zone, depending on the time of year. The plots on our “1D” chart diverge when the difference is at least 15 hours because the bars open at 09:30 in exchange time, and 15 hours ahead is 00:30 on the next calendar day.
It’s important to understand that although the time
argument in a calendar-based function call represents a single, absolute point in time, each function returns only part of the date and time information available from the timestamp. Consequently, a calendar-based function’s returned value does not directly correspond to a unique time point, and conditions based on individual calendar values can apply to multiple bars.
For example, this script uses the timestamp() function to calculate a UNIX timestamp from a date “string”, and it calculates the calendar day from that timestamp, in the exchange time zone, with the dayofmonth() function. The script compares each bar’s opening day to the calculated day and highlights the background when the two are equal:
Note that:
- The timestamp() call treats its argument as a UTC calendar date because its
dateString
argument does not specify time zone information. However, the dayofmonth() call calculates the day in the exchange time zone. Our example symbol’s exchange time zone is “America/New_York” (UTC-4/-5). Therefore, the returned value on this chart is 28 instead of 29. - The script highlights any bar on our chart that opens on the 28th day of any month instead of only a specific bar because the dayofmonth() function’s returned value does not represent a specific point in time on its own.
- This script highlights the bars that open on the day of the month calculated from the timestamp. However, some months on our chart have no trading activity on that day. For example, the script does not highlight when the July 28, 2024 occurs on our chart because NASDAQ is closed on Sundays.
Similar to calendar-based variables, these functions are also helpful when testing for dates/times and detecting calendar changes on the chart. The example below uses the year(), month(), weekofyear(), and dayofweek() functions on the time_close timestamp to create conditions that test if the current bar is the first bar that closes in a new year, quarter, month, week, and day. The script uses plotted shapes, labels, and background colors to visualize the conditions on the chart:
Note that:
- This script’s conditions check for the first bar that closes after each calendar unit changes its value. The bar where each condition is
true
varies with the data available on the chart. For example, thecloseInNewMonth
condition can betrue
after the first calendar day of the month if a chart bar did not close on that day. - To detect when new bars start on a specific timeframe rather than strictly calendar changes, check when the ta.change() of a time() or time_close() call’s returned value is nonzero, or use the timeframe.change() function. See this section above for more information.
`timestamp()`
The timestamp() function calculates a UNIX timestamp from a specified calendar date and time. It has the following three signatures:
The first two signatures listed include year
, month
, day
, hour
, minute
, and second
parameters that accept “int” values defining the calendar date and time. A timestamp() call with either signature must include year
, month
, and day
arguments. The other parameters are optional, each with a default value of 0. Both signatures can return either “simple” or “series” values, depending on the qualified types of the specified arguments.
The primary difference between the first two signatures is the timezone
parameter, which accepts a time zone string that determines the time zone of the date and time specified by the other parameters. If a timestamp() call with “int” calendar arguments does not include a timezone
argument, it uses the exchange time zone (syminfo.timezone) by default.
The third signature listed has only one parameter, dateString
, which accepts a “string” representing a valid calendar date (e.g., "20 Aug 2024"
). The value can also include the time of day and time zone (e.g., "20 Aug 2024 00:00:00 UTC+0"
). If the dateString
argument does not specify the time of day, the timestamp() call considers the time 00:00 (midnight).
Unlike the other two signatures, the default time zone for the third signature is GMT+0. It does not use the exchange time zone by default because it interprets time zone information from the dateString
directly. Additionally, the third signature is the only one that returns a “const int” value. As shown in the Time input section of the Inputs page, programmers can use this overload’s returned value as the defval
argument in an input.time() function call.
When using the timestamp() function, it’s crucial to understand how time zone information affects its calculations. The absolute point in time represented by a specific calendar date depends on its time zone, as an identical date and time in various time zones can refer to different amounts of time elapsed since the UNIX Epoch. Therefore, changing the time zone of the calendar date and time in a timestamp() call can change its returned UNIX timestamp.
The following script compares the results of four different timestamp() calls that evaluate the date 2021-01-01 in different time zones. The first timestamp() call does not specify time zone information in its dateString
argument, so it treats the value as a UTC calendar date. The fourth call also evaluates the calendar date in UTC because it includes "UTC0"
as the timezone
argument. The second timestamp() call uses the first signature listed above, meaning it uses the exchange time zone, and the third call uses the second signature with "America/New_York"
as the timezone
argument.
The script draws a table with rows displaying each timestamp() call, its assigned variable, the calculated UNIX timestamp, and a formatted representation of the time. As we see on the “NASDAQ:MSFT” chart below, the first and fourth table rows show different timestamps than the first and third, leading to different formatted strings in the last column:
Note that:
- The formatted date-time strings express results in the exchange time zone because the str.format_time() function uses syminfo.timezone as the default
timezone
argument. The formatted values on our example chart show the offset string"-0500"
because NASDAQ’s time zone (“America/New_York”) follows UTC-5 during standard time. - The formatted strings on the first and fourth rows show the date and time five hours before January 1, 2021, because the timestamp() calls evaluated the date in UTC and the str.format_time() calls used a time zone five hours behind UTC.
- On our chart, the second and third rows have matching timestamps because both corresponding timestamp() calls evaluated the date in the “America/New_York” time zone. The two rows would show different results if we applied the script to a symbol with a different exchange time zone.
Formatting dates and times
Programmers can format UNIX timestamps into human-readable dates and times, expressed in specific time zones, using the str.format_time() function. The function has the following signature:
Where:
- The
time
parameter specifies the “int” UNIX timestamp to express as a readable time. - The
format
parameter accepts a “string” consisting of formatting tokens that determine the returned information. If the function call does not include aformat
argument, it uses the ISO 8601 standard format:"yyyy-MM-dd'T'HH:mm:ssZ"
. See the table below for a list of valid tokens and the information they represent. - The
timezone
parameter determines the time zone of the formatted result. It accepts a time zone string in UTC or IANA notation. If the call does not specify atimezone
, it uses the exchange time zone (syminfo.timezone).
The general-purpose str.format() function can also format UNIX timestamps into readable dates and times. However, the function cannot express time information in different time zones. It always expresses dates and times in UTC+0. In turn, using this function to format timestamps often results in erroneous practices, such as mathematically modifying a timestamp to try and represent the time in another time zone. However, a UNIX timestamp is a unique, time zone-agnostic representation of a specific point in time. As such, modifying a UNIX timestamp changes the absolute time it represents rather than expressing the same time in a different time zone.
The str.format_time() function does not have this limitation, as it can calculate dates and times in any time zone correctly without changing the meaning of a UNIX timestamp. In addition, unlike str.format(), it is optimized specifically for processing time values. Therefore, we recommend that programmers use str.format_time() instead of str.format() to format UNIX timestamps into readable dates and times.
A str.format_time() call’s format
argument determines the time information its returned value contains. The function treats characters and sequences in the argument as formatting tokens, which act as placeholders for values in the returned date/time “string”. The following table outlines valid formatting tokens and explains what each represents:
Token | Represents | Remarks and examples |
---|---|---|
"y" | Year | Use "yy" to include the last two digits of the year (e.g., "00" ), or "yyyy" to include the complete year number (e.g., "2000" ). |
"M" | Month | Uppercase "M" for the month, not to be confused with lowercase "m" for the minute. Use "MM" to include the two-digit month number with a leading zero for single-digit values (e.g., "01" ), "MMM" to include the three-letter abbreviation of month (e.g., "Jan" ), or "MMMM" for the full month name (e.g., "January" ). |
"d" | Day of the month | Lowercase "d" . Includes the numeric day of the month ( "1" to "31" ). Use "dd" for the two-digit day number with a leading zero for single-digit values. It is not a placeholder for the day number of the week (1-7). Use dayofweek() to calculate that value. |
"D" | Day of the year | Uppercase "D" . Includes the numeric day of the year ( "1" to "366" ). Use "DD" or "DDD" for the two-digit or three-digit day number with leading zeros. |
"w" | Week of the year | Lowercase "w" . Includes the week number of the year ( "1" to "53" ). Use "ww" for the two-digit week number with a leading zero for single-digit values. |
"W" | Week of the month | Uppercase "W" . Includes the week number of the month ( "1" to "5" ). |
"a" | AM/PM postfix | Lowercase "a" . Includes "AM" if the time of day is before noon, "PM" otherwise. |
"h" | Hour in the 12-hour format | Lowercase "h" . The included hour number from this token ranges from "0" to "11" . Use "hh" for the two-digit hour with a leading zero for single-digit values. |
"H" | Hour in the 24-hour format | Uppercase "H" . The included hour number from this token ranges from "0" to "23" . Use "HH" for the two-digit hour with a leading zero for single-digit values. |
"m" | Minute | Lowercase "m" for the minute, not to be confused with uppercase "M" for the month. Use "mm" for the two-digit minute with a leading zero for single-digit values. |
"s" | Second | Lowercase "s" for the second, not to be confused with uppercase "S" for fractions of a second. Use "ss" for the two-digit second with a leading zero for single-digit values. |
"S" | Fractions of a second | Uppercase "S" . Includes the number of milliseconds in the fractional second ( "0" to "999" ). Use "SS" or "SSS" for the two-digit or three-digit millisecond number with leading zeros. |
"Z" | Time zone (UTC offset) | Uppercase "Z" . Includes the hour and minute UTC offset value in "HHmm" format, preceded by its sign (e.g., "-0400" ). |
"z" | Time zone (acronym or name) | Lowercase "z" . A single "z" includes the abbreviation of the time zone (e.g., "EDT" ). Use "zzzz" for the time zone’s name (e.g., "Eastern Daylight Time" ). It is not a placeholder for the IANA identifier. Use syminfo.timezone to retrieve the exchange time zone’s IANA representation. |
":" , "/" , "-" , "." , "," , "(" , ")" , " " | Separators | These characters are separators for formatting tokens. They appear as they are in the formatted text. (e.g., "01/01/24" , "12:30:00" , "Jan 1, 2024" ). Some other characters can also act as separators. However, the ones listed are the most common. |
"'" | Escape character | Characters enclosed within two single quotes appear as they are in the result, even if they otherwise act as formatting tokens. For example, " 'Day' " appears as-is in the resulting “string” instead of listing the day of the year, AM/PM postfix, and year. |
The following example demonstrates how various formatting tokens affect the str.format_time() function’s result. The script calls the function with different format
arguments to create date/time strings from time, timenow, and time_close timestamps. It displays each format
value and the corresponding formatted result in a table on the last bar:
Expressing time differences
Every UNIX timestamp represents a specific point in time as the absolute time difference from a fixed historical point (epoch). The specific epoch all UNIX timestamps reference is midnight UTC on January 1, 1970. Programmers can format UNIX timestamps into readable date-time strings with the str.format_time() function because it uses the time difference from the UNIX Epoch in its date and time calculations.
In contrast, the difference between two nonzero UNIX timestamps represents the number of milliseconds elapsed from one absolute point to another. The difference does not directly refer to a specific point in UNIX time if neither timestamp in the operation has a value of 0 (corresponding to the UNIX Epoch).
Programmers may want to express the millisecond difference between two UNIX timestamps in other time units, such as seconds, days, etc. Some might assume they can use the difference as the time
argument in a str.format_time() call to achieve this result. However, the function always treats its time
argument as the time elapsed from the UNIX Epoch to derive a calendar date/time representation in a specific time zone. It does not express time differences directly. Therefore, attempting to format timestamp differences rather than timestamps with str.format_time() leads to unintended results.
For example, the following script calculates the millisecond difference between the current execution time (timenow) and the “1M” bar’s closing time (time_close(“1M”)) for a monthly countdown timer display. It attempts to express the time difference in another format using str.format_time(). It displays the function call’s result in a table, along with the original millisecond difference (timeLeft
) and formatted date-time representations of the timestamps.
As we see below, the table shows correct results for the formatted timestamps and the timeLeft
value. However, the formatted time difference appears as "1970-01-12T16:47:10-0500"
. Although the timeLeft
value is supposed to represent a difference between timestamps rather than a specific point in time, the str.format_time() function still treats the value as a UNIX timestamp. Consequently, it creates a “string” expressing the value as a date and time in the UTC-5 time zone:
To express the difference between timestamps in other time units correctly, programmers must write code that calculates the number of units elapsed instead of erroneously formatting the difference as a specific date or time.
The calculations required to express time differences depend on the chosen time units. The sections below explain how to express millisecond differences in weekly and smaller units, and monthly and larger units.
Weekly and smaller units
Weeks and smaller time units (days, hours, minutes, seconds, and milliseconds) cover consistent blocks of time. These units have the following relationship:
- One week equals seven days.
- One day equals 24 hours.
- One hour equals 60 minutes.
- One minute equals 60 seconds.
- One second equals 1000 milliseconds.
Using this relationship, programmers can define the span of these units by the number of milliseconds they contain. For example, since every hour has 60 minutes, every minute has 60 seconds, and every second has 1000 milliseconds, the number of milliseconds per hour is 60 * 60 * 1000
, which equals 3600000
.
Programmers can use modular arithmetic based on the milliseconds in each unit to calculate the total number of weeks, days, and smaller spans covered by the difference between two UNIX timestamps. The process is as follows, starting from the largest time unit in the calculation:
- Calculate the number of milliseconds in the time unit.
- Divide the remaining millisecond difference by the calculated value and round down to the nearest whole number. The result represents the number of complete time units within the interval.
- Use the remainder from the division as the new remaining millisecond difference.
- Repeat steps 1-3 for each time unit in the calculation, in descending order based on size.
The following script implements this process in a custom formatTimeSpan()
function. The function accepts two UNIX timestamps defining a start and end point, and its “bool” parameters control whether it calculates the number of weeks or smaller units covered by the time range. The function calculates the millisecond distance between the two timestamps. It then calculates the numbers of complete units covered by that distance and formats the results into a “string”.
The script calls formatTimeSpan()
to express the difference between two separate time input values in selected time units. It then displays the resulting “string” in a table alongside formatted representations of the start and end times:
Note that:
- The user-defined function uses math.floor() to round each divided result down to the nearest “int” value to get the number of complete units in the interval. After division, it uses the modulo assignment operator (%=) to get the remainder and assign that value to the
timeDifference
variable. This process repeats for each selected unit.
The image above shows the calculated time difference in mixed time units. By toggling the “bool” inputs, users can also isolate specific units in the calculation. For example, this image shows the result after enabling only the “Milliseconds” input:
Monthly and larger units
Unlike weeks and smaller units, months and larger units vary in length based on calendar rules. For example, a month can contain 28, 29, 30, or 31 days, and a year can contain 365 or 366 days.
Some programmers prefer to use the modular arithmetic outlined in the previous section, with approximate lengths for these irregular units, to calculate large-unit durations between UNIX timestamps. With this process, programmers usually define the units in either of the following ways:
- Using common lengths, e.g., a common year equals 365 days, and a common month equals 30 days.
- Using the average lengths, e.g., an average year equals 365.25 days, and an average month equals 30.4375 days.
Calculations involving approximate units produce rough estimates of the elapsed time. Such estimates are often practical when expressing relatively short durations. However, their precision diminishes with the size of the difference, drifting further away from the actual time elapsed.
Therefore, expressing time differences in monthly and larger units with precision requires a different calculation than the process outlined above. For a more precise estimate of months, years, and larger units elapsed, the calculations should use the actual span of each individual unit rather than approximations, meaning it must account for leap years and variations in month sizes.
The advanced example below contains a custom formatTimeDifference()
function that calculates the years and months, in addition to days and smaller units, elapsed between two UNIX timestamps.
The function uses the process outlined in the previous section to calculate the daily and smaller units within the interval. For the monthly and yearly units, which have irregular lengths, the function uses a while loop to iterate across calendar months. On each iteration, it increments monthly and yearly counters and subtracts the number of days in the added month from the day counter. After the loop ends, the function adjusts the year, month, and day counters to account for partial months elapsed between the timestamps. Finally, it uses the counters in a str.format() call to create a formatted “string” containing the calculated values.
The script calls this formatTimeDifference()
function to calculate the years, months, days, hours, minutes, seconds, and milliseconds elapsed between two separate time input values and displays the result in a label:
Note that:
- The script determines the number of days in each month with the user-defined
daysPerMonth()
function. The function identifies whether a month has 28, 29, 30, or 31 days based on its month number and the year it belongs to. Its calculation accounts for leap years. A leap year occurs when the year is divisible by 4 or 400 but not by 100. - Before the while loop, the function subtracts the number of days in a partial starting month from the initial day count, aligning the counters with the beginning of a new month. It re-adds the subtracted days after the loop to adjust the counters for partial months. It adjusts the month and year counters based on the days in the
startMonth
if thestartDay
is less than halfway through that month. Otherwise, it adjusts the values based on the days in theendMonth
.