Setting the Date/Time Format

When you are working with dates and times in loops or cards, remembering the different formats can be challenging. To that end, I put together a component and a shortcode as seen here. Feel free to use these as you see fit. You can also just go to the official WP documentation linked above.

<?php
/**
 * Shortcode: [fw_date_formatter]
 */

add_shortcode( 'fw_date_formatter', 'fw_date_formatter_shortcode' );

function fw_date_formatter_shortcode( $atts = [] ) {
	static $instance = 0;
	static $script_printed = false;

	$instance++;

	$atts = shortcode_atts(
		[
			'date'        => '1/1/2027',
			'time'        => '10:30',
			'ampm'        => 'PM',
			'format'      => 'date_long',
			'show_format' => 'true',
		],
		$atts,
		'fw_date_formatter'
	);

	$id = 'fw-date-formatter-' . $instance;

	$formats = [
		'date_full'      => 'Date Full',
		'date_long'      => 'Date Long',
		'date_medium'    => 'Date Medium',
		'date_short'     => 'Date Short',
		'y_m_d'          => 'Y-m-d',
		'iso_datetime'   => 'ISO Date and Time',
		'time_full'      => 'Time Full',
		'time_long'      => 'Time Long',
		'time_medium'    => 'Time Medium',
		'time_short'     => 'Time Short',
		'datetime_full'  => 'Date and Time Full',
		'datetime_long'  => 'Date and Time Long',
		'datetime_short' => 'Date and Time Short',
	];

	ob_start();
	?>

	<div
		id="<?php echo esc_attr( $id ); ?>"
		class="fw-date-formatter"
		data-fw-date-formatter
	>
		<div class="fw-date-formatter__fields">
			<div class="fw-date-formatter__field">
				<label class="fw-date-formatter__label" for="<?php echo esc_attr( $id ); ?>-date">
					Date
				</label>
				<input
					class="fw-date-formatter__input"
					id="<?php echo esc_attr( $id ); ?>-date"
					type="text"
					value="<?php echo esc_attr( $atts['date'] ); ?>"
					placeholder="m/d/yyyy"
					data-fw-date-input
				>
			</div>

			<div class="fw-date-formatter__field">
				<label class="fw-date-formatter__label" for="<?php echo esc_attr( $id ); ?>-time">
					Time
				</label>
				<input
					class="fw-date-formatter__input"
					id="<?php echo esc_attr( $id ); ?>-time"
					type="text"
					value="<?php echo esc_attr( $atts['time'] ); ?>"
					placeholder="hh:mm"
					data-fw-time-input
				>
			</div>

			<div class="fw-date-formatter__field">
				<label class="fw-date-formatter__label" for="<?php echo esc_attr( $id ); ?>-ampm">
					AM/PM
				</label>
				<select
					class="fw-date-formatter__select"
					id="<?php echo esc_attr( $id ); ?>-ampm"
					data-fw-ampm-input
				>
					<option value="24H" <?php selected( $atts['ampm'], '24H' ); ?>>24H</option>
					<option value="AM" <?php selected( $atts['ampm'], 'AM' ); ?>>AM</option>
					<option value="PM" <?php selected( $atts['ampm'], 'PM' ); ?>>PM</option>
				</select>
			</div>

			<div class="fw-date-formatter__field">
				<label class="fw-date-formatter__label" for="<?php echo esc_attr( $id ); ?>-format">
					Format
				</label>
				<select
					class="fw-date-formatter__select"
					id="<?php echo esc_attr( $id ); ?>-format"
					data-fw-format-input
				>
					<?php foreach ( $formats as $value => $label ) : ?>
						<option value="<?php echo esc_attr( $value ); ?>" <?php selected( $atts['format'], $value ); ?>>
							<?php echo esc_html( $label ); ?>
						</option>
					<?php endforeach; ?>
				</select>
			</div>

			<div class="fw-date-formatter__field fw-date-formatter__field--checkbox">
				<input
					class="fw-date-formatter__checkbox"
					id="<?php echo esc_attr( $id ); ?>-show-format"
					type="checkbox"
					data-fw-show-format-input
					<?php checked( filter_var( $atts['show_format'], FILTER_VALIDATE_BOOLEAN ) ); ?>
				>
				<label class="fw-date-formatter__label" for="<?php echo esc_attr( $id ); ?>-show-format">
					Show dateFormat
				</label>
			</div>
		</div>

		<p class="fw-date-formatter__result">
			<time class="fw-date-formatter__output" data-fw-date-output></time>
		</p>
	</div>

	<?php if ( ! $script_printed ) : ?>
		<script>
			(() => {
				const wpFormatMap = {
					date_full: 'l, F j, Y',
					date_long: 'F j, Y',
					date_medium: 'M j, Y',
					date_short: 'n/j/y',
					y_m_d: 'Y-m-d',
					iso_datetime: 'c',
					time_full: 'g:i:s A T',
					time_long: 'g:i:s A',
					time_medium: 'g:i A',
					time_short: 'g:i A',
					datetime_full: 'l, F j, Y g:i A',
					datetime_long: 'F j, Y g:i A',
					datetime_short: 'n/j/y g:i A',
				};

				const monthsFull = ['January','February','March','April','May','June','July','August','September','October','November','December'];
				const monthsShort = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
				const daysFull = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];

				const pad = (value) => String(value).padStart(2, '0');

				function formatWithWpTokens(date, format) {
					const hours24 = date.getHours();
					const hours12 = hours24 % 12 || 12;

					const map = {
						Y: date.getFullYear(),
						y: String(date.getFullYear()).slice(-2),
						F: monthsFull[date.getMonth()],
						M: monthsShort[date.getMonth()],
						m: pad(date.getMonth() + 1),
						n: date.getMonth() + 1,
						d: pad(date.getDate()),
						j: date.getDate(),
						l: daysFull[date.getDay()],
						H: pad(hours24),
						G: hours24,
						h: pad(hours12),
						g: hours12,
						i: pad(date.getMinutes()),
						s: pad(date.getSeconds()),
						A: hours24 >= 12 ? 'PM' : 'AM',
						a: hours24 >= 12 ? 'pm' : 'am',
						T: Intl.DateTimeFormat('en-US', { timeZoneName: 'short' })
							.formatToParts(date)
							.find((part) => part.type === 'timeZoneName')?.value || '',
						c: date.toISOString(),
					};

					return format.replace(/[YyFMmndjlHGhgisAaTc]/g, (token) => map[token] ?? token);
				}

				function updateFormatter(formatter) {
					const dateInput = formatter.querySelector('[data-fw-date-input]');
					const timeInput = formatter.querySelector('[data-fw-time-input]');
					const ampmInput = formatter.querySelector('[data-fw-ampm-input]');
					const formatInput = formatter.querySelector('[data-fw-format-input]');
					const showFormatInput = formatter.querySelector('[data-fw-show-format-input]');
					const output = formatter.querySelector('[data-fw-date-output]');

					if (!dateInput || !timeInput || !ampmInput || !formatInput || !output) return;

					const dateValue = dateInput.value.trim();
					const timeValue = timeInput.value.trim();
					const ampm = ampmInput.value.trim();
					const formatKey = formatInput.value || 'date_long';
					const format = wpFormatMap[formatKey] || 'F j, Y';

					const combinedTime = `${timeValue || '00:00'} ${ampm && ampm !== '24H' ? ampm : ''}`.trim();
					const date = new Date(`${dateValue} ${combinedTime}`);

					if (Number.isNaN(date.getTime())) {
						output.textContent = 'Enter a valid date and time.';
						output.removeAttribute('datetime');
						return;
					}

					const formatted = formatWithWpTokens(date, format);
					const showFormat = showFormatInput?.checked;

					output.textContent = showFormat
						? `${formatted} (use dateFormat '${format}')`
						: formatted;

					output.setAttribute('datetime', date.toISOString());
				}

				document.querySelectorAll('[data-fw-date-formatter]').forEach((formatter) => {
					updateFormatter(formatter);

					formatter.addEventListener('input', () => updateFormatter(formatter));
					formatter.addEventListener('change', () => updateFormatter(formatter));
				});
			})();
		</script>
		<?php $script_printed = true; ?>
	<?php endif; ?>

	<?php
	return ob_get_clean();
}

Leave a Reply

Your email address will not be published. Required fields are marked *