diff --git a/apps/output/views.py b/apps/output/views.py
index 8f1790b3..e4a5f0e5 100644
--- a/apps/output/views.py
+++ b/apps/output/views.py
@@ -634,6 +634,22 @@ def generate_custom_dummy_programs(channel_id, channel_name, now, num_days, cust
minute = temp_date_output.minute
logger.debug(f"Converted display time from {source_tz} to {output_tz}: {hour_24}:{minute:02d}")
+ # Add date placeholders based on the OUTPUT timezone
+ # This ensures {date}, {month}, {day}, {year} reflect the converted timezone
+ all_groups['date'] = temp_date_output.strftime('%Y-%m-%d')
+ all_groups['month'] = str(temp_date_output.month)
+ all_groups['day'] = str(temp_date_output.day)
+ all_groups['year'] = str(temp_date_output.year)
+ logger.debug(f"Converted date placeholders to {output_tz}: {all_groups['date']}")
+ else:
+ # No output timezone conversion - use source timezone for date
+ # Create temp date to get proper date in source timezone
+ temp_date_source = datetime.now(source_tz).replace(hour=hour_24, minute=minute, second=0, microsecond=0)
+ all_groups['date'] = temp_date_source.strftime('%Y-%m-%d')
+ all_groups['month'] = str(temp_date_source.month)
+ all_groups['day'] = str(temp_date_source.day)
+ all_groups['year'] = str(temp_date_source.year)
+
# Format 24-hour start time string - only include minutes if non-zero
if minute > 0:
all_groups['starttime24'] = f"{hour_24}:{minute:02d}"
diff --git a/frontend/src/components/forms/DummyEPG.jsx b/frontend/src/components/forms/DummyEPG.jsx
index 2b6984a8..d61c446f 100644
--- a/frontend/src/components/forms/DummyEPG.jsx
+++ b/frontend/src/components/forms/DummyEPG.jsx
@@ -226,10 +226,27 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
const sourceTimezone = form.values.custom_properties?.timezone || 'UTC';
const outputTimezone = form.values.custom_properties?.output_timezone;
- if (outputTimezone && outputTimezone !== sourceTimezone) {
- // Create a date in the source timezone
- const sourceDate = dayjs()
+ // Determine the base date to use
+ let baseDate = dayjs().tz(sourceTimezone);
+
+ // If date was extracted from pattern, use that instead of today
+ if (result.dateGroups.month && result.dateGroups.day) {
+ const extractedMonth = parseInt(result.dateGroups.month);
+ const extractedDay = parseInt(result.dateGroups.day);
+ const extractedYear = result.dateGroups.year
+ ? parseInt(result.dateGroups.year)
+ : dayjs().year(); // Default to current year if not provided
+
+ baseDate = dayjs()
.tz(sourceTimezone)
+ .year(extractedYear)
+ .month(extractedMonth - 1) // dayjs months are 0-indexed
+ .date(extractedDay);
+ }
+
+ if (outputTimezone && outputTimezone !== sourceTimezone) {
+ // Create a date in the source timezone with extracted or current date
+ const sourceDate = baseDate
.set('hour', hour24)
.set('minute', minute)
.set('second', 0);
@@ -241,6 +258,13 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
hour24 = outputDate.hour();
const convertedMinute = outputDate.minute();
+ // Add date placeholders based on the OUTPUT timezone
+ // This ensures {date}, {month}, {day}, {year} reflect the converted timezone
+ allGroups.date = outputDate.format('YYYY-MM-DD');
+ allGroups.month = outputDate.month() + 1; // dayjs months are 0-indexed
+ allGroups.day = outputDate.date();
+ allGroups.year = outputDate.year();
+
// Format 24-hour start time string with converted time
if (convertedMinute > 0) {
allGroups.starttime24 = `${hour24.toString().padStart(2, '0')}:${convertedMinute.toString().padStart(2, '0')}`;
@@ -269,6 +293,17 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
allGroups.starttime24_long = `${hour24.toString().padStart(2, '0')}:${convertedMinute.toString().padStart(2, '0')}`;
} else {
// No timezone conversion - use original logic
+ // Add date placeholders based on the source timezone
+ const sourceDate = baseDate
+ .set('hour', hour24)
+ .set('minute', minute)
+ .set('second', 0);
+
+ allGroups.date = sourceDate.format('YYYY-MM-DD');
+ allGroups.month = sourceDate.month() + 1; // dayjs months are 0-indexed
+ allGroups.day = sourceDate.date();
+ allGroups.year = sourceDate.year();
+
// Format 24-hour start time string
if (minute > 0) {
allGroups.starttime24 = `${hour24.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
@@ -341,6 +376,10 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
endtime: allGroups.endtime,
endtime24: allGroups.endtime24,
endtime_long: allGroups.endtime_long,
+ date: allGroups.date,
+ month: allGroups.month,
+ day: allGroups.day,
+ year: allGroups.year,
};
} catch (e) {
// If parsing fails, leave starttime/endtime as placeholders
@@ -781,7 +820,7 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
id="title_template"
name="title_template"
label="Title Template"
- description="Format the EPG title using extracted groups. Use {starttime} (12-hour: '10 PM'), {starttime24} (24-hour: '22:00'), {endtime} (12-hour end), or {endtime24} (24-hour end). Example: {league} - {team1} vs {team2} ({starttime}-{endtime})"
+ description="Format the EPG title using extracted groups. Use {starttime} (12-hour: '10 PM'), {starttime24} (24-hour: '22:00'), {endtime} (12-hour end), {endtime24} (24-hour end), {date} (YYYY-MM-DD), {month}, {day}, or {year}. Date/time placeholders respect Output Timezone settings. Example: {league} - {team1} vs {team2} ({starttime}-{endtime})"
placeholder="{league} - {team1} vs {team2}"
value={titleTemplate}
onChange={(e) => {
@@ -795,7 +834,7 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
id="description_template"
name="description_template"
label="Description Template"
- description="Format the EPG description using extracted groups. Use {starttime} (12-hour), {starttime24} (24-hour), {endtime} (12-hour end), or {endtime24} (24-hour end). Example: Watch {team1} take on {team2} from {starttime} to {endtime}!"
+ description="Format the EPG description using extracted groups. Use {starttime} (12-hour), {starttime24} (24-hour), {endtime} (12-hour end), {endtime24} (24-hour end), {date} (YYYY-MM-DD), {month}, {day}, or {year}. Date/time placeholders respect Output Timezone settings. Example: Watch {team1} take on {team2} on {date} from {starttime} to {endtime}!"
placeholder="Watch {team1} take on {team2} in this exciting {league} matchup from {starttime} to {endtime}!"
minRows={2}
value={descriptionTemplate}
@@ -825,7 +864,7 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
id="upcoming_title_template"
name="upcoming_title_template"
label="Upcoming Title Template"
- description="Title for programs before the event starts. Use {starttime} (12-hour), {starttime24} (24-hour), {endtime} (12-hour end), or {endtime24} (24-hour end). Example: {team1} vs {team2} starting at {starttime}."
+ description="Title for programs before the event starts. Use {starttime} (12-hour), {starttime24} (24-hour), {endtime} (12-hour end), {endtime24} (24-hour end), {date} (YYYY-MM-DD), {month}, {day}, or {year}. Date/time placeholders respect Output Timezone settings. Example: {team1} vs {team2} starting at {starttime}."
placeholder="{team1} vs {team2} starting at {starttime}."
value={upcomingTitleTemplate}
onChange={(e) => {
@@ -842,7 +881,7 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
id="upcoming_description_template"
name="upcoming_description_template"
label="Upcoming Description Template"
- description="Description for programs before the event. Use {starttime} (12-hour), {starttime24} (24-hour), {endtime} (12-hour end), or {endtime24} (24-hour end). Example: Upcoming: Watch the {league} match up where the {team1} take on the {team2} from {starttime} to {endtime}!"
+ description="Description for programs before the event. Use {starttime} (12-hour), {starttime24} (24-hour), {endtime} (12-hour end), {endtime24} (24-hour end), {date} (YYYY-MM-DD), {month}, {day}, or {year}. Date/time placeholders respect Output Timezone settings. Example: Upcoming: Watch the {league} match up where the {team1} take on the {team2} on {date} from {starttime} to {endtime}!"
placeholder="Upcoming: Watch the {league} match up where the {team1} take on the {team2} from {starttime} to {endtime}!"
minRows={2}
value={upcomingDescriptionTemplate}
@@ -860,7 +899,7 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
id="ended_title_template"
name="ended_title_template"
label="Ended Title Template"
- description="Title for programs after the event has ended. Use {starttime} (12-hour), {starttime24} (24-hour), {endtime} (12-hour end), or {endtime24} (24-hour end). Example: {team1} vs {team2} started at {starttime}."
+ description="Title for programs after the event has ended. Use {starttime} (12-hour), {starttime24} (24-hour), {endtime} (12-hour end), {endtime24} (24-hour end), {date} (YYYY-MM-DD), {month}, {day}, or {year}. Date/time placeholders respect Output Timezone settings. Example: {team1} vs {team2} started at {starttime}."
placeholder="{team1} vs {team2} started at {starttime}."
value={endedTitleTemplate}
onChange={(e) => {
@@ -877,7 +916,7 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
id="ended_description_template"
name="ended_description_template"
label="Ended Description Template"
- description="Description for programs after the event. Use {starttime} (12-hour), {starttime24} (24-hour), {endtime} (12-hour end), or {endtime24} (24-hour end). Example: The {league} match between {team1} and {team2} ran from {starttime} to {endtime}."
+ description="Description for programs after the event. Use {starttime} (12-hour), {starttime24} (24-hour), {endtime} (12-hour end), {endtime24} (24-hour end), {date} (YYYY-MM-DD), {month}, {day}, or {year}. Date/time placeholders respect Output Timezone settings. Example: The {league} match between {team1} and {team2} on {date} ran from {starttime} to {endtime}."
placeholder="The {league} match between {team1} and {team2} ran from {starttime} to {endtime}."
minRows={2}
value={endedDescriptionTemplate}
@@ -1154,42 +1193,6 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
)}
- {/* Show calculated time placeholders when time is extracted */}
- {patternValidation.timeMatch &&
- Object.keys(patternValidation.calculatedPlaceholders || {})
- .length > 0 && (
-
-
- Available Time Placeholders:
-
-
- {Object.entries(
- patternValidation.calculatedPlaceholders
- ).map(([key, value]) => (
-
-
- {'{' + key + '}'}:
-
-
- {value}
-
-
- ))}
-
-
- )}
-
{patternValidation.dateMatch && (
@@ -1231,6 +1234,42 @@ const DummyEPGForm = ({ epg, isOpen, onClose }) => {
)}
+ {/* Show calculated time placeholders when time is extracted */}
+ {patternValidation.timeMatch &&
+ Object.keys(patternValidation.calculatedPlaceholders || {})
+ .length > 0 && (
+
+
+ Available Time Placeholders:
+
+
+ {Object.entries(
+ patternValidation.calculatedPlaceholders
+ ).map(([key, value]) => (
+
+
+ {'{' + key + '}'}:
+
+
+ {value}
+
+
+ ))}
+
+
+ )}
+
{/* Output Preview */}
{(patternValidation.titleMatch ||
patternValidation.timeMatch ||