···
+
const COOKIE_LIFETIME = 60 * 60 * 24 * 365; // 1 year
+
const SITE_URL = 'https://tz.rita.moe';
+
// Set security headers
+
$nonce = bin2hex(random_bytes(16));
+
header("Content-Security-Policy: default-src 'none'; script-src 'self' 'nonce-$nonce'; img-src 'self'; style-src 'self'; form-action 'self';");
+
header('X-Content-Type-Options: nosniff');
+
header('X-Frame-Options: DENY');
+
header('X-XSS-Protection: 1; mode=block');
function getOffset($tz) {
$tz = new DateTime('now', new DateTimeZone($tz));
$offset = $tz->format('T');
···
function setUserCookie($name, $value) {
+
setcookie($name, $value, time() + COOKIE_LIFETIME, '/');
$_COOKIE[$name] = $value;
+
'America/Los_Angeles', // PST/PDT
+
'America/New_York', // EST/EDT
+
'Europe/London', // BST/GMT
+
'Europe/Paris', // CEST/CET
+
'Asia/Dubai', // UTC+04 "UAE"
+
'Australia/Sydney', // AEST/AEDT
+
$allTZ = timezone_identifiers_list(DateTimeZone::ALL);
// User sets timezone cookie
+
if (isset($_POST['user-tz']) && in_array($_POST['user-tz'], $allTZ)) {
setUserCookie('user-tz', $_POST['user-tz']);
···
+
if (isset($_POST['datetime']) && isset($_POST['timezone'])) {
+
// -- Redirect to submitted date
+
$postTZ = $_POST['timezone'];
+
// Handle if timezone is an offset
+
if (is_numeric($_POST['timezone'])) {
+
$isNeg = str_starts_with($postTZ, '-');
+
$h = str_pad(abs(intdiv($postTZ, 60)), 2, '0', STR_PAD_LEFT);
+
$m = str_pad($postTZ % 60, 2, '0', STR_PAD_LEFT);
+
$postTZ = ($isNeg ? '-' : '+') . $h . $m;
+
// Make our date object
+
$dateObj = new DateTime($_POST['datetime'], new DateTimeZone($postTZ));
+
// Redirect, with the "+" replaced by "_"
+
header('Location: /' . str_replace('+', '_', $dateObj->format('c')));
+
} elseif ($_SERVER['REQUEST_URI'] !== '/') {
+
$req = substr($_SERVER['REQUEST_URI'], 1);
+
// First check if date is following the format
+
$re = '/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[\_|-]\d{2}:\d{2}$/';
+
if (!preg_match($re, $req)) {
+
http_response_code(404);
+
$error = 'Date is not valid, wrong format, or file not found.';
+
// Make our date object, convert back "_" to "+"
+
$dt = new DateTime(str_replace('_', '+', $req));
+
} catch (Exception $e) {
+
http_response_code(400);
+
$error = 'Invalid date format or date out of range.';
···
<meta name="viewport" content="width=device-width, initial-scale=1.0">
+
<title>Time Zones Are Hard<?= isset($req) ? ' - ' . htmlspecialchars(str_replace('_', '+', $req), ENT_QUOTES, 'UTF-8') : '' ?></title>
<link rel="stylesheet" href="/css/styles.css">
<?php if (isset($dt)) { ?>
<meta name="robots" content="noindex,noarchive">
<script src="/js/dayjs.min.js"></script>
<script src="/js/relativeTime.js"></script>
+
<script nonce="<?= $nonce ?>">
dayjs.extend(window.dayjs_plugin_relativeTime)
···
Please select your local time zone <small>(or enable JavaScript)</small>:<br/>
+
<select required id="user-tz" name="user-tz" aria-label="Timezone selector">
<option selected disabled>(pick one)</option>
<optgroup label="Popular">
<?php foreach($popTZ as $tz) { ?>
···
+
<?php foreach($allTZ as $tz) { ?>
<option value="<?= $tz ?>">
<?= str_replace('_', ' ', $tz) ?> (<?= getOffset($tz) ?>)
···
+
<input type="checkbox" id="user-24" name="user-24" aria-label="Use 24-hour time format instead of 12-hour AM/PM"/>
<label for="user-24"><span>24h format</span></label>
<button type="submit">Submit</button>
···
+
<input type="hidden" id="url" value="<?= htmlspecialchars(SITE_URL . $_SERVER['REQUEST_URI'], ENT_QUOTES, 'UTF-8') ?>"/>
+
<script nonce="<?= $nonce ?>">
// Make our day.js object
const djs = dayjs('<?= $dt->format('c') ?>')
···
if (navigator.canShare) {
// Use native navigator share
+
url: '<?= htmlspecialchars(SITE_URL . $_SERVER['REQUEST_URI'], ENT_QUOTES, 'UTF-8') ?>'
} else if (navigator.clipboard) {
+
navigator.clipboard.writeText('<?= htmlspecialchars(SITE_URL . $_SERVER['REQUEST_URI'], ENT_QUOTES, 'UTF-8') ?>')
alert('URL copied to clipboard!')
···
alert('URL copied to clipboard!')
// Fallback to prompt with URL
+
prompt('Share this URL:', shareTextInput.value)
···
+
<?php foreach($allTZ as $tz) { ?>
<option value="<?= $tz ?>">
<?= str_replace('_', ' ', $tz) ?> (<?= getOffset($tz) ?>)
···
<button type="submit">Submit</button>
+
<script nonce="<?= $nonce ?>">
···
<?php if ($_COOKIE['has-js'] !== '1') { ?>
+
<script nonce="<?= $nonce ?>">
// Save on the document size if user has JavaScript
+
date.setTime(date.getTime() + (1000 * <?= COOKIE_LIFETIME ?>))
document.cookie = 'has-js=1; expires=' + date.toUTCString() + '; path=/'