Appearance
Date & Time System Variables — Implementation Plan
Summary
Add built-in system variables for date and time that are always available in the flow variable system. These enable time-sensitive content (greetings, weekend offers, campaign deadlines) and date-based conditions without any SetVariable setup.
Proposed Variables
| Variable | Example output | Description |
|---|---|---|
{date} | 22.02.2026 | Current date, localized |
{time} | 14:30 | Current time (HH:mm), user's timezone |
{day} | Saturday | Weekday name, localized |
{day.number} | 6 | Day of week (1=Monday, 7=Sunday) |
{hour} | 14 | Current hour (0–23), user's timezone |
{month} | February | Month name, localized |
{month.number} | 2 | Month number (1–12) |
{year} | 2026 | Four-digit year |
Format suffixes (future/optional)
Could support {date:DD/MM/YYYY}, {time:HH:mm:ss} etc. via a colon syntax. Not MVP — the defaults above cover most use cases.
Use Cases
- Greetings: "Good {greeting}, welcome!" where
{greeting}= morning/afternoon/evening based on{hour}- Or simpler: condition on
{hour}→ show different text operators
- Or simpler: condition on
- Weekend content: "When day.number >= 6" → show weekend offer
- Campaign deadline: "Offer valid until March 1st — only {daysLeft} days remaining!"
{daysLeft}would require computed variables (future feature), but{date}and conditions get us partway
- Opening hours: "When hour >= 9" AND "When hour < 17" → show "We're open now!"
- Calendar booking: Show time-relevant availability text
Architecture
Timezone: Client-Side Resolution
The CE runs inside the user's browser, so Date and Intl APIs already use the correct local timezone. No server coordination needed.
// In CE — resolving date/time variables
const now = new Date()
const locale = navigator.language || 'en'
const dateVars = {
'date': now.toLocaleDateString(locale),
'time': now.toLocaleTimeString(locale, { hour: '2-digit', minute: '2-digit' }),
'day': now.toLocaleDateString(locale, { weekday: 'long' }),
'day.number': String(now.getDay() === 0 ? 7 : now.getDay()), // 1=Mon, 7=Sun
'hour': String(now.getHours()),
'month': now.toLocaleDateString(locale, { month: 'long' }),
'month.number': String(now.getMonth() + 1),
'year': String(now.getFullYear()),
}CE Changes
1. src/services/dataStore.ts — Inject date variables
Date/time variables should be resolved fresh on each substitution (not stored once at init), since a user might have the creative open across midnight or for extended periods.
Option A: Lazy getter in flowVariables that computes on access. Option B: Resolve in substituteVariables() directly before checking flowVariables.
Recommendation: Option B — keeps flowVariables clean (only user-set state) and avoids reactive overhead.
2. src/utils/content/richText.ts (or wherever substituteVariables lives)
Before looking up flowVariables[varName], check if varName is a date/time key and resolve it inline:
typescript
function resolveDateVariable(name: string): string | undefined {
const now = new Date()
const locale = navigator.language || 'en'
switch (name) {
case 'date': return now.toLocaleDateString(locale)
case 'time': return now.toLocaleTimeString(locale, { hour: '2-digit', minute: '2-digit' })
case 'day': return now.toLocaleDateString(locale, { weekday: 'long' })
case 'day.number': return String(now.getDay() === 0 ? 7 : now.getDay())
case 'hour': return String(now.getHours())
case 'month': return now.toLocaleDateString(locale, { month: 'long' })
case 'month.number': return String(now.getMonth() + 1)
case 'year': return String(now.getFullYear())
default: return undefined
}
}3. src/utils/evaluateCondition.ts
No changes needed — the condition evaluator already receives the resolved variable value. Date variables flow through the same substituteVariables → evaluateCondition pipeline.
Exception: {day} returns a localized string ("Lørdag" in Norwegian). For conditions like When day = Saturday, the user would need to type the localized name. This is fine for MVP — document it. Future: could add a day picker in the condition UI.
Composer Changes
None. Date variables are CE-only (resolved at runtime). No data flows through the JSON/composer.
Frontend Changes
1. VariableSuggestions.vue
Add date/time variables to the availableVariables computed, in a "Date & Time" group:
typescript
// After system vars (answer, answer.position):
vars.push(
{ name: 'date', description: "Today's date" },
{ name: 'time', description: 'Current time' },
{ name: 'day', description: 'Day of the week' },
{ name: 'hour', description: 'Current hour (0–23)' },
)Keep it minimal — don't show all 8 variables. date, time, day, hour cover the common cases. Advanced users can type month, year etc. manually.
2. ConditionFields.vue
Add the same variables to availableVariables for the datalist.
For {day} conditions: could add a datalist of localized weekday names as suggested values (like we do for answer choices). Nice-to-have.
For {hour} conditions: no special suggestions needed — user types a number.
MVP Scope
- CE:
resolveDateVariable()function + integrate into substitution pipeline - CE: Freeze date variables at step creation (same as
{answer}freeze in Text.vue) - Frontend: Add
date,time,day,hourto VariableSuggestions + ConditionFields - Test:
{time}in a text operator shows current time, condition on{hour}works
Out of Scope (future)
- Format suffixes (
{date:DD/MM}) {greeting}computed variable (morning/afternoon/evening)- Date arithmetic (
{daysUntil:2026-03-01}) - Server-side date resolution (for analytics/server-rendered contexts)
- Day picker UI in condition fields
- Countdown timer variable (
{countdown:2026-03-01T00:00})
Interaction with Existing Features
- Variable substitution: Date vars go through the same
{varName}→ value pipeline - Conditions: Work with existing
evaluateCondition— numeric comparison forhour/day.number, string comparison forday - Text.vue freeze: Date variables get frozen at render time (same as
{answer}), so a message saying "Sent at {time}" keeps its original time - SetVariable: Users could technically
SetVariable date = {date}to snapshot a date — works automatically since substitution happens before storage