Adapt relative date logic from F# version
This commit is contained in:
parent
b759c3494e
commit
3ebb03d470
@ -526,45 +526,45 @@ let routes = [
|
||||
GET_HEAD [ route "/" Home.home ]
|
||||
subRoute "/components/" [
|
||||
GET_HEAD [
|
||||
route "journal-items" Components.journalItems
|
||||
route "journal-items" Components.journalItems // done
|
||||
routef "request/%s/add-notes" Components.addNotes
|
||||
routef "request/%s/item" Components.requestItem
|
||||
routef "request/%s/notes" Components.notes
|
||||
routef "request/%s/snooze" Components.snooze
|
||||
]
|
||||
]
|
||||
GET_HEAD [ route "/docs" Home.docs ]
|
||||
GET_HEAD [ route "/journal" Journal.journal ]
|
||||
GET_HEAD [ route "/docs" Home.docs ] // done
|
||||
GET_HEAD [ route "/journal" Journal.journal ] // done
|
||||
subRoute "/legal/" [
|
||||
GET_HEAD [
|
||||
route "privacy-policy" Legal.privacyPolicy
|
||||
route "terms-of-service" Legal.termsOfService
|
||||
route "privacy-policy" Legal.privacyPolicy // done
|
||||
route "terms-of-service" Legal.termsOfService // done
|
||||
]
|
||||
]
|
||||
subRoute "/request" [
|
||||
GET_HEAD [
|
||||
routef "/%s/edit" Request.edit
|
||||
routef "/%s/full" Request.getFull
|
||||
route "s/active" Request.active
|
||||
route "s/answered" Request.answered
|
||||
routef "/%s/edit" Request.edit // done
|
||||
routef "/%s/full" Request.getFull // done
|
||||
route "s/active" Request.active // done
|
||||
route "s/answered" Request.answered // done
|
||||
route "s/snoozed" Request.snoozed
|
||||
]
|
||||
PATCH [
|
||||
route "" Request.update
|
||||
route "" Request.update // done
|
||||
routef "/%s/cancel-snooze" Request.cancelSnooze
|
||||
routef "/%s/prayed" Request.prayed
|
||||
routef "/%s/prayed" Request.prayed // done
|
||||
routef "/%s/show" Request.show
|
||||
routef "/%s/snooze" Request.snooze
|
||||
]
|
||||
POST [
|
||||
route "" Request.add
|
||||
route "" Request.add // done
|
||||
routef "/%s/note" Request.addNote
|
||||
]
|
||||
]
|
||||
subRoute "/user/" [
|
||||
GET_HEAD [
|
||||
route "log-off" User.logOff
|
||||
route "log-on" User.logOn
|
||||
route "log-off" User.logOff // done
|
||||
route "log-on" User.logOn // done
|
||||
]
|
||||
]
|
||||
]
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace MyPrayerJournal;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use BitBadger\PDODocument\{Custom, DocumentException, DocumentList, Find, Mapper\DocumentMapper};
|
||||
use Exception;
|
||||
use JsonSerializable;
|
||||
|
@ -6,6 +6,7 @@ use BitBadger\PDODocument\DocumentException;
|
||||
use BitBadger\PDODocument\DocumentList;
|
||||
use DateTimeImmutable;
|
||||
use DateTimeZone;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* User interface building blocks
|
||||
@ -103,33 +104,69 @@ class UI
|
||||
foreach ($attrs as $key => $value) echo " $key=\"" . htmlspecialchars($value) . "\""; ?>><?=$text?></a><?php
|
||||
}
|
||||
|
||||
// Thanks, William Entriken! https://stackoverflow.com/a/42446994/276707
|
||||
/** @var array Periods for the relative date/time display */
|
||||
private static array $periods = [
|
||||
[ 60, 1, '%s seconds ago', 'a second ago'],
|
||||
[ 60*100, 60, '%s minutes ago', 'one minute ago'],
|
||||
[ 3600*70, 3600, '%s hours ago', 'an hour ago'],
|
||||
[ 3600*24*10, 3600*24, '%s days ago', 'yesterday'],
|
||||
[ 3600*24*30, 3600*24*7, '%s weeks ago', 'a week ago'],
|
||||
[3600*24*30*30, 3600*24*30, '%s months ago', 'last month'],
|
||||
[ INF, 3600*24*265, '%s years ago', 'last year']
|
||||
];
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function relativeDate(string $date): void
|
||||
{
|
||||
$parsed = new DateTimeImmutable($date);
|
||||
$diff = time() - $parsed->getTimestamp();
|
||||
foreach (self::$periods as $period) {
|
||||
if ($diff > $period[0]) continue;
|
||||
$diff = floor($diff / $period[1]);
|
||||
$value = $diff > 1 ? sprintf($period[2], $diff) : $period[3];
|
||||
$inZone = $parsed->setTimezone(new DateTimeZone($_SERVER['HTTP_X_TIME_ZONE'] ?? 'Etc/UTC'));
|
||||
echo '<span title="' . date_format($inZone, 'l, F j, Y \a\t g:ia T') . '">' . $value . '</span>';
|
||||
break;
|
||||
}
|
||||
$inZone = $parsed->setTimezone(new DateTimeZone($_SERVER['HTTP_X_TIME_ZONE'] ?? 'Etc/UTC'));
|
||||
echo '<span title="' . date_format($inZone, 'l, F j, Y \a\t g:ia T') . '">'
|
||||
. self::formatDistance('now', $parsed) . '</span>';
|
||||
}
|
||||
|
||||
// Many thanks to date-fns (https://date-fns.org) for this logic
|
||||
/**
|
||||
* Format the distance between two dates
|
||||
*
|
||||
* @param string|DateTimeImmutable $from The starting date/time
|
||||
* @param string|DateTimeImmutable $to The ending date/time
|
||||
* @return string The distance between two dates
|
||||
* @throws Exception If date/time objects cannot be created
|
||||
*/
|
||||
public static function formatDistance(string|DateTimeImmutable $from, string|DateTimeImmutable $to): string
|
||||
{
|
||||
$aDay = 1_440.0;
|
||||
$almost2Days = 2_520.0;
|
||||
$aMonth = 43_200.0;
|
||||
$twoMonths = 86_400.0;
|
||||
|
||||
$dtFrom = is_string($from) ? new DateTimeImmutable($from) : $from;
|
||||
$dtTo = is_string($to) ? new DateTimeImmutable($to) : $to;
|
||||
$minutes = abs($dtFrom->getTimestamp() - $dtTo->getTimestamp()) / 60;
|
||||
$months = round($minutes / $aMonth);
|
||||
$years = round($months / 12);
|
||||
|
||||
$typeAndNumber = match (true) {
|
||||
$minutes < 1.0 => [FormatDistanceToken::LessThanXMinutes, 1],
|
||||
$minutes < 45.0 => [FormatDistanceToken::XMinutes, round($minutes)],
|
||||
$minutes < 90.0 => [FormatDistanceToken::AboutXHours, 1],
|
||||
$minutes < $aDay => [FormatDistanceToken::AboutXHours, round($minutes / 60)],
|
||||
$minutes < $almost2Days => [FormatDistanceToken::XDays, 1],
|
||||
$minutes < $aMonth => [FormatDistanceToken::XDays, round($minutes / $aDay)],
|
||||
$minutes < $twoMonths => [FormatDistanceToken::AboutXMonths, round($minutes / $aMonth)],
|
||||
$months < 12 => [FormatDistanceToken::XMonths, round($minutes / $aMonth)],
|
||||
$months % 12 < 3 => [FormatDistanceToken::AboutXYears, $years],
|
||||
$months % 12 < 9 => [FormatDistanceToken::OverXYears, $years],
|
||||
default => [FormatDistanceToken::AlmostXYears, $years]
|
||||
};
|
||||
$format = match ($typeAndNumber[0]) {
|
||||
FormatDistanceToken::LessThanXMinutes => ['less than a minute', 'less than %d minutes'],
|
||||
FormatDistanceToken::XMinutes => ['a minute', '%d minutes'],
|
||||
FormatDistanceToken::AboutXHours => ['about an hour', 'about %d hours'],
|
||||
FormatDistanceToken::XHours => ['an hour', '%d hours'],
|
||||
FormatDistanceToken::XDays => ['a day', '%d days'],
|
||||
FormatDistanceToken::AboutXWeeks => ['about a week', 'about %d weeks'],
|
||||
FormatDistanceToken::XWeeks => ['a week', '%d weeks'],
|
||||
FormatDistanceToken::AboutXMonths => ['about a month', 'about %d months'],
|
||||
FormatDistanceToken::XMonths => ['a month', '%d months'],
|
||||
FormatDistanceToken::AboutXYears => ['about a year', 'about %d years'],
|
||||
FormatDistanceToken::XYears => ['a year', '%d years'],
|
||||
FormatDistanceToken::OverXYears => ['over a year', 'over %d years'],
|
||||
FormatDistanceToken::AlmostXYears => ['almost a year', 'almost %d years']
|
||||
};
|
||||
$value = $typeAndNumber[1] == 1 ? $format[0] : sprintf($format[1], $typeAndNumber[1]);
|
||||
return $dtFrom > $dtTo ? "$value ago" : "in $value";
|
||||
}
|
||||
|
||||
public static function requestList(DocumentList $reqs): void
|
||||
@ -171,3 +208,20 @@ class UI
|
||||
</div><?php
|
||||
}
|
||||
}
|
||||
|
||||
enum FormatDistanceToken
|
||||
{
|
||||
case LessThanXMinutes;
|
||||
case XMinutes;
|
||||
case AboutXHours;
|
||||
case XHours;
|
||||
case XDays;
|
||||
case AboutXWeeks;
|
||||
case XWeeks;
|
||||
case AboutXMonths;
|
||||
case XMonths;
|
||||
case AboutXYears;
|
||||
case XYears;
|
||||
case OverXYears;
|
||||
case AlmostXYears;
|
||||
}
|
||||
|
@ -30,8 +30,8 @@ Layout::pageHead('Full Request');?>
|
||||
<div class=card-body>
|
||||
<h6 class="card-subtitle text-muted mb-2"><?php
|
||||
if (!is_null($answered)) { ?>
|
||||
Answered <?=$answered->format('F j, Y')?> (<?php UI::relativeDate($req->history[0]->asOf); ?>)
|
||||
•<?php
|
||||
Answered <?=$answered->format('F j, Y')?>
|
||||
(<?=UI::formatDistance('now', $req->history[0]->asOf);?>) •<?php
|
||||
} ?>
|
||||
Prayed <?=number_format($prayed)?> times • Open <?=number_format($daysOpen)?> days
|
||||
</h6>
|
||||
|
Loading…
Reference in New Issue
Block a user