Skip to content

Commit d0eb0fa

Browse files
authored
Merge pull request #16965 from craftcms/bugfix/16953-intl-timezones-and-old-dates
allow for dates to be formatted without IntlDateFormatter
2 parents c371715 + 89289a3 commit d0eb0fa

File tree

2 files changed

+56
-0
lines changed

2 files changed

+56
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- Fixed a bug where relations weren’t getting saved for new elements, if the element was created with `Craft::createObject()` with its relation field data included in the passed-in config. ([#16942](https://github.com/craftcms/cms/pull/16942))
88
- Fixed a bug where relational fields with a “Related To” rule on their selectable elements condition weren’t making all expected elements selectable. ([#16945](https://github.com/craftcms/cms/issues/16945))
99
- Fixed a bug where some subdivisions weren’t available when creating addresses. ([#16951](https://github.com/craftcms/cms/issues/16951))
10+
- Fixed a bug where some older dates could be formatted incorrectly. ([#16953](https://github.com/craftcms/cms/issues/16953))
1011

1112
## 4.14.11.1 - 2025-03-19
1213

src/i18n/Formatter.php

+55
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@
1111
use craft\helpers\DateTimeHelper;
1212
use DateTime;
1313
use DateTimeZone;
14+
use Exception;
15+
use IntlTimeZone;
1416
use NumberFormatter;
17+
use Yii;
1518
use yii\base\InvalidArgumentException;
1619
use yii\base\InvalidConfigException;
20+
use yii\helpers\FormatConverter;
1721

1822
/**
1923
* @inheritdoc
@@ -80,13 +84,64 @@ public function asDate($value, $format = null): string
8084
$format = $this->dateTimeFormats[$format]['date'];
8185
}
8286

87+
// https://github.com/craftcms/cms/issues/16953
88+
$dateFormattedWithoutIntl = $this->formatDateWithoutIntl($value, $format);
89+
if ($dateFormattedWithoutIntl !== null) {
90+
return $dateFormattedWithoutIntl;
91+
}
92+
8393
if (strncmp($format, 'php:', 4) === 0) {
8494
return $this->_formatDateTimeValueWithPhpFormat($value, substr($format, 4), 'date');
8595
}
8696

8797
return parent::asDate($value, $format);
8898
}
8999

100+
/**
101+
* Checks if the date should be formatted without the help of intl.
102+
* This is needed for some "old" dates, like dates from 19th century from Europe/Amsterdam timezone
103+
*
104+
* @param int|string|DateTime $value
105+
* @param string|null $format
106+
* @return string|null
107+
* @throws Exception
108+
*/
109+
private function formatDateWithoutIntl(int|string|DateTime $value, ?string $format): ?string
110+
{
111+
// see https://github.com/craftcms/cms/issues/16953 for more details on why this method was added
112+
113+
if (!$value instanceof DateTime || !extension_loaded('intl')) {
114+
return null;
115+
}
116+
117+
$intlTimeZone = IntlTimeZone::fromDateTimeZone($value->getTimezone());
118+
$intlTimeZone->getOffset($value->getTimestamp(), true, $o1, $o2);
119+
$intlTimeZone->getOffset($value->getTimestamp(), false, $o3, $o4);
120+
121+
// get PHP DateTime offset for this date
122+
$phpOffset = $value->getTimezone()->getOffset($value);
123+
// get DST offset that intl time zone would use (divided by 1000, b/c different units)
124+
$dstIntlOffset = ($intlTimeZone->getRawOffset() + $intlTimeZone->getDSTSavings()) / 1000;
125+
// get raw offset that intl time zone would use (divided by 1000, b/c different units)
126+
$rawIntlOffset = $intlTimeZone->getRawOffset() / 1000;
127+
128+
// compare the php and intl offsets
129+
// if either are the same, we're good to proceed with the intl approach, as we used to;
130+
// but if they're both different, we should use PHP to format the date
131+
if ($phpOffset === $dstIntlOffset || $phpOffset === $rawIntlOffset) {
132+
return null;
133+
}
134+
135+
// copied from yii\i18n\Formatter::formatDateTimeValue()
136+
if (strncmp($format, 'php:', 4) === 0) {
137+
$format = substr($format, 4);
138+
} else {
139+
$format = FormatConverter::convertDateIcuToPhp($format, 'date', Yii::$app->language);
140+
}
141+
142+
return $value->format($format);
143+
}
144+
90145
/**
91146
* @inheritdoc
92147
* @param int|string|DateTime $value

0 commit comments

Comments
 (0)