A calendar written with IntlCalendar class

Code
<?php

// Berlin - Germany
$c IntlCalendar::createInstance('Europe/Berlin''de_DE');
$renderer = new CalendarMonthRenderer(new IntlDateFormatter('de_DE'nullnull));
echo 
$renderer->render(new CalendarMonth($c));

// USA - New York
$c IntlCalendar::createInstance('America/New_York''en_US');
$renderer = new CalendarMonthRenderer(new IntlDateFormatter('en_US'nullnull));
echo 
$renderer->render(new CalendarMonth($c));


/**
 * This code snippet is no copy and paste example. It has no error
 * handling and isn't generic or clean written enough to be used in 
 * production.
 */
class CalendarMonth
{
    private 
$calendar;
    private 
$now;
    
    const 
DAY IntlCalendar::FIELD_DATE;
    const 
MONTH IntlCalendar::FIELD_MONTH;
    const 
DOW_LOCAL IntlCalendar::FIELD_DOW_LOCAL;
    
    public function 
__construct(IntlCalendar $calendar)
    {
        
$this->calendar $calendar;
        
$this->now $calendar->getTime();
    }
    
    public function 
getNow()
    {
        return 
$this->now;
    }
    
    public function 
createStructure()
    {
        
$calendar $this->calendar;
        
        
// beginning of the week
        
$calendar->set(self::DAY$calendar->getMinimum(self::DAY));
        
$dayOfWeek $calendar->get(self::DOW_LOCAL);
        
$month $calendar->get(self::MONTH);
        
$calendar->add(self::DAY, -($dayOfWeek 1));

        
$structure = [];
        
$week = [];
        while (
$calendar->get(self::MONTH) <= $month || 
            !
$this->isNewWeek($calendar)
        ) {
            if (
$this->isNewWeek($calendar)) {
                
$week = [];
            }
            
            
$week[] = $calendar->getTime();
            
            if (
$this->isEndOfWeek($calendar)) {
                
$structure[] = $week;
                if (
$calendar->get(self::MONTH) != $month) {
                    break;
                }
            }
            
            
$calendar->add(self::DAY1);
        }
        
        return 
$structure;
    }
    
    private function 
isNewWeek(IntlCalendar $c)
    {
        return 
=== $c->get(self::DOW_LOCAL);
    }
    
    private function 
isEndOfWeek(IntlCalendar $c)
    {
        return 
=== $c->get(self::DOW_LOCAL);
    }
}

class 
CalendarMonthRenderer
{
    private 
$formatter;
    
    public function 
__construct(IntlDateFormatter $formatter)
    {
        
$this->formatter $formatter;
    }
    
    public function 
render(CalendarMonth $calendar)
    {
        
$days $weekdays '';
        foreach (
$calendar->createStructure() as $pos => $week) {
            
$days .= '<tr>';
            foreach(
$week as $day) {
                if (
=== $pos) {
                    
$weekdays .= '<td>' $this->format($day 1000'E') . '</td>';
                }
            
                
$days .= '<td align="right">' $this->format($day 1000'd') . '</td>';
            }
            
$days .= '</tr>';
        }
        
        
$caption '<caption>' $this->format($calendar->getNow() / 1000'LLLL') . '</caption>';
        
        return 
'<table border="1">' $caption '<tr>' $weekdays '</tr>' $days '</table>';
    }
    
    private function 
format($c$format)
    {
        
$this->formatter->setPattern($format);
        return 
$this->formatter->format($c);
    }
}
Result
Mai
Mo.Di.Mi.Do.Fr.Sa.So.
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345
May
SunMonTueWedThuFriSat
1234567
891011121314
15161718192021
22232425262728
2930311234
Used Versions
PHP 8.0, Laminas MVC 3.2, Symfony 5.2, Laravel 8.28, PHPUnit 9.5, Doctrine ORM 2.8