* Creates a real-time countdown timer displaying days, hours, minutes, and seconds
* until a specified date. Uses WordPress timezone settings and validates date input.
*
* CONFIGURATION REQUIRED
* - CSS: Style classes .countdown-timer, .countdown-segment, .countdown-number, .countdown-label
* - WordPress: Ensure timezone is set correctly in Settings > General
* - Date Format: Must use dd/mm/yyyy format (e.g., 31/12/2025)
* - Time Format: HH:MM:SS or HH:MM (24-hour format) in local timezone. Default: 00:00:00 (midnight)
* - End Text: Optional text to display when countdown reaches zero.
*
* USAGE
* [countdown date="31/12/2025"]
* [countdown date="01/01/2026"]
*
* NOTES
* - Supports multiple timers on same page via static counter
* - Countdown stops at zero (doesn't go negative)
* - Uses site timezone from WordPress settings
* - JavaScript updates every second
* - Timer starts automatically on page load
* - Falls back gracefully with error messages for invalid dates
*/
add_shortcode(
'countdown',
function ( $atts ) {
static $count = 0;
++$count;
// Register script handle once
static $script_enqueued = false;
if ( ! $script_enqueued ) {
wp_register_script( 'bld-countdown-timer', '', [], false, true ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.NoExplicitVersion
wp_enqueue_script( 'bld-countdown-timer' );
$script_enqueued = true;
}
$atts = shortcode_atts(
[
'date' => '', // expects dd/mm/yyyy,
'end_text' => '',
'time' => '00:00:00', // expects HH:MM:SS or HH:MM in 24-hour format
],
$atts,
'countdown'
);
if ( empty( $atts['date'] ) ) {
return 'Countdown date not set.
';
}
// Convert dd/mm/yyyy to yyyy-mm-dd
$parts = explode( '/', $atts['date'] );
if ( count( $parts ) !== 3 ) {
return 'Invalid date format. Use dd/mm/yyyy.
';
}
list($day, $month, $year) = $parts;
if ( ! checkdate( $month, $day, $year ) ) {
return 'Invalid date. Make sure the day, month, and year are valid.
';
}
$time = sanitize_text_field( $atts['time'] );
// Validate time format (HH:MM or HH:MM:SS)
if ( ! preg_match( '/^([01]?[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?$/', $time ) ) {
return 'Invalid time format. Use HH:MM or HH:MM:SS.
';
}
try {
$target_datetime = new DateTime( "$year-$month-$day $time", wp_timezone() );
} catch ( Exception $e ) {
return 'Invalid timezone or date. Please check site settings.
';
}
$iso8601_date = $target_datetime->format( DateTimeInterface::ATOM ); // ISO 8601 format with timezone
$timer_id = 'countdown-timer-' . $count;
$end_text = ! empty( $atts['end_text'] ) ? wp_kses_post( $atts['end_text'] ) : '';
ob_start();
?>