Skip to content
Draft
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
2 changes: 2 additions & 0 deletions .typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ is_writeable = "is_writeable"
[default.extend-words]
# override false-positives
Excluder = "Excluder"
# English ordinal suffix ("2nd"), from the "S" date format
nd = "nd"
34 changes: 28 additions & 6 deletions src/Type/Php/DateFunctionReturnTypeHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,6 @@ public function getTypeFromFormatType(Type $formatType, bool $useMicrosec): Type
$type = TypeCombinator::union(...$types);
}

if ($type->isNumericString()->no() && $formatType->isNonEmptyString()->yes()) {
$type = TypeCombinator::union($type, new IntersectionType([
new StringType(), new AccessoryNonEmptyStringType(),
]));
}

return $type;
}

Expand Down Expand Up @@ -84,6 +78,20 @@ private function buildReturnTypeFromFormat(string $formatString, bool $useMicros
return $useMicrosec
? new IntersectionType([new StringType(), new AccessoryNonFalsyStringType(), new AccessoryNumericStringType()])
: new ConstantStringType('000');
case 'a':
return $this->buildConstantStringsType(['am', 'pm']);
case 'A':
return $this->buildConstantStringsType(['AM', 'PM']);
case 'S':
return $this->buildConstantStringsType(['st', 'nd', 'rd', 'th']);
case 'D':
return $this->buildConstantStringsType(['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']);
case 'l':
return $this->buildConstantStringsType(['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']);
case 'M':
return $this->buildConstantStringsType(['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']);
case 'F':
return $this->buildConstantStringsType(['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']);
}

$date = date($formatString);
Expand Down Expand Up @@ -117,4 +125,18 @@ private function buildNumericRangeType(int $min, int $max, bool $zeroPad): Type
return new UnionType($types);
}

/**
* @param list<string> $values
*/
private function buildConstantStringsType(array $values): Type
{
$types = [];

foreach ($values as $value) {
$types[] = new ConstantStringType($value);
}

return new UnionType($types);
}

}
29 changes: 29 additions & 0 deletions tests/PHPStan/Analyser/nsrt/bug-15537.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php declare(strict_types = 1);

namespace Bug15537;

use DateTimeImmutable;
use function PHPStan\Testing\assertType;

/**
* @param 'am'|'pm'|'AM'|'PM' $meridiem
*/
function translate_meridiem( string $meridiem ): string {
return $meridiem;
}

function test( DateTimeImmutable $datetime, int $timestamp ): void {
echo translate_meridiem( $datetime->format( 'a' ) );
echo translate_meridiem( date( 'A', $timestamp ) );

assertType( "'am'|'pm'", $datetime->format( 'a' ) );
assertType( "'AM'|'PM'", $datetime->format( 'A' ) );
assertType( "'nd'|'rd'|'st'|'th'", $datetime->format( 'S' ) );
assertType( "'Fri'|'Mon'|'Sat'|'Sun'|'Thu'|'Tue'|'Wed'", $datetime->format( 'D' ) );
assertType( "'Friday'|'Monday'|'Saturday'|'Sunday'|'Thursday'|'Tuesday'|'Wednesday'", $datetime->format( 'l' ) );
assertType( "'Apr'|'Aug'|'Dec'|'Feb'|'Jan'|'Jul'|'Jun'|'Mar'|'May'|'Nov'|'Oct'|'Sep'", $datetime->format( 'M' ) );
assertType( "'April'|'August'|'December'|'February'|'January'|'July'|'June'|'March'|'May'|'November'|'October'|'September'", $datetime->format( 'F' ) );

assertType( "'am'|'pm'", date( 'a', $timestamp ) );
assertType( "'Friday'|'Monday'|'Saturday'|'Sunday'|'Thursday'|'Tuesday'|'Wednesday'", date_format( $datetime, 'l' ) );
}
10 changes: 5 additions & 5 deletions tests/PHPStan/Analyser/nsrt/date-format.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,39 +7,39 @@
function (string $s): void {
assertType('\'\'', date(''));
assertType('string', date($s));
assertType('non-falsy-string', date('D'));
assertType("'Fri'|'Mon'|'Sat'|'Sun'|'Thu'|'Tue'|'Wed'", date('D'));
assertType('numeric-string', date('Y'));
assertType('numeric-string', date('Ghi'));
};

function (\DateTime $dt, string $s): void {
assertType('\'\'', date_format($dt, ''));
assertType('string', date_format($dt, $s));
assertType('non-falsy-string', date_format($dt, 'D'));
assertType("'Fri'|'Mon'|'Sat'|'Sun'|'Thu'|'Tue'|'Wed'", date_format($dt, 'D'));
assertType('numeric-string', date_format($dt, 'Y'));
assertType('numeric-string', date_format($dt, 'Ghi'));
};

function (\DateTimeInterface $dt, string $s): void {
assertType('\'\'', $dt->format(''));
assertType('string', $dt->format($s));
assertType('non-falsy-string', $dt->format('D'));
assertType("'Fri'|'Mon'|'Sat'|'Sun'|'Thu'|'Tue'|'Wed'", $dt->format('D'));
assertType('numeric-string', $dt->format('Y'));
assertType('numeric-string', $dt->format('Ghi'));
};

function (\DateTime $dt, string $s): void {
assertType('\'\'', $dt->format(''));
assertType('string', $dt->format($s));
assertType('non-falsy-string', $dt->format('D'));
assertType("'Fri'|'Mon'|'Sat'|'Sun'|'Thu'|'Tue'|'Wed'", $dt->format('D'));
assertType('numeric-string', $dt->format('Y'));
assertType('numeric-string', $dt->format('Ghi'));
};

function (\DateTimeImmutable $dt, string $s): void {
assertType('\'\'', $dt->format(''));
assertType('string', $dt->format($s));
assertType('non-falsy-string', $dt->format('D'));
assertType("'Fri'|'Mon'|'Sat'|'Sun'|'Thu'|'Tue'|'Wed'", $dt->format('D'));
assertType('numeric-string', $dt->format('Y'));
assertType('numeric-string', $dt->format('Ghi'));
};
Expand Down
Loading