Skip to content

Commit 1cf4cc3

Browse files
committed
ext/intl: IntlDateFormatter::parseToCalendar addition.
Unlike IntlDateFormatter::parse, the timezone is updated accordingly. Close GH-13779
1 parent 5823a96 commit 1cf4cc3

File tree

6 files changed

+113
-9
lines changed

6 files changed

+113
-9
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ PHP NEWS
8686
. ResourceBundle::get() now has a tentative return type of:
8787
ResourceBundle|array|string|int|null
8888
. Added the new Grapheme function grapheme_str_split. (youkidearitai)
89+
. Addewd IntlDateFormatter::parseToCalendar. (David Carlier)
8990

9091
- LDAP:
9192
. Added LDAP_OPT_X_TLS_PROTOCOL_MAX/LDAP_OPT_X_TLS_PROTOCOL_TLS1_3

UPGRADING

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,8 @@ PHP 8.4 UPGRADE NOTES
469469
the IANA identifier from a given timezone.
470470
. Added grapheme_str_split which allow to support emoji and Variation
471471
Selectors.
472+
. Added IntlDateFormatter::parseToCalendar which behaves like
473+
IntlDateFormatter::parse except the time zone is updated.
472474

473475
- MBString:
474476
. Added mb_trim, mb_ltrim and mb_rtrim functions.

ext/intl/dateformat/dateformat.stub.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,11 @@ public static function formatObject($datetime, $format = null, ?string $locale =
159159
*/
160160
public function parse(string $string, &$offset = null): int|float|false {}
161161

162+
/**
163+
* @param int $offset
164+
*/
165+
public function parseToCalendar(string $string, &$offset = null): int|float|false {}
166+
162167
/**
163168
* @param int $offset
164169
* @return array<string, int>|false

ext/intl/dateformat/dateformat_arginfo.h

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/intl/dateformat/dateformat_parse.c

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
* if set to 1 - store any error encountered in the parameter parse_error
3232
* if set to 0 - no need to store any error encountered in the parameter parse_error
3333
*/
34-
static void internal_parse_to_timestamp(IntlDateFormatter_object *dfo, char* text_to_parse, size_t text_len, int32_t *parse_pos, zval *return_value)
34+
static void internal_parse_to_timestamp(IntlDateFormatter_object *dfo, char* text_to_parse, size_t text_len, int32_t *parse_pos, bool update_calendar, zval *return_value)
3535
{
3636
double result = 0;
3737
UDate timestamp =0;
@@ -42,13 +42,22 @@ static void internal_parse_to_timestamp(IntlDateFormatter_object *dfo, char* tex
4242
intl_convert_utf8_to_utf16(&text_utf16, &text_utf16_len, text_to_parse, text_len, &INTL_DATA_ERROR_CODE(dfo));
4343
INTL_METHOD_CHECK_STATUS(dfo, "Error converting timezone to UTF-16" );
4444

45-
timestamp = udat_parse( DATE_FORMAT_OBJECT(dfo), text_utf16, text_utf16_len, parse_pos, &INTL_DATA_ERROR_CODE(dfo));
46-
if( text_utf16 ){
47-
efree(text_utf16);
45+
if (UNEXPECTED(update_calendar)) {
46+
UCalendar *parsed_calendar = (UCalendar *)udat_getCalendar(DATE_FORMAT_OBJECT(dfo));
47+
udat_parseCalendar(DATE_FORMAT_OBJECT(dfo), parsed_calendar, text_utf16, text_utf16_len, parse_pos, &INTL_DATA_ERROR_CODE(dfo));
48+
if (text_utf16) {
49+
efree(text_utf16);
50+
}
51+
INTL_METHOD_CHECK_STATUS( dfo, "Calendar parsing failed" );
52+
timestamp = ucal_getMillis( parsed_calendar, &INTL_DATA_ERROR_CODE(dfo));
53+
} else {
54+
timestamp = udat_parse(DATE_FORMAT_OBJECT(dfo), text_utf16, text_utf16_len, parse_pos, &INTL_DATA_ERROR_CODE(dfo));
55+
if (text_utf16) {
56+
efree(text_utf16);
57+
}
4858
}
4959

5060
INTL_METHOD_CHECK_STATUS( dfo, "Date parsing failed" );
51-
5261
/* Since return is in sec. */
5362
result = (double)timestamp / U_MILLIS_PER_SECOND;
5463
if (result > (double)LONG_MAX || result < (double)LONG_MIN) {
@@ -145,18 +154,63 @@ PHP_FUNCTION(datefmt_parse)
145154
RETURN_FALSE;
146155
}
147156
parse_pos = (int32_t)long_parse_pos;
148-
if((size_t)parse_pos > text_len) {
157+
if ((size_t)parse_pos > text_len) {
149158
RETURN_FALSE;
150159
}
151160
}
152-
internal_parse_to_timestamp( dfo, text_to_parse, text_len, z_parse_pos?&parse_pos:NULL, return_value);
153-
if(z_parse_pos) {
161+
internal_parse_to_timestamp( dfo, text_to_parse, text_len, z_parse_pos ? &parse_pos : NULL, false, return_value);
162+
if (z_parse_pos) {
154163
zval_ptr_dtor(z_parse_pos);
155164
ZVAL_LONG(z_parse_pos, parse_pos);
156165
}
157166
}
158167
/* }}} */
159168

169+
PHP_METHOD(IntlDateFormatter, parseToCalendar)
170+
{
171+
zend_string *text_to_parse = NULL;
172+
zval* z_parse_pos = NULL;
173+
int32_t parse_pos = -1;
174+
175+
DATE_FORMAT_METHOD_INIT_VARS;
176+
177+
ZEND_PARSE_PARAMETERS_START(1, 2)
178+
Z_PARAM_STR(text_to_parse)
179+
Z_PARAM_OPTIONAL
180+
Z_PARAM_ZVAL(z_parse_pos)
181+
ZEND_PARSE_PARAMETERS_END();
182+
183+
object = ZEND_THIS;
184+
185+
/* Fetch the object. */
186+
DATE_FORMAT_METHOD_FETCH_OBJECT;
187+
188+
if (z_parse_pos) {
189+
zend_long long_parse_pos;
190+
ZVAL_DEREF(z_parse_pos);
191+
bool failed = false;
192+
long_parse_pos = zval_try_get_long(z_parse_pos, &failed);
193+
if (failed) {
194+
zend_argument_type_error(2, "must be of type int, %s given", zend_zval_value_name(z_parse_pos));
195+
RETURN_THROWS();
196+
}
197+
if (ZEND_LONG_INT_OVFL(long_parse_pos)) {
198+
intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR);
199+
intl_error_set_custom_msg(NULL, "String index is out of valid range.", 0);
200+
RETURN_FALSE;
201+
}
202+
parse_pos = (int32_t)long_parse_pos;
203+
if (parse_pos != -1 && (size_t)parse_pos > ZSTR_LEN(text_to_parse)) {
204+
RETURN_FALSE;
205+
}
206+
}
207+
internal_parse_to_timestamp( dfo, ZSTR_VAL(text_to_parse), ZSTR_LEN(text_to_parse), z_parse_pos ? &parse_pos : NULL, true, return_value);
208+
if (z_parse_pos) {
209+
zval_ptr_dtor(z_parse_pos);
210+
ZVAL_LONG(z_parse_pos, parse_pos);
211+
}
212+
}
213+
160214
/* {{{ Parse the string $value to a localtime array */
161215
PHP_FUNCTION(datefmt_localtime)
162216
{

ext/intl/tests/gh13766.phpt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
IntlDateFormatter::parse update its calendar
3+
--EXTENSIONS--
4+
intl
5+
--FILE--
6+
<?php
7+
$oIntlDateFormatter = new IntlDateFormatter("en_GB");
8+
$oIntlDateFormatter->setTimeZone('Europe/Berlin');
9+
$oIntlDateFormatter->setPattern('VV');
10+
11+
var_dump($oIntlDateFormatter->parse('America/Los_Angeles', $offset1));
12+
var_dump($oIntlDateFormatter->getTimeZone()->getID());
13+
var_dump($oIntlDateFormatter->parseToCalendar('America/Los_Angeles', $offset2));
14+
var_dump($oIntlDateFormatter->getTimeZone()->getID());
15+
$offset3 = "offset";
16+
17+
try {
18+
$oIntlDateFormatter->parseToCalendar('America/Los_Angeles', $offset3);
19+
} catch (\TypeError $e) {
20+
echo $e->getMessage() . PHP_EOL;
21+
}
22+
$offset3 = PHP_INT_MAX * 16;
23+
try {
24+
$oIntlDateFormatter->parseToCalendar('America/Los_Angeles', $offset3);
25+
} catch (\ValueError $e) {
26+
echo $e->getMessage();
27+
}
28+
--EXPECTF--
29+
int(%d)
30+
string(13) "Europe/Berlin"
31+
int(%d)
32+
string(19) "America/Los_Angeles"
33+
IntlDateFormatter::parseToCalendar(): Argument #2 ($offset) must be of type int, string given
34+
35+
Deprecated: Implicit conversion from float 1.4757395258967641E+20 to int loses precision in %s on line %d

0 commit comments

Comments
 (0)