gzhegow/calendar

1.4.0 2025-03-30 07:39 UTC

This package is auto-updated.

Last update: 2025-03-30 07:39:46 UTC


README

Что это

Этот пакет сделан, чтобы сделать работу с датами более удобной.

  • Позволяет передавать в конструкторы дат и временных зон строки без необходимости создавать объект ради объекта
  • В нем можно посмотреть, как сделать грамотное наследование имеющищегося в коробке PHP ужаса, чтобы этим управлять или добавить своё
  • Интервалы и даты имеют замененный метод jsonSerialize(), который возвращает короткую форму записи согласно ISO 8601
  • Интервалы научились понимать дробные значения согласно ISO 8601, например, интервал P0.5Y валидный, но PHP его не понимает

Есть желание помочь?

  • Жаль, но я так и не смог добиться работы генериков, которые должны бы распознаваться как только мы присвоим класс, здесь генерики распространяются на весь пакет, а я пока умею их использовать только для одного класса

Установка

composer require gzhegow/calendar;

Примеры и тесты

<?php

require_once __DIR__ . '/vendor/autoload.php';


// > настраиваем PHP
ini_set('memory_limit', '32M');


// > настраиваем обработку ошибок
(new \Gzhegow\Lib\Exception\ErrorHandler())
    ->useErrorReporting()
    ->useErrorHandler()
    ->useExceptionHandler()
;


// > добавляем несколько функция для тестирования
function _values($separator = null, ...$values) : string
{
    return \Gzhegow\Lib\Lib::debug()->values($separator, [], ...$values);
}

function _print(...$values) : void
{
    echo _values(' | ', ...$values) . PHP_EOL;
}

function _assert_stdout(
    \Closure $fn, array $fnArgs = [],
    string $expectedStdout = null
) : void
{
    $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);

    \Gzhegow\Lib\Lib::test()->assertStdout(
        $trace,
        $fn, $fnArgs,
        $expectedStdout
    );
}


// >>> ЗАПУСКАЕМ!

// > сначала всегда фабрика
$factory = new \Gzhegow\Calendar\CalendarFactory();

// > создаем конфигурацию
$config = new \Gzhegow\Calendar\CalendarConfig();
$config->configure(function (\Gzhegow\Calendar\CalendarConfig $config) {
    // > можно использовать собственный форматтер для некоторых функций работы с датами
    $config->formatter->dateTimeFormatter = new \Gzhegow\Calendar\Formatter\DateTime\DateTimeFormatter();
    // > можно использовать собственный форматтер для некоторых функций работы с интервалами
    $config->formatter->dateIntervalFormatter = new \Gzhegow\Calendar\Formatter\DateInterval\DateIntervalFormatter();

    // > можно указать "дату по-умолчанию", которая будет создана, если не передать аргументов вовсе
    $config->manager->dateTimeDefault = '1970-01-01 midnight';
    // > можно указать "временную зону по-умолчанию", которая будет установлена, если при разборе даты не удалось определить временную зону
    $config->manager->dateTimeZoneDefault = 'Europe/Minsk';
    // > можно указать "интервал по-умолчанию", который будет создаваться, если не передать аргументов
    $config->manager->dateIntervalDefault = 'P0D';

    // > можно указать форматы для разбора даты, если таковые не переданы прямо в функцию
    $config->parser->parseDateTimeFormatsDefault = [
        \Gzhegow\Calendar\Calendar::FORMAT_SQL,
        \Gzhegow\Calendar\Calendar::FORMAT_JAVASCRIPT,
    ];
    // > можно указать форматы для разбора интервалов, если таковые не переданы прямо в функцию
    $config->parser->parseDateIntervalFormatsDefault = [
        \Gzhegow\Calendar\Calendar::FORMAT_SQL_TIME,
    ];
});


// > можно изменить классы дат на свои собственные реализации
$type = new \Gzhegow\Calendar\CalendarType();
// if (PHP_VERSION_ID >= 80000) {
//     $type->classDateTime(\Gzhegow\Calendar\Struct\DateTime::class);
//     $type->classDateTimeImmutable(\Gzhegow\Calendar\Struct\DateTimeImmutable::class);
//     $type->classDateInterval(\Gzhegow\Calendar\Struct\DateInterval::class);
//     $type->classDateTimeZone(\Gzhegow\Calendar\Struct\DateTimeZone::class);
//
// } else {
//     $type->classDateTime(\Gzhegow\Calendar\Struct\PHP7\DateTime::class);
//     $type->classDateTimeImmutable(\Gzhegow\Calendar\Struct\PHP7\DateTimeImmutable::class);
//     $type->classDateInterval(\Gzhegow\Calendar\Struct\PHP7\DateInterval::class);
//     $type->classDateTimeZone(\Gzhegow\Calendar\Struct\PHP7\DateTimeZone::class);
// }

// > создаем парсер
$parser = new \Gzhegow\Calendar\Parser\CalendarParser(
    $factory,
    //
    $config->parser
);

// > создаем менеджер
$manager = new \Gzhegow\Calendar\Manager\CalendarManager(
    $factory,
    //
    $parser,
    //
    $config->manager
);

// > создаем форматтер
$formatter = new \Gzhegow\Calendar\Formatter\CalendarFormatter(
    $factory,
    //
    $config->formatter
);

// > создаем фасад
$calendar = new \Gzhegow\Calendar\CalendarFacade(
    $factory,
    $type,
    //
    $parser,
    $manager,
    $formatter,
    //
    $config
);
// if (PHP_VERSION_ID >= 80000) {
//     $calendar->classDateTime(\Gzhegow\Calendar\Struct\DateTime::class);
//     $calendar->classDateTimeImmutable(\Gzhegow\Calendar\Struct\DateTimeImmutable::class);
//     $calendar->classDateInterval(\Gzhegow\Calendar\Struct\DateInterval::class);
//     $calendar->classDateTimeZone(\Gzhegow\Calendar\Struct\DateTimeZone::class);
//
// } else {
//     $calendar->classDateTime(\Gzhegow\Calendar\Struct\PHP7\DateTime::class);
//     $calendar->classDateTimeImmutable(\Gzhegow\Calendar\Struct\PHP7\DateTimeImmutable::class);
//     $calendar->classDateInterval(\Gzhegow\Calendar\Struct\PHP7\DateInterval::class);
//     $calendar->classDateTimeZone(\Gzhegow\Calendar\Struct\PHP7\DateTimeZone::class);
// }

// > сохраняем фасад статически (чтобы вызывать без привязки к контейнеру)
\Gzhegow\Calendar\Calendar::setFacade($calendar);
// if (PHP_VERSION_ID >= 80000) {
//     \Gzhegow\Calendar\Calendar::classDateTime(\Gzhegow\Calendar\Struct\DateTime::class);
//     \Gzhegow\Calendar\Calendar::classDateTimeImmutable(\Gzhegow\Calendar\Struct\DateTimeImmutable::class);
//     \Gzhegow\Calendar\Calendar::classDateInterval(\Gzhegow\Calendar\Struct\DateInterval::class);
//     \Gzhegow\Calendar\Calendar::classDateTimeZone(\Gzhegow\Calendar\Struct\DateTimeZone::class);
//
// } else {
//     \Gzhegow\Calendar\Calendar::classDateTime(\Gzhegow\Calendar\Struct\PHP7\DateTime::class);
//     \Gzhegow\Calendar\Calendar::classDateTimeImmutable(\Gzhegow\Calendar\Struct\PHP7\DateTimeImmutable::class);
//     \Gzhegow\Calendar\Calendar::classDateInterval(\Gzhegow\Calendar\Struct\PHP7\DateInterval::class);
//     \Gzhegow\Calendar\Calendar::classDateTimeZone(\Gzhegow\Calendar\Struct\PHP7\DateTimeZone::class);
// }


// > TEST
// > создаем дату, временную зону и интервал
$fn = function () use ($calendar) {
    _print('TEST 1');
    echo PHP_EOL;

    $result = $calendar->dateTime($from = '', $dateTimeZone = null);
    _print($result, json_encode($result));

    $result = $calendar->dateTime($from = '1970-01-01 00:00:00', $dateTimeZone = null);
    _print($result, json_encode($result));
    echo PHP_EOL;


    $result = $calendar->dateTimeImmutable($from = '', $dateTimeZone = null);
    _print($result, json_encode($result));

    $result = $calendar->dateTimeImmutable($from = '1970-01-01 00:00:00', $dateTimeZone = null);
    _print($result, json_encode($result));
    echo PHP_EOL;


    $result = $calendar->dateTimeZone($from = '');
    _print($result, json_encode($result));

    $result = $calendar->dateTimeZone($from = 'UTC');
    _print($result, json_encode($result));
    echo PHP_EOL;


    $result = $calendar->dateInterval($from = '');
    _print($result, json_encode($result));

    $result = $calendar->dateInterval($from = 'P1D');
    _print($result, json_encode($result));

    $result = $calendar->dateInterval($from = 'P1.5D');
    _print($result, json_encode($result));
};
_assert_stdout($fn, [], PHP_VERSION_ID >= 80000
    ? '
"TEST 1"

{ object # Gzhegow\Calendar\Struct\DateTime # "1970-01-01T00:00:00.000+03:00" } | "\"1970-01-01T00:00:00.000+03:00\""
{ object # Gzhegow\Calendar\Struct\DateTime # "1970-01-01T00:00:00.000+03:00" } | "\"1970-01-01T00:00:00.000+03:00\""

{ object # Gzhegow\Calendar\Struct\DateTimeImmutable # "1970-01-01T00:00:00.000+03:00" } | "\"1970-01-01T00:00:00.000+03:00\""
{ object # Gzhegow\Calendar\Struct\DateTimeImmutable # "1970-01-01T00:00:00.000+03:00" } | "\"1970-01-01T00:00:00.000+03:00\""

{ object # Gzhegow\Calendar\Struct\DateTimeZone # "Europe/Minsk" } | "\"Europe\/Minsk\""
{ object # Gzhegow\Calendar\Struct\DateTimeZone # "UTC" } | "\"UTC\""

{ object # Gzhegow\Calendar\Struct\DateInterval # "P0D" } | "\"P0D\""
{ object # Gzhegow\Calendar\Struct\DateInterval # "P1D" } | "\"P1D\""
{ object # Gzhegow\Calendar\Struct\DateInterval # "P1DT12H" } | "\"P1DT12H\""
'
    : '
"TEST 1"

{ object # Gzhegow\Calendar\Struct\PHP7\DateTime # "1970-01-01T00:00:00.000+03:00" } | "\"1970-01-01T00:00:00.000+03:00\""
{ object # Gzhegow\Calendar\Struct\PHP7\DateTime # "1970-01-01T00:00:00.000+03:00" } | "\"1970-01-01T00:00:00.000+03:00\""

{ object # Gzhegow\Calendar\Struct\PHP7\DateTimeImmutable # "1970-01-01T00:00:00.000+03:00" } | "\"1970-01-01T00:00:00.000+03:00\""
{ object # Gzhegow\Calendar\Struct\PHP7\DateTimeImmutable # "1970-01-01T00:00:00.000+03:00" } | "\"1970-01-01T00:00:00.000+03:00\""

{ object # Gzhegow\Calendar\Struct\PHP7\DateTimeZone # "Europe/Minsk" } | "\"Europe\/Minsk\""
{ object # Gzhegow\Calendar\Struct\PHP7\DateTimeZone # "UTC" } | "\"UTC\""

{ object # Gzhegow\Calendar\Struct\PHP7\DateInterval # "P0D" } | "\"P0D\""
{ object # Gzhegow\Calendar\Struct\PHP7\DateInterval # "P1D" } | "\"P1D\""
{ object # Gzhegow\Calendar\Struct\PHP7\DateInterval # "P1DT12H" } | "\"P1DT12H\""
');


// > TEST
// > распознаем дату, временную зону и интервал
$fn = function () use ($calendar) {
    _print('TEST 2');
    echo PHP_EOL;

    $result = $calendar->parseDateTime($from = '1970-01-01 00:00:00', $formats = [ 'Y-m-d H:i:s' ], $dateTimeZoneIfParsed = 'UTC');
    _print($result, json_encode($result));

    $result = $calendar->parseDateTimeFromNumeric($from = 1.5, $dateTimeZoneIfParsed = 'UTC');
    _print($result, json_encode($result));
    echo PHP_EOL;


    $result = $calendar->parseDateTimeImmutable($from = '1970-01-01 00:00:00', $formats = [ 'Y-m-d H:i:s' ], $dateTimeZoneIfParsed = 'UTC');
    _print($result, json_encode($result));

    $result = $calendar->parseDateTimeImmutableFromNumeric($from = 1.5, $dateTimeZoneIfParsed = 'UTC');
    _print($result, json_encode($result));
    echo PHP_EOL;


    $result = $calendar->parseDateTimeZone($from = 'UTC');
    _print($result, json_encode($result));
    echo PHP_EOL;


    $result = $calendar->parseDateInterval($from = 'P0D', $formats = null);
    _print($result, json_encode($result));

    $result = $calendar->parseDateIntervalFromNumeric($from = 1.5);
    _print($result, json_encode($result));
};
_assert_stdout($fn, [], PHP_VERSION_ID >= 80000
    ? '
"TEST 2"

{ object # Gzhegow\Calendar\Struct\DateTime # "1970-01-01T00:00:00.000+00:00" } | "\"1970-01-01T00:00:00.000+00:00\""
{ object # Gzhegow\Calendar\Struct\DateTime # "1970-01-01T00:00:01.500+00:00" } | "\"1970-01-01T00:00:01.500+00:00\""

{ object # Gzhegow\Calendar\Struct\DateTimeImmutable # "1970-01-01T00:00:00.000+00:00" } | "\"1970-01-01T00:00:00.000+00:00\""
{ object # Gzhegow\Calendar\Struct\DateTimeImmutable # "1970-01-01T00:00:01.500+00:00" } | "\"1970-01-01T00:00:01.500+00:00\""

{ object # Gzhegow\Calendar\Struct\DateTimeZone # "UTC" } | "\"UTC\""

{ object # Gzhegow\Calendar\Struct\DateInterval # "P0D" } | "\"P0D\""
{ object # Gzhegow\Calendar\Struct\DateInterval # "PT1.5S" } | "\"PT1.5S\""
'
    : '
"TEST 2"

{ object # Gzhegow\Calendar\Struct\PHP7\DateTime # "1970-01-01T00:00:00.000+00:00" } | "\"1970-01-01T00:00:00.000+00:00\""
{ object # Gzhegow\Calendar\Struct\PHP7\DateTime # "1970-01-01T00:00:01.500+00:00" } | "\"1970-01-01T00:00:01.500+00:00\""

{ object # Gzhegow\Calendar\Struct\PHP7\DateTimeImmutable # "1970-01-01T00:00:00.000+00:00" } | "\"1970-01-01T00:00:00.000+00:00\""
{ object # Gzhegow\Calendar\Struct\PHP7\DateTimeImmutable # "1970-01-01T00:00:01.500+00:00" } | "\"1970-01-01T00:00:01.500+00:00\""

{ object # Gzhegow\Calendar\Struct\PHP7\DateTimeZone # "UTC" } | "\"UTC\""

{ object # Gzhegow\Calendar\Struct\PHP7\DateInterval # "P0D" } | "\"P0D\""
{ object # Gzhegow\Calendar\Struct\PHP7\DateInterval # "PT1.5S" } | "\"PT1.5S\""
');


// > TEST
// > проводим действия над датой
$fn = function () use ($calendar) {
    _print('TEST 3');
    echo PHP_EOL;

    $dateTimeImmutable11 = $calendar->parseDateTimeImmutable($datetime = '1970-01-01 midnight', $formats = null, $timezoneIfParsed = null);
    $dateTimeImmutable12 = $dateTimeImmutable11->modify('+ 10 hours');
    $dateTimeImmutableDiff13 = $dateTimeImmutable11->diff($dateTimeImmutable12);
    _print($dateTimeImmutable11, json_encode($dateTimeImmutable11));
    _print($dateTimeImmutable12, json_encode($dateTimeImmutable12));
    _print($dateTimeImmutableDiff13, $calendar->formatIntervalAgo($dateTimeImmutableDiff13));

    $dateTimeImmutable21 = $calendar->parseDateTimeImmutable($datetime = '1970-01-01 midnight', $formats = null, $timezoneIfParsed = null);
    $dateTimeImmutable22 = $dateTimeImmutable21->add(new \DateInterval('PT10H'));
    $dateTimeImmutableDiff23 = $dateTimeImmutable21->diff($dateTimeImmutable22);
    _print($dateTimeImmutable21, json_encode($dateTimeImmutable21));
    _print($dateTimeImmutable22, json_encode($dateTimeImmutable22));
    _print($dateTimeImmutableDiff23, $calendar->formatIntervalAgo($dateTimeImmutableDiff23));

    $dateTimeImmutable31 = $calendar->parseDateTimeImmutable($datetime = '1970-01-01 midnight', $formats = null, $timezoneIfParsed = null);
    $dateTimeImmutable32 = $dateTimeImmutable31->sub(new \DateInterval('PT10H'));
    $dateTimeImmutableDiff33 = $dateTimeImmutable31->diff($dateTimeImmutable32);
    _print($dateTimeImmutable31, json_encode($dateTimeImmutable31));
    _print($dateTimeImmutable32, json_encode($dateTimeImmutable32));
    _print($dateTimeImmutableDiff33, $calendar->formatIntervalAgo($dateTimeImmutableDiff33));
};
_assert_stdout($fn, [], PHP_VERSION_ID >= 80000
    ? '
"TEST 3"

{ object # Gzhegow\Calendar\Struct\DateTimeImmutable # "1970-01-01T00:00:00.000+03:00" } | "\"1970-01-01T00:00:00.000+03:00\""
{ object # Gzhegow\Calendar\Struct\DateTimeImmutable # "1970-01-01T10:00:00.000+03:00" } | "\"1970-01-01T10:00:00.000+03:00\""
{ object # Gzhegow\Calendar\Struct\DateInterval # "PT10H" } | "через 10 час."
{ object # Gzhegow\Calendar\Struct\DateTimeImmutable # "1970-01-01T00:00:00.000+03:00" } | "\"1970-01-01T00:00:00.000+03:00\""
{ object # Gzhegow\Calendar\Struct\DateTimeImmutable # "1970-01-01T10:00:00.000+03:00" } | "\"1970-01-01T10:00:00.000+03:00\""
{ object # Gzhegow\Calendar\Struct\DateInterval # "PT10H" } | "через 10 час."
{ object # Gzhegow\Calendar\Struct\DateTimeImmutable # "1970-01-01T00:00:00.000+03:00" } | "\"1970-01-01T00:00:00.000+03:00\""
{ object # Gzhegow\Calendar\Struct\DateTimeImmutable # "1969-12-31T14:00:00.000+03:00" } | "\"1969-12-31T14:00:00.000+03:00\""
{ object # Gzhegow\Calendar\Struct\DateInterval # "PT10H" } | "10 час. назад"
'
    : '
"TEST 3"

{ object # Gzhegow\Calendar\Struct\PHP7\DateTimeImmutable # "1970-01-01T00:00:00.000+03:00" } | "\"1970-01-01T00:00:00.000+03:00\""
{ object # Gzhegow\Calendar\Struct\PHP7\DateTimeImmutable # "1970-01-01T10:00:00.000+03:00" } | "\"1970-01-01T10:00:00.000+03:00\""
{ object # Gzhegow\Calendar\Struct\PHP7\DateInterval # "PT10H" } | "через 10 час."
{ object # Gzhegow\Calendar\Struct\PHP7\DateTimeImmutable # "1970-01-01T00:00:00.000+03:00" } | "\"1970-01-01T00:00:00.000+03:00\""
{ object # Gzhegow\Calendar\Struct\PHP7\DateTimeImmutable # "1970-01-01T10:00:00.000+03:00" } | "\"1970-01-01T10:00:00.000+03:00\""
{ object # Gzhegow\Calendar\Struct\PHP7\DateInterval # "PT10H" } | "через 10 час."
{ object # Gzhegow\Calendar\Struct\PHP7\DateTimeImmutable # "1970-01-01T00:00:00.000+03:00" } | "\"1970-01-01T00:00:00.000+03:00\""
{ object # Gzhegow\Calendar\Struct\PHP7\DateTimeImmutable # "1969-12-31T14:00:00.000+03:00" } | "\"1969-12-31T14:00:00.000+03:00\""
{ object # Gzhegow\Calendar\Struct\PHP7\DateInterval # "PT10H" } | "10 час. назад"
');


// > TEST
// > проводим действия над датой
$fn = function () use ($calendar) {
    _print('TEST 4');
    echo PHP_EOL;

    $dateTime = $calendar->dateTime();
    $formatted = $calendar->formatDateHuman($dateTime);
    _print($dateTime, json_encode($dateTime), $formatted);

    $dateTime = $calendar->dateTime();
    $formatted = $calendar->formatDateHumanDay($dateTime);
    _print($dateTime, json_encode($dateTime), $formatted);
};
_assert_stdout($fn, [], PHP_VERSION_ID >= 80000
    ? '
"TEST 4"

{ object # Gzhegow\Calendar\Struct\DateTime # "1970-01-01T00:00:00.000+03:00" } | "\"1970-01-01T00:00:00.000+03:00\"" | "Thu, 01 Jan 1970 00:00:00 +0300"
{ object # Gzhegow\Calendar\Struct\DateTime # "1970-01-01T00:00:00.000+03:00" } | "\"1970-01-01T00:00:00.000+03:00\"" | "Thu, 01 Jan 1970 +0300"
'
    : '
"TEST 4"

{ object # Gzhegow\Calendar\Struct\PHP7\DateTime # "1970-01-01T00:00:00.000+03:00" } | "\"1970-01-01T00:00:00.000+03:00\"" | "Thu, 01 Jan 1970 00:00:00 +0300"
{ object # Gzhegow\Calendar\Struct\PHP7\DateTime # "1970-01-01T00:00:00.000+03:00" } | "\"1970-01-01T00:00:00.000+03:00\"" | "Thu, 01 Jan 1970 +0300"
');