#EXT-X-PROGRAM-DATE-TIME Explained: Absolute Timestamps, DVR Seeking, and Multi-Rendition Sync
August 4, 2026 · 6 min read
Key takeaways
- #EXT-X-PROGRAM-DATE-TIME attaches an ISO 8601 UTC timestamp to one segment in a media playlist, letting a player map any segment's position to a real wall-clock time.
- Without it, a live player can only express time as 'N seconds from the start of the playlist window' — which shifts every time old segments are removed.
- DVR scrubbing, chapter markers, and caption sync to broadcast times all depend on PROGRAM-DATE-TIME being present and accurate.
- Multi-rendition sync relies on PROGRAM-DATE-TIME to align the audio, video, and subtitle tracks — renditions without it can drift.
- This site's /play player inherits hls.js's PROGRAM-DATE-TIME support; /convert ignores it since the converter only enumerates segments and doesn't model wall-clock time.
An HLS media playlist has two ways to express time. The first is relative: segment durations accumulate from the beginning of the playlist window, giving every position a value in seconds-from-start. The second is absolute: a single tag pins one segment to a real wall-clock timestamp, from which every other segment's real time can be calculated. That tag is #EXT-X-PROGRAM-DATE-TIME.
What the tag looks like and what it declares
#EXT-X-PROGRAM-DATE-TIME appears immediately before a #EXTINF line in a media playlist:
#EXT-X-PROGRAM-DATE-TIME:2026-08-01T18:30:00.000Z
#EXTINF:6.000,
https://cdn.example.com/seg042.tsThe value is an ISO 8601 date-time string with millisecond precision. It declares the wall-clock time at which the tagged segment begins. From that anchor, the player can calculate the real start time of every other segment in the playlist by summing or subtracting the #EXTINF durations of the segments between them.
The tag only needs to appear once per discontinuity, but it may appear more often. Some encoders emit it for every segment; others only at the first segment of each discontinuity boundary. Both are valid per the spec — a player accumulates durations forward from whichever anchor it finds.
Why relative time is insufficient for live streams
A live HLS playlist is a sliding window. As new segments are appended, old ones are removed. The #EXT-X-MEDIA-SEQUENCE number increments to track how many segments have been dropped, but the relative time offsets within the window reset with each poll.
A player keeping track of "how far into the stream" a viewer is, using only relative time, will drift every time it re-fetches the playlist and computes offsets from the new window start. Two things break in practice:
- DVR seeking.If you want to let a viewer scrub back 30 minutes in a live broadcast, the player needs to know which historical segment corresponds to "30 minutes ago" in wall time — not just relative to the current window. Without
#EXT-X-PROGRAM-DATE-TIME, the player has no stable reference for this calculation. - Display of current program time.Broadcast players often show the current time in the stream ("Live: 7:32 PM") or a relative offset ("-14:22 from live"). Both require knowing the real clock time that corresponds to the current playhead position.
Multi-rendition synchronization
A master playlist typically includes several media renditions: multiple audio tracks, subtitle tracks, and video variants at different bitrates. Each rendition has its own media playlist with its own segment numbering. The renditions are independent files — there is no shared timeline in the manifest format itself.
#EXT-X-PROGRAM-DATE-TIME is the mechanism that ties them together. When a player switches renditions — because the viewer changed audio language, or because ABR selected a different bitrate variant — it uses the PROGRAM-DATE-TIME values in both the old and new renditions to find the segment in the new rendition that corresponds to the current wall-clock position. This keeps the playhead in the right place after a rendition switch.
A rendition that is missing #EXT-X-PROGRAM-DATE-TIME— or that has timestamps out of sync with the other renditions — can produce an audio/video drift after an ABR switch, or incorrect subtitle timing after a language change. The drift may be small (a few hundred milliseconds) or large, depending on how far the encoder's clock has wandered.
Chapter markers and timed metadata
Some streaming platforms use wall-clock-aligned #EXT-X-DATERANGE tags to declare chapter boundaries, ad break start and end points, or event markers. The START-DATE attribute of #EXT-X-DATERANGE is an ISO 8601 timestamp. For the player to know which segment corresponds to a given START-DATE, it must have a #EXT-X-PROGRAM-DATE-TIME anchor in the same playlist. The two tags work as a pair: one anchors the timeline, the other places events on it.
This is the mechanism behind the "skip intro" button and live chapter navigation in players that support them. Strip out #EXT-X-PROGRAM-DATE-TIME from a stream that uses #EXT-X-DATERANGE markers and those features stop working, even though nothing else in the playlist has changed.
Clock accuracy requirements
The #EXT-X-PROGRAM-DATE-TIMEvalue is only as accurate as the encoder's clock. An encoder with a clock that drifts 200 milliseconds per hour introduces 200 ms of wall-time error per hour into the stream. Over a twelve-hour broadcast, that accumulates to nearly 2.5 seconds — enough to place a DVR seek noticeably off-target or to put a subtitle track slightly out of sync.
Production encoders synchronize their clocks to NTP or PTP (Precision Time Protocol) to keep this error small. Consumer-grade streams and re-encoded content sometimes skip this step, which is why DVR seeking is occasionally inaccurate on lower-budget streams even when the tag is present.
How hls.js uses PROGRAM-DATE-TIME
hls.js reads #EXT-X-PROGRAM-DATE-TIME when parsing a media playlist and stores the mapping from programDateTime to each fragment. It exposes the current wall-clock position through the Hls.Events.FRAG_CHANGED event's programDateTime field, and uses the stored mapping to implement its seekTo functionality when a target time is expressed as a Date rather than a numeric offset.
This site's /play player uses hls.js and inherits this behavior. The live broadcast mode (broadcast=1) in particular relies on hls.js's playlist polling and position tracking, both of which benefit from PROGRAM-DATE-TIME being present.
What this site's converter does with it
The /convert route fetches a playlist, enumerates its segment URLs, downloads them in order, and muxes them into a single MP4 using ffmpeg.wasm. The converter doesn't model wall-clock time at all — it only needs the sequence of segment URLs and their durations. As a result, #EXT-X-PROGRAM-DATE-TIMEtags are simply ignored during conversion. The output MP4's timestamps are relative to the start of the downloaded segment range, not to the original broadcast time.
For most use cases this is fine. The video plays correctly; the timeline in a video player shows time-from-start. If you need the output to carry the original broadcast timestamps, you would need a separate muxing step that reads the PROGRAM-DATE-TIME values and sets the corresponding MP4 creation and edit list metadata — which is outside what a stream-copy converter does.
Detecting PROGRAM-DATE-TIME manually
If you're investigating a stream — for example, one that the Stream Video Downloader extension detected on a page — and want to know whether it carries wall-clock timestamps, fetch the media playlist URL directly (not the master playlist) and search for #EXT-X-PROGRAM-DATE-TIMEin the response. If the tag is absent from a live stream, DVR features and multi-rendition sync will degrade. If it's present, check that the timestamps are plausible: a value stuck at a date years in the past signals an encoder clock issue rather than a valid reference.