Dates and logging

A page's PHP asks for the date all the time: to stamp a record, to say how old a comment is, to write down what happened. These helpers give "now" in the application's timezone, read a date whatever form it arrives in, put an age into words and keep a log of the application's own - plain functions, loaded on every request.

function what it answers
padNow ( $format = NULL ) now, as a DateTimeImmutable in $padTimezone (else PHP's zone) - or, with a format, the formatted text
padToday ( $format = NULL ) today at midnight
padNowFreeze ( $time = NULL ) fixes now for the rest of the request; NULL lets the clock run again
padDateParse ( $value ) a DateTimeImmutable from a timestamp, a date in text or a date object - NULL for what is none of those
padAgo ( $date, $now = NULL ) the age in words: just now, 5 minutes ago, yesterday, in 3 days
padLog ( $message, $level = 'info', $context = [] ) appends one line to the application's log of the day

Now and today

The example freezes the clock first, so it shows the same on every day it is read - what a test does too. Without the freeze, now is the real moment.

PHP
fragments/dates_and_logging_1.php
HTML
fragments/dates_and_logging_1.pad
Result
<?php

padNowFreeze
( '2026-10-05 14:30:00' );

$now = padNow ( 'l j F Y, H:i' );
$today = padToday ( 'Y-m-d H:i:s' );
$christmas = padDateParse ( '2026-12-25' )->format ( 'l j F' );
$nextWeek = padDateParse ( '+1 week' )->format ( 'Y-m-d' );
$nothing = ( padDateParse ( '2026-02-30' ) === NULL )
?
'NULL' : 'a date';

?>
<p>Now: {$now}</p>
<p>Today: {$today}</p>
<p>Christmas: {$christmas}</p>
<p>A week from now: {$nextWeek}</p>
<p>30 February: {$nothing}</p>

Now: Monday 5 October 2026, 14:30

Today: 2026-10-05 00:00:00

Christmas: Friday 25 December

A week from now: 2026-10-12

30 February: NULL

Freezing the clock

padNowFreeze ( '2026-10-05 14:30:00' ) fixes now for the rest of the request: padNow, padToday, padAgo, the time stamp of padLog, and text like tomorrow or +1 week given to padDateParse all count from that moment. A test gets the same answer on every run, and a page can be looked at as it will be on another day. padNowFreeze () lets the clock run again.

How long ago

padAgo says the age of a moment in words, and the ago pipe does the same in a template: {$posted | ago}.

PHP
fragments/dates_and_logging_2.php
HTML
fragments/dates_and_logging_2.pad
Result
<?php

padNowFreeze
( '2026-10-05 14:30:00' );

$comments = [
[
'author' => 'Ann', 'posted' => '2026-10-05 14:29:55' ],
[
'author' => 'Bob', 'posted' => '2026-10-05 14:12:00' ],
[
'author' => 'Cleo', 'posted' => '2026-10-04 09:00:00' ],
[
'author' => 'Dirk', 'posted' => '2026-09-20 18:00:00' ],
[
'author' => 'Eve', 'posted' => '2025-06-01' ]
];

$deadline = padAgo ( '2026-10-08 12:00' );

?>
<ul>
{comments}
<li>{$author} - {$posted | ago}</li>
{/comments}
</ul>
<p>The deadline is {$deadline}.</p>
  • Ann - just now
  • Bob - 18 minutes ago
  • Cleo - yesterday
  • Dirk - 2 weeks ago
  • Eve - 1 year ago

The deadline is in 3 days.

Logging

padLog appends one line to DATA/logs/<application>/<Y-m-d>.log: the time, the level, the message and the context as JSON. The levels are those of PSR-3 - debug, info, notice, warning, error, critical, alert and emergency - and {name} in the message is filled from the context.

PHP
fragments/dates_and_logging_3.php
HTML
fragments/dates_and_logging_3.pad
Result
<?php

padNowFreeze
( '2026-10-05 14:30:00' );

padLog ( 'Order {order} paid by {customer}', 'info',
[
'order' => 1042, 'customer' => 'Ann' ] );

$log = DATA . "logs/$padApp/2026-10-05.log";
$lines = file ( $log, FILE_IGNORE_NEW_LINES );
$line = end ( $lines );

?>
{$line}
2026-10-05 14:30:00 INFO Order 1042 paid by Ann {"order":1042,"customer":"Ann"}