Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
"assoconnect/php-quality-config": "^1.1"
},
"require": {
"php": "^7.4|^8.0"
"php": "^7.4|^8.0",
"ext-intl": "*",
"thecodingmachine/safe": "^1.3|^2.0"
},
"config": {
"allow-plugins": {
Expand Down
12 changes: 6 additions & 6 deletions src/AbsoluteDate.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class AbsoluteDate

/**
* AbsoluteDate constructor from a date as string
* Use createInTimezone method if you have a DateTime object or your format includes the hour part
* Use createInTimezone method if you have a DateTime instance or your format includes the hour part
*
* @param string $date Date as string
* @param string $format Format to parse the provided date
Expand Down Expand Up @@ -97,7 +97,7 @@ private function getDateTimeFromFormatAndTimezone(string $format, \DateTimeZone
}

/**
* Checks whether the value represented by this object equals to the other.
* Checks whether the date represented by this instance equals to the other.
*/
public function equalsTo(self $other): bool
{
Expand Down Expand Up @@ -138,15 +138,15 @@ public function isBeforeOrEqualTo(self $other): bool
}

/**
* Returns whether this instant is after another.
* Returns whether this date is after another.
*/
public function isAfter(self $other): bool
{
return $this->__toString() > $other->__toString();
}

/**
* Returns whether this instant is after or equal to another.
* Returns whether this date is after or equal to another.
*/
public function isAfterOrEqualTo(self $other): bool
{
Expand All @@ -172,7 +172,7 @@ public function __toString(): string
}

/**
* Returns an AbsoluteDate object
* Returns an AbsoluteDate instance
*
* @param \DateTimeZone $timezone Timezone to use to get the right date
* @param \DateTimeInterface|null $datetime Point in time to find the date from
Expand All @@ -187,7 +187,7 @@ public static function createInTimezone(\DateTimeZone $timezone, \DateTimeInterf
}

/**
* Returns an AbsoluteDate object from a relative format in a given timezone
* Returns an AbsoluteDate instance from a relative format in a given timezone
*
* @param string $relative Relative format to use
* @param ?\DateTimeZone $timezone Timezone to use to get the right date
Expand Down
9 changes: 9 additions & 0 deletions src/Exception/UnknownPatternException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

namespace AssoConnect\PHPDate\Exception;

class UnknownPatternException extends \UnexpectedValueException
{
}
62 changes: 62 additions & 0 deletions src/LocalizedStringParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

declare(strict_types=1);

namespace AssoConnect\PHPDate;

use AssoConnect\PHPDate\Exception\UnknownPatternException;
use IntlDateFormatter;

class LocalizedStringParser
{
/**
* Returns an AbsoluteDate instance from a formatted string like 9/1/23 for September, 1st 2023 in the USA
* @param string $date Formatted date
* @param string $locale Locale used to format the date (both en-US & en_US patterns are supported)
*/
public function create(string $date, string $locale): AbsoluteDate
{
// ⚠️ IntlDateFormatter & DateTimeInterface don't use the same patterns
$parts = \Safe\array_combine(
explode('/', $this->getPatternFromLocale($locale)), // IntlDateFormatter pattern
explode('/', $date)
);
$orderedDateParts = ['', '', ''];
$patternParts = ['', '', '']; // DateTimeInterface pattern
foreach ($parts as $pattern => $value) {
switch ($pattern) {
case 'd': // Day without leading 0
case 'dd': // Day with leading 0
$orderedDateParts[2] = str_pad($value, 2, '0', STR_PAD_LEFT);
$patternParts[2] = 'd';
break;
case 'M': // Month without leading 0
case 'MM': // Month with leading 0
$orderedDateParts[1] = str_pad($value, 2, '0', STR_PAD_LEFT);
$patternParts[1] = 'm';
break;
case 'y': // Year on 4 digits
case 'yy': // Year on 2 digits
case 'yyyy': // Year on 4 digits
$orderedDateParts[0] = $value;
$patternParts[0] = (2 === strlen($value)) ? 'y' : 'Y';
break;
default:
throw new UnknownPatternException($pattern);
}
}
return new AbsoluteDate(
implode('-', $orderedDateParts),
implode('-', $patternParts)
);
}

public function getPatternFromLocale(string $locale): string
{
return (new IntlDateFormatter(
$locale,
IntlDateFormatter::SHORT,
IntlDateFormatter::NONE,
))->getPattern();
}
}
30 changes: 30 additions & 0 deletions tests/LocalizedStringParserTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace AssoConnect\PHPDate\Tests;

use AssoConnect\PHPDate\LocalizedStringParser;
use PHPUnit\Framework\TestCase;

class LocalizedStringParserTest extends TestCase
{
/** @dataProvider provideStringsAndLocales */
public function testCreateFromLocaleWorks(string $formattedDate, string $locale, string $date): void
{
$parser = new LocalizedStringParser();
self::assertSame($date, $parser->create($formattedDate, $locale)->format());
}

/** @return array{string, string, string}[] */
public function provideStringsAndLocales(): iterable
{
yield ['09/01/2023', 'en_US', '2023-09-01'];
yield ['9/1/2023', 'en_US', '2023-09-01'];
yield ['9/1/23', 'en_US', '2023-09-01'];

yield ['09/01/2023', 'fr_FR', '2023-01-09'];
yield ['9/1/2023', 'fr_FR', '2023-01-09'];
yield ['9/1/23', 'fr_FR', '2023-01-09'];
}
}