Event Timer and Date Countdown Script
Countdown is a Perl CGI script that displays the time remaining until a specific date or event. You can configure the countdown to show seconds, minutes, hours, days, months, or years. Perfect for launch pages, event announcements, promotions, and milestone celebrations.
JavaScript countdown libraries provide real-time updates, animations, and rich styling without page refreshes.
Lightweight, performant flip clock. Multiple instances, custom themes, completion callback. Pure JavaScript.
npm install flipdown
Zero dependencies, TypeScript support. Multiple themes (dark, cyber, circle). Perfect for landing pages.
npm install simplycountdown.js
Stopwatch/countdown library. Event-driven, configurable precision. Works with Node.js and browsers.
npm install easytimer.js
Use Day.js for date math with custom rendering. Tiny (2KB), immutable, works like Moment.js.
npm install dayjs
No library needed! See our code examples above for a production-ready vanilla JS solution.
| Feature | Perl CGI (1990s) | Modern JavaScript |
|---|---|---|
| Real-time updates | No (page refresh) | Yes |
| Server required | Yes (CGI) | No |
| Animations | None | Flip, fade, etc. |
| Timezone handling | Server TZ only | User's local TZ |
| Multiple countdowns | Multiple iframes | Easy |
| Mobile-friendly | Basic | Responsive |
| Completion action | None | Callbacks, redirects |
The Countdown script calculates the time difference between the current date/time and a target date you specify. It can be configured to show the countdown at various levels of precision, from years down to seconds.
| File | Description |
|---|---|
countdown.pl |
Main Perl script that calculates and displays the countdown |
countdown.html |
Example HTML file showing various implementation methods |
README |
Installation instructions and configuration guide |
Display countdown in years, months, days, hours, minutes, or seconds - choose the precision that fits your needs.
Specify target dates in a simple format: year, month, day, hour, minute, second.
Configure the HTML template to match your site's design and style.
Pass target date via URL query string for dynamic countdown pages.
Calculate time elapsed since past dates for "time since" displays.
Can be modified to work with Server Side Includes for inline display.
| URL Format | Description | Example Output |
|---|---|---|
countdown.cgi?2025,12,31,0,0,0 |
Countdown to New Year's Eve 2025 | 15 days, 6 hours, 23 minutes, 45 seconds |
countdown.cgi?2025,7,4,12,0,0 |
Countdown to July 4th, 2025 at noon | 200 days, 18 hours, 0 minutes, 0 seconds |
countdown.cgi?2026,1,1,0,0,0 |
Full year countdown | 1 year, 15 days, 6 hours... |
countdown.cgi?YEAR,MONTH,DAY,HOUR,MINUTE,SECOND
countdown.pl to your cgi-bin directory.
#!/usr/bin/perl).
chmod 755 countdown.pl
#!/usr/bin/perl
use strict;
use warnings;
use Time::Local;
use CGI;
my $cgi = CGI->new;
# Get target date from query string
my $query = $ENV{'QUERY_STRING'} || '2025,1,1,0,0,0';
my ($year, $month, $day, $hour, $min, $sec) = split(/,/, $query);
# Validate input
$year ||= 2025;
$month ||= 1;
$day ||= 1;
$hour ||= 0;
$min ||= 0;
$sec ||= 0;
# Calculate target timestamp
my $target = eval {
timelocal($sec, $min, $hour, $day, $month - 1, $year);
};
if ($@) {
print $cgi->header('text/html');
print "Invalid date specified";
exit;
}
# Calculate difference
my $now = time();
my $diff = $target - $now;
my $past = 0;
if ($diff < 0) {
$past = 1;
$diff = abs($diff);
}
# Break down the difference
my $days = int($diff / 86400);
my $hours = int(($diff % 86400) / 3600);
my $minutes = int(($diff % 3600) / 60);
my $seconds = $diff % 60;
# Output
print $cgi->header('text/html');
print $cgi->start_html('Countdown');
if ($past) {
print "Time Since Event
";
} else {
print "Countdown
";
}
print "$days days, $hours hours, $minutes minutes, $seconds seconds
";
print $cgi->end_html;
exit 0;
<?php
/**
* Countdown Timer - PHP Version
*/
// Get target date from query string or use default
$target_str = $_GET['date'] ?? '2025-01-01 00:00:00';
try {
$target = new DateTime($target_str);
$now = new DateTime();
$diff = $target->diff($now);
$is_past = $target < $now;
} catch (Exception $e) {
die("Invalid date format. Use: YYYY-MM-DD HH:MM:SS");
}
// Function to format countdown
function formatCountdown($diff, $is_past) {
$parts = [];
if ($diff->y > 0) {
$parts[] = $diff->y . ' ' . ($diff->y == 1 ? 'year' : 'years');
}
if ($diff->m > 0) {
$parts[] = $diff->m . ' ' . ($diff->m == 1 ? 'month' : 'months');
}
if ($diff->d > 0) {
$parts[] = $diff->d . ' ' . ($diff->d == 1 ? 'day' : 'days');
}
if ($diff->h > 0) {
$parts[] = $diff->h . ' ' . ($diff->h == 1 ? 'hour' : 'hours');
}
if ($diff->i > 0) {
$parts[] = $diff->i . ' ' . ($diff->i == 1 ? 'minute' : 'minutes');
}
if ($diff->s > 0) {
$parts[] = $diff->s . ' ' . ($diff->s == 1 ? 'second' : 'seconds');
}
$prefix = $is_past ? 'Time since event: ' : 'Time remaining: ';
return $prefix . implode(', ', $parts);
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Countdown</title>
</head>
<body>
<h1><?php echo $is_past ? 'Time Since' : 'Countdown'; ?></h1>
<p><?php echo formatCountdown($diff, $is_past); ?></p>
<!-- Total seconds remaining (useful for JavaScript) -->
<p>Total seconds: <?php
$total = $diff->days * 86400 + $diff->h * 3600 + $diff->i * 60 + $diff->s;
echo $is_past ? -$total : $total;
?></p>
</body>
</html>
/**
* Countdown Timer - JavaScript Version
* Real-time updating countdown with no page refresh
*/
class CountdownTimer {
constructor(element, targetDate, options = {}) {
this.element = typeof element === 'string'
? document.querySelector(element)
: element;
this.target = new Date(targetDate);
this.options = {
showYears: true,
showMonths: true,
showDays: true,
showHours: true,
showMinutes: true,
showSeconds: true,
onComplete: null,
updateInterval: 1000,
...options
};
this.intervalId = null;
this.start();
}
start() {
this.update();
this.intervalId = setInterval(() => this.update(), this.options.updateInterval);
}
stop() {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = null;
}
}
update() {
const now = new Date();
let diff = this.target - now;
const isPast = diff < 0;
if (isPast) {
diff = Math.abs(diff);
}
const time = this.calculateTime(diff);
this.render(time, isPast);
if (diff <= 0 && this.options.onComplete) {
this.options.onComplete();
this.stop();
}
}
calculateTime(ms) {
const seconds = Math.floor(ms / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
const months = Math.floor(days / 30);
const years = Math.floor(days / 365);
return {
years: years,
months: months % 12,
days: days % 30,
hours: hours % 24,
minutes: minutes % 60,
seconds: seconds % 60,
totalDays: days,
totalHours: hours,
totalMinutes: minutes,
totalSeconds: seconds
};
}
render(time, isPast) {
const parts = [];
if (this.options.showYears && time.years > 0) {
parts.push(`${time.years} ${time.years === 1 ? 'year' : 'years'}`);
}
if (this.options.showMonths && time.months > 0) {
parts.push(`${time.months} ${time.months === 1 ? 'month' : 'months'}`);
}
if (this.options.showDays && time.days > 0) {
parts.push(`${time.days} ${time.days === 1 ? 'day' : 'days'}`);
}
if (this.options.showHours) {
parts.push(`${time.hours} ${time.hours === 1 ? 'hour' : 'hours'}`);
}
if (this.options.showMinutes) {
parts.push(`${time.minutes} ${time.minutes === 1 ? 'minute' : 'minutes'}`);
}
if (this.options.showSeconds) {
parts.push(`${time.seconds} ${time.seconds === 1 ? 'second' : 'seconds'}`);
}
const prefix = isPast ? 'Time since: ' : '';
this.element.innerHTML = prefix + parts.join(', ');
}
// Static method for simple countdown display
static simple(elementId, targetDate) {
return new CountdownTimer(elementId, targetDate);
}
}
// Usage examples:
// Basic usage
new CountdownTimer('#countdown', '2025-12-31T00:00:00');
// With options
new CountdownTimer('#event-countdown', '2025-07-04T12:00:00', {
showYears: false,
showMonths: false,
onComplete: () => {
alert('Event has started!');
}
});
// Simple one-liner
CountdownTimer.simple('#timer', 'January 1, 2026');
<!-- Link to countdown page -->
<a href="/cgi-bin/countdown.cgi?2025,12,31,0,0,0">
Countdown to New Year 2026!
</a>
<!-- Embed in iframe -->
<iframe src="/cgi-bin/countdown.cgi?2025,7,4,0,0,0"
width="400" height="100" frameborder="0">
</iframe>
<!-- SSI include (if modified for SSI) -->
<!--#exec cgi="/cgi-bin/countdown.cgi?2025,12,25,0,0,0"-->
<!-- Modern JavaScript approach (recommended) -->
<div id="my-countdown"></div>
<script>
// Countdown to specific date
new CountdownTimer('#my-countdown', '2025-12-31T23:59:59', {
onComplete: function() {
document.getElementById('my-countdown').innerHTML =
'<span class="text-success">Happy New Year!</span>';
}
});
</script>
<!-- Bootstrap card with countdown -->
<div class="card">
<div class="card-header bg-primary text-white">
<i class="bi bi-clock"></i> Special Sale Ends In:
</div>
<div class="card-body text-center">
<div id="sale-countdown" class="display-6"></div>
</div>
</div>
Main Perl script that calculates and displays the countdown
Example HTML file with implementation examples
Installation instructions and configuration guide
<a href="/">Return to Home</a>. You can also pass a return URL as an additional query parameter and include it dynamically in the output.
onComplete callback that fires when the countdown reaches zero.
new CountdownTimer() instances with different target elements and dates.