Pine Script: How to Convert a Timestamp to a Date?

Navigating time in trading scripts is fundamental, whether you’re backtesting strategies, setting time-based conditions, or simply displaying relevant information on the chart. Pine Script, TradingView’s scripting language, handles time primarily through timestamps.

This article dives into the practical aspects of working with timestamps and, crucially, how to convert them into human-readable date formats. We’ll explore the built-in functions and provide actionable code examples for intermediate to senior Pine Script developers.

Understanding Pine Script Timestamps

In Pine Script, timestamps represent a specific point in time. The most common timestamps you’ll encounter are the built-in variables time and time_close. Both store the time as a Unix epoch timestamp in milliseconds.

  • time: The timestamp of the beginning of the current bar.
  • time_close: The timestamp of the end of the current bar.

These variables are essential because they link your script’s execution context to the specific time of the bar being processed. Understanding that these are numerical values (milliseconds since January 1, 1970, 00:00:00 UTC) is key to manipulating them.

Why Convert Timestamps to Dates?

While timestamps are useful for internal calculations (like durations or ordering), they are not intuitive for humans. Converting a timestamp to a date (year, month, day) serves several critical purposes in trading scripts:

  1. Display: Showing the date of a specific event (like a signal) directly on the chart.
  2. Filtering: Executing logic only within specific date ranges or on particular days.
  3. Identification: Pinpointing the date associated with historical data points during backtesting.
  4. Alerts: Triggering alerts based on reaching a certain date or time.

Converting timestamps makes your script’s output and logic more understandable and controllable from a time-based perspective.

Methods for Converting Timestamp to Date in Pine Script

Pine Script provides a suite of built-in functions specifically designed to extract date and time components from a timestamp. These functions implicitly operate on the current bar’s timestamp (time) if no explicit timestamp argument is provided.

Utilizing the ‘year()’, ‘month()’, and ‘dayofmonth()’ Functions

The most straightforward way to convert a timestamp to its date components is by using the dedicated functions:

  • year(timestamp): Returns the year (e.g., 2023) for the given timestamp.
  • month(timestamp): Returns the month (1-12) for the given timestamp.
  • dayofmonth(timestamp): Returns the day of the month (1-31) for the given timestamp.
  • dayofweek(timestamp): Returns the day of the week (1 for Sunday, 7 for Saturday).
  • hour(timestamp): Returns the hour (0-23).
  • minute(timestamp): Returns the minute (0-59).
  • second(timestamp): Returns the second (0-59).

By default, if you omit the timestamp argument, these functions use the time variable (the bar’s opening timestamp). You can explicitly pass time_close or any other valid timestamp variable or literal.

// Get date components for the current bar's opening time
currentYear = year(time)
currentMonth = month(time)
currentDay = dayofmonth(time)
currentWeekday = dayofweek(time)

// You can also get components for the bar's closing time
closeYear = year(time_close)
closeMonth = month(time_close)
closeDay = dayofmonth(time_close)

// Example: Getting date components for a specific timestamp (UTC)
someTimestamp = timestamp("2023-01-01T10:00:00Z") // UTC timestamp literal
someYear = year(someTimestamp)
someMonth = month(someTimestamp)
someDay = dayofmonth(someTimestamp)

These functions return integer values, making them easy to use in conditional statements or calculations.

Combining Functions for Custom Date Formatting

Often, you’ll want to display the date in a standard YYYY-MM-DD or MM/DD/YYYY format. You can achieve this by concatenating the results of the date functions using the tostring() function.

tostring() converts various data types, including integers, to a string. You can then use the + operator to join strings.

// Get date components
currentYear = year(time)
currentMonth = month(time)
currentDay = dayofmonth(time)

// Convert components to strings and concatenate
// Simple format: YYYY-M-D
formattedDate = tostring(currentYear) + "-" + tostring(currentMonth) + "-" + tostring(currentDay)

// To add leading zeros for single-digit months/days:
// Requires checking if the value is less than 10
monthStr = currentMonth < 10 ? "0" + tostring(currentMonth) : tostring(currentMonth)
dayStr = currentDay < 10 ? "0" + tostring(currentDay) : tostring(currentDay)

// Formatted YYYY-MM-DD
formattedDatePadded = tostring(currentYear) + "-" + monthStr + "-" + dayStr

// Example: Displaying the formatted date on the chart
label.new(bar_index, high, text=formattedDatePadded, textcolor=color.white)

// Plotting a value and showing date in the data window tooltip
plot(close, title="Close", display=display.all, tooltip=formattedDatePadded)

Creating padded date strings is a common requirement for consistent display and demonstrates combining conditional logic with string manipulation.

Practical Examples of Timestamp to Date Conversion

Let’s look at some real-world scenarios where converting timestamps to dates is essential for building functional trading scripts.

Displaying the Date on the Chart

Visual confirmation of dates is helpful for debugging or understanding specific events. Labels are ideal for this.

//@version=5
indicator("Show Bar Date", overlay=true)

// Convert the current bar's time to YYYY-MM-DD format
currentYear = year(time)
currentMonth = month(time)
currentDay = dayofmonth(time)

monthStr = currentMonth < 10 ? "0" + tostring(currentMonth) : tostring(currentMonth)
dayStr = currentDay < 10 ? "0" + tostring(currentDay) : tostring(currentDay)

formattedDate = tostring(currentYear) + "-" + monthStr + "-" + dayStr

// Add a label to every 100th bar showing its date
if bar_index % 100 == 0
    label.new(bar_index, high, text=formattedDate, textcolor=color.white, style=label.style_label_down)

plotchar(true, title="Bar Date", char='', tooltip=formattedDate) // Display date in Data Window

This example shows how to format the date and use a label or plotchar tooltip to make it visible.

Calculating Time Differences Using Dates

While Pine Script offers functions like time for direct timestamp comparisons, sometimes working with specific dates is more intuitive. You can get the timestamp for a specific date and time using the timestamp() function.

timestamp(year, month, day, hour, minute, second) or timestamp(string)

This function creates a timestamp in UTC from the specified components or from a UTC-formatted string. Comparing this UTC timestamp to the bar’s time (which is in broker time) requires careful consideration of time zones (discussed later).

A simpler approach for calculating duration between bars is to compare their time or time_close timestamps directly.

//@version=5
indicator("Days Since Start", overlay=false)

// Get the timestamp of the very first bar
var int firstBarTime = na
if bar_index == 0
    firstBarTime := time

// Calculate the difference in milliseconds
timeDifferenceMs = time - firstBarTime

// Convert milliseconds to days (1 day = 24 hours * 60 mins * 60 secs * 1000 ms)
msPerDay = 24 * 60 * 60 * 1000
daysSinceStart = timeDifferenceMs / msPerDay

// Plot the number of days since the first bar
plot(daysSinceStart, title="Days Since First Bar")

// Example: Getting a specific timestamp and comparing (UTC vs Broker Time - careful here!)
// Let's get the timestamp for Jan 1, 2023, 00:00:00 UTC
janFirst2023UTC = timestamp("2023-01-01T00:00:00Z")

// This comparison needs time zone awareness. For simplicity, comparing bar time is often better.
// Or, convert the specific date to broker time if possible, or convert bar time to UTC.
// Example check if current bar is on or after a specific date (simplistic, assumes time zone alignment or check only day/month/year)
// More robust would be comparing date components or handling time zones explicitly.
// Let's use date components for a date range check instead:

// Check if current bar is in Jan 2023
isJan2023 = year(time) == 2023 and month(time) == 1

plotchar(isJan2023, title="Is Jan 2023", char='J', location=location.top)

Calculating differences between bar timestamps is straightforward. Comparing against a specific target date created with timestamp() requires understanding time zones or using component-wise comparisons (year(), month(), dayofmonth()) based on the bar’s local time (time).

Creating Alerts Based on Specific Dates

Filtering logic or triggering alerts based on dates is a common use case. You can use the date component functions to create conditions.

//@version=5
indicator("Date-Based Alert", overlay=true)

// Define a target date (e.g., Dec 25, 2023)
targetYear = 2023
targetMonth = 12
targetDay = 25

// Check if the current bar's date matches the target date
// Use time_close for the check to ensure the *entire* day is processed before triggering on the last bar
isTargetDate = year(time_close) == targetYear and month(time_close) == targetMonth and dayofmonth(time_close) == targetDay

// Trigger an alert on the close of the target date
alertcondition(isTargetDate, title="Merry Christmas!", message="It's Christmas Day 2023!")

// Example: Alert for a specific date and time (e.g., Jan 1, 2024, 09:00 broker time)
targetYearTime = 2024
targetMonthTime = 1
targetDayTime = 1
targetHourTime = 9
targetMinuteTime = 0

isTargetDateTime = year(time) == targetYearTime and month(time) == targetMonthTime and dayofmonth(time) == targetDayTime and hour(time) == targetHourTime and minute(time) == targetMinuteTime

alertcondition(isTargetDateTime, title="New Year Morning", message="It's Jan 1, 2024, 09:00!")

Using time_close for date-based conditions ensures that the condition is evaluated based on the date the bar completes, which is often more reliable for daily checks. For specific times, using time is appropriate.

Advanced Techniques and Considerations

Working with time, especially across different instruments and exchanges, introduces complexities. Acknowledging these is vital for robust scripts.

Handling Time Zones

This is a significant pitfall. The built-in variables time and time_close are in the broker’s time zone (the time zone of the exchange/symbol you are currently viewing). The timestamp() function, however, creates timestamps based on UTC when using the string format YYYY-MM-DDTHH:mm:ssZ or when omitting time components.

Comparing a timestamp() literal (UTC) directly to time or time_close (broker time) without accounting for the offset can lead to errors. While Pine Script doesn’t offer direct timestamp conversion between arbitrary time zones easily within the script itself, the year(), month(), etc., functions correctly report the date components based on the timestamp provided, which for time and time_close is the broker’s time.

If you need to work with specific UTC dates, it’s often best to convert the bar’s time to UTC or use time zone information explicitly (though Pine’s capabilities here are somewhat limited compared to general-purpose languages). The ta.timezone() function can give you the current symbol’s time zone offset from UTC in minutes.

//@version=5
indicator("Time Zone Info", overlay=false)

// Get the current bar's date components in broker time
currentYearBroker = year(time)
currentMonthBroker = month(time)
currentDayBroker = dayofmonth(time)

// Get the time zone offset in minutes from UTC
// Offset is positive for zones east of UTC, negative for zones west.
// Example: New York (EST) is -300 minutes (-5 hours) from UTC.
currentOffsetMinutes = ta.timezone(syminfo.timezone)

// You could technically try to calculate UTC time based on offset,
// but relying on the built-in functions for broker time is usually more practical.

// Getting date components of a UTC timestamp literal
utcTimestamp = timestamp("2023-01-01T10:00:00Z") // 10:00 AM UTC on Jan 1, 2023
utcYear = year(utcTimestamp) // Will be 2023
utcMonth = month(utcTimestamp) // Will be 1
utcDay = dayofmonth(utcTimestamp) // Will be 1

// Note: Comparing utcTimestamp directly to `time` or `time_close` needs offset adjustment!
// It's often safer to compare date components extracted from the bar's `time` if you only care about the *date* in broker time.

// Example: Check if the bar's date in *broker time* is after Jan 1, 2023
isAfterJan12023Broker = time > timestamp("2023-01-01") // timestamp("YYYY-MM-DD") defaults to 00:00:00 UTC
// This comparison is tricky because `timestamp("2023-01-01")` is 00:00 UTC, while `time` is broker time.
// A safer check using components based on broker time:
isAfterJan12023Component = year(time) > 2023 or (year(time) == 2023 and month(time) > 1) or (year(time) == 2023 and month(time) == 1 and dayofmonth(time) >= 1)

plotchar(isAfterJan12023Component, title="After Jan 1 2023 (Broker Time)", char='A', location=location.bottom)

For robust date filtering, comparing the extracted date components (year(), month(), dayofmonth()) from the bar’s time is generally the safest approach if your logic is based on the broker’s date.

Dealing with Historical Data

The date conversion functions (year(), month(), etc.) work identically on historical bars as they do on the real-time bar. The time and time_close variables hold the correct timestamp for each bar in the history being processed during a backtest or historical analysis.

Your logic using these dates will function correctly across the dataset. Be mindful of potential edge cases like data gaps or exchange holidays, which might affect sequences of dates.

Functions like isfirst() and islast() can be useful when dealing with historical data periods defined by dates. For example, using isfirst allows you to initialize variables only on the very first bar of the loaded history, regardless of its date.

Optimization Tips for Date Conversions

Date conversion functions are relatively fast, so performance is rarely an issue unless you’re doing complex string formatting or comparisons within tight loops on massive datasets. However, standard optimization principles apply:

  • Calculate Once: If you need the date components multiple times within the same bar’s execution, calculate them once and store them in variables (var int currentYear = year(time)).
  • Conditional Execution: If date calculations or formatting are only needed for specific events (like a signal or alert condition), put the conversion logic inside an if statement to avoid unnecessary computation on every single bar.
//@version=5
indicator("Optimized Date Display", overlay=true)

// Only calculate and format date when adding a label
addLabelCondition = close > open and bar_index % 50 == 0

var string dateString = na // Use var to retain value across bars

if addLabelCondition
    currentYear = year(time)
    currentMonth = month(time)
    currentDay = dayofmonth(time)
    monthStr = currentMonth < 10 ? "0" + tostring(currentMonth) : tostring(currentMonth)
    dayStr = currentDay < 10 ? "0" + tostring(currentDay) : tostring(currentDay)
    dateString := tostring(currentYear) + "-" + monthStr + "-" + dayStr
    label.new(bar_index, high, text=dateString, color=color.green, style=label.style_label_up)

// plotchar could still use the dateString even if label isn't added on every bar
// plotchar(true, title="Bar Date", char='', tooltip=dateString) // If dateString is needed for every bar, calculate it outside the 'if'

By calculating dateString only when addLabelCondition is true, you save computation on the majority of bars where the label is not drawn. If the tooltip is required on every bar, the formatting calculation would need to be moved outside the if block.

Conclusion

Converting timestamps to dates in Pine Script is a fundamental skill that unlocks powerful time-based logic and improves the readability of your script’s output. The built-in year(), month(), dayofmonth(), and related functions provide the tools needed to extract date components from the bar’s timestamp (time or time_close).

Combining these functions with tostring() and string concatenation allows you to format dates for display, filtering, or alerts. While straightforward, remember the crucial aspect of time zones when comparing timestamps created with timestamp() literals (UTC) against the bar’s time (broker time). For most filtering based on the chart’s displayed date, comparing components extracted from time or time_close is the most reliable method.

Mastering these techniques will enable you to build more sophisticated and time-aware trading indicators and strategies in Pine Script.

Summary of Timestamp to Date Conversion in Pine Script

  • Timestamps (time, time_close) are milliseconds since the Unix epoch.
  • Functions like year(), month(), dayofmonth() extract components from a timestamp.
  • These functions operate on the bar’s timestamp (time by default).
  • Use tostring() and string concatenation for custom date formatting.
  • Be mindful of time zones: timestamp() literals are UTC, time/time_close are broker time.
  • Comparing date components from time or time_close is often the safest way for broker-time date logic.

Further Resources and Learning

For more details on all date and time functions available in Pine Script, consult the official TradingView Pine Script Reference Manual. It provides comprehensive documentation on time, time_close, timestamp(), year(), month(), dayofmonth(), hour(), minute(), second(), dayofweek(), weekofyear(), ta.timezone(), and more.


Leave a Reply