Implement IntlGregorianCalendar::createFromDate() and IntlGregorianCalendar::createFromDateTime()

This commit is contained in:
Máté Kocsis 2023-07-12 00:44:53 +02:00
parent f236eb83b4
commit 1486f52a12
10 changed files with 250 additions and 34 deletions

View File

@ -488,6 +488,10 @@ class IntlCalendar
/** @not-serializable */
class IntlGregorianCalendar extends IntlCalendar
{
public static function createFromDate(int $year, int $month, int $dayOfMonth): static {}
public static function createFromDateTime(int $year, int $month, int $dayOfMonth, int $hour, int $minute, ?int $second = null): static {}
/**
* @param DateTimeZone|IntlTimeZone|string|int|null $timezoneOrYear
* @param string|int|null $localeOrMonth

View File

@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: c1d451a668ccab343208ab5cc30ab8457d6802b9 */
* Stub hash: 1eb2511da8ecb00132a00d1f3c95e03f9463db55 */
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_IntlCalendar___construct, 0, 0, 0)
ZEND_END_ARG_INFO()
@ -131,6 +131,21 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_IntlCalendar_set, 0, 0, 2)
ZEND_ARG_TYPE_INFO(0, second, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_IntlCalendar_setDate, 0, 3, IS_VOID, 0)
ZEND_ARG_TYPE_INFO(0, year, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, month, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, dayOfMonth, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_IntlCalendar_setDateTime, 0, 5, IS_VOID, 0)
ZEND_ARG_TYPE_INFO(0, year, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, month, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, dayOfMonth, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, hour, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, minute, IS_LONG, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, second, IS_LONG, 1, "null")
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_IntlCalendar_setFirstDayOfWeek, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, dayOfWeek, IS_LONG, 0)
ZEND_END_ARG_INFO()
@ -156,6 +171,21 @@ ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_IntlCalendar_toDateTime, 0, 0, DateTime, MAY_BE_FALSE)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_IntlGregorianCalendar_createFromDate, 0, 3, IS_STATIC, 0)
ZEND_ARG_TYPE_INFO(0, year, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, month, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, dayOfMonth, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_IntlGregorianCalendar_createFromDateTime, 0, 5, IS_STATIC, 0)
ZEND_ARG_TYPE_INFO(0, year, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, month, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, dayOfMonth, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, hour, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, minute, IS_LONG, 0)
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, second, IS_LONG, 1, "null")
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_IntlGregorianCalendar___construct, 0, 0, 0)
ZEND_ARG_INFO(0, timezoneOrYear)
ZEND_ARG_INFO(0, localeOrMonth)
@ -213,6 +243,8 @@ ZEND_FUNCTION(intlcal_is_weekend);
ZEND_FUNCTION(intlcal_roll);
ZEND_FUNCTION(intlcal_is_set);
ZEND_FUNCTION(intlcal_set);
ZEND_METHOD(IntlCalendar, setDate);
ZEND_METHOD(IntlCalendar, setDateTime);
ZEND_FUNCTION(intlcal_set_first_day_of_week);
ZEND_FUNCTION(intlcal_set_lenient);
ZEND_FUNCTION(intlcal_set_repeated_wall_time_option);
@ -220,6 +252,8 @@ ZEND_FUNCTION(intlcal_set_skipped_wall_time_option);
ZEND_FUNCTION(intlcal_set_time);
ZEND_FUNCTION(intlcal_set_time_zone);
ZEND_FUNCTION(intlcal_to_date_time);
ZEND_METHOD(IntlGregorianCalendar, createFromDate);
ZEND_METHOD(IntlGregorianCalendar, createFromDateTime);
ZEND_METHOD(IntlGregorianCalendar, __construct);
ZEND_FUNCTION(intlgregcal_set_gregorian_change);
ZEND_FUNCTION(intlgregcal_get_gregorian_change);
@ -266,6 +300,8 @@ static const zend_function_entry class_IntlCalendar_methods[] = {
ZEND_ME_MAPPING(roll, intlcal_roll, arginfo_class_IntlCalendar_roll, ZEND_ACC_PUBLIC)
ZEND_ME_MAPPING(isSet, intlcal_is_set, arginfo_class_IntlCalendar_isSet, ZEND_ACC_PUBLIC)
ZEND_ME_MAPPING(set, intlcal_set, arginfo_class_IntlCalendar_set, ZEND_ACC_PUBLIC)
ZEND_ME(IntlCalendar, setDate, arginfo_class_IntlCalendar_setDate, ZEND_ACC_PUBLIC)
ZEND_ME(IntlCalendar, setDateTime, arginfo_class_IntlCalendar_setDateTime, ZEND_ACC_PUBLIC)
ZEND_ME_MAPPING(setFirstDayOfWeek, intlcal_set_first_day_of_week, arginfo_class_IntlCalendar_setFirstDayOfWeek, ZEND_ACC_PUBLIC)
ZEND_ME_MAPPING(setLenient, intlcal_set_lenient, arginfo_class_IntlCalendar_setLenient, ZEND_ACC_PUBLIC)
ZEND_ME_MAPPING(setRepeatedWallTimeOption, intlcal_set_repeated_wall_time_option, arginfo_class_IntlCalendar_setRepeatedWallTimeOption, ZEND_ACC_PUBLIC)
@ -278,6 +314,8 @@ static const zend_function_entry class_IntlCalendar_methods[] = {
static const zend_function_entry class_IntlGregorianCalendar_methods[] = {
ZEND_ME(IntlGregorianCalendar, createFromDate, arginfo_class_IntlGregorianCalendar_createFromDate, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(IntlGregorianCalendar, createFromDateTime, arginfo_class_IntlGregorianCalendar_createFromDateTime, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
ZEND_ME(IntlGregorianCalendar, __construct, arginfo_class_IntlGregorianCalendar___construct, ZEND_ACC_PUBLIC)
ZEND_ME_MAPPING(setGregorianChange, intlgregcal_set_gregorian_change, arginfo_class_IntlGregorianCalendar_setGregorianChange, ZEND_ACC_PUBLIC)
ZEND_ME_MAPPING(getGregorianChange, intlgregcal_get_gregorian_change, arginfo_class_IntlGregorianCalendar_getGregorianChange, ZEND_ACC_PUBLIC)

View File

@ -39,10 +39,45 @@ using icu::Locale;
using icu::UnicodeString;
using icu::StringPiece;
#define ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(argument, zpp_arg_position) \
if (UNEXPECTED(argument < INT32_MIN || argument > INT32_MAX)) { \
zend_argument_value_error(zpp_arg_position, "must be between %d and %d", INT32_MIN, INT32_MAX); \
RETURN_THROWS(); \
}
static inline GregorianCalendar *fetch_greg(Calendar_object *co) {
return (GregorianCalendar*)co->ucal;
}
static bool set_gregorian_calendar_time_zone(GregorianCalendar *gcal, UErrorCode status)
{
if (U_FAILURE(status)) {
intl_error_set(NULL, status,
"IntlGregorianCalendar: Error creating ICU GregorianCalendar from date",
0
);
return false;
}
timelib_tzinfo *tzinfo = get_timezone_info();
UnicodeString tzstr = UnicodeString::fromUTF8(StringPiece(tzinfo->name));
if (tzstr.isBogus()) {
intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
"IntlGregorianCalendar: Could not create UTF-8 string "
"from PHP's default timezone name (see date_default_timezone_get())",
0
);
return false;
}
TimeZone *tz = TimeZone::createTimeZone(tzstr);
gcal->adoptTimeZone(tz);
return true;
}
static void _php_intlgregcal_constructor_body(
INTERNAL_FUNCTION_PARAMETERS, bool is_constructor, zend_error_handling *error_handling, bool *error_handling_replaced)
{
@ -135,11 +170,7 @@ static void _php_intlgregcal_constructor_body(
} else {
// From date/time (3, 5 or 6 arguments)
for (int i = 0; i < variant; i++) {
if (UNEXPECTED(largs[i] < INT32_MIN || largs[i] > INT32_MAX)) {
zend_argument_value_error(getThis() ? (i-1) : i,
"must be between %d and %d", INT32_MIN, INT32_MAX);
RETURN_THROWS();
}
ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(largs[i], getThis() ? (i-1) : i);
}
if (variant == 3) {
@ -152,27 +183,11 @@ static void _php_intlgregcal_constructor_body(
gcal = new GregorianCalendar((int32_t)largs[0], (int32_t)largs[1],
(int32_t)largs[2], (int32_t)largs[3], (int32_t)largs[4], (int32_t)largs[5],
status);
}
if (U_FAILURE(status)) {
intl_error_set(NULL, status, "intlgregcal_create_instance: error "
"creating ICU GregorianCalendar from date", 0);
if (gcal) {
delete gcal;
}
if (!is_constructor) {
zval_ptr_dtor(return_value);
RETVAL_NULL();
}
return;
} else {
ZEND_UNREACHABLE();
}
timelib_tzinfo *tzinfo = get_timezone_info();
UnicodeString tzstr = UnicodeString::fromUTF8(StringPiece(tzinfo->name));
if (tzstr.isBogus()) {
intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR,
"intlgregcal_create_instance: could not create UTF-8 string "
"from PHP's default timezone name (see date_default_timezone_get())",
0);
if (!set_gregorian_calendar_time_zone(gcal, status)) {
delete gcal;
if (!is_constructor) {
zval_ptr_dtor(return_value);
@ -180,9 +195,6 @@ static void _php_intlgregcal_constructor_body(
}
return;
}
TimeZone *tz = TimeZone::createTimeZone(tzstr);
gcal->adoptTimeZone(tz);
}
co->ucal = gcal;
@ -208,6 +220,82 @@ U_CFUNC PHP_METHOD(IntlGregorianCalendar, __construct)
}
}
U_CFUNC PHP_METHOD(IntlGregorianCalendar, createFromDate)
{
zend_long year, month, day;
UErrorCode status = U_ZERO_ERROR;
zend_error_handling error_handling;
Calendar_object *co;
GregorianCalendar *gcal;
intl_error_reset(NULL);
if (zend_parse_parameters(ZEND_NUM_ARGS(), "lll", &year, &month, &day) == FAILURE) {
RETURN_THROWS();
}
ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(year, 1);
ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(month, 2);
ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(day, 3);
zend_replace_error_handling(EH_THROW, IntlException_ce_ptr, &error_handling);
gcal = new GregorianCalendar((int32_t) year, (int32_t) month, (int32_t) day, status);
if (!set_gregorian_calendar_time_zone(gcal, status)) {
delete gcal;
goto cleanup;
}
object_init_ex(return_value, GregorianCalendar_ce_ptr);
co = Z_INTL_CALENDAR_P(return_value);
co->ucal = gcal;
cleanup:
zend_restore_error_handling(&error_handling);
}
U_CFUNC PHP_METHOD(IntlGregorianCalendar, createFromDateTime)
{
zend_long year, month, day, hour, minute, second;
bool second_is_null = 1;
UErrorCode status = U_ZERO_ERROR;
zend_error_handling error_handling;
Calendar_object *co;
GregorianCalendar *gcal;
intl_error_reset(NULL);
if (zend_parse_parameters(ZEND_NUM_ARGS(), "lllll|l!", &year, &month, &day, &hour, &minute, &second, &second_is_null) == FAILURE) {
RETURN_THROWS();
}
ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(year, 1);
ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(month, 2);
ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(day, 3);
ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(hour, 4);
ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(minute, 5);
zend_replace_error_handling(EH_THROW, IntlException_ce_ptr, &error_handling);
if (second_is_null) {
gcal = new GregorianCalendar((int32_t) year, (int32_t) month, (int32_t) day, (int32_t) hour, (int32_t) minute, status);
} else {
ZEND_VALUE_ERROR_OUT_OF_BOUND_VALUE(second, 6);
gcal = new GregorianCalendar((int32_t) year, (int32_t) month, (int32_t) day, (int32_t) hour, (int32_t) minute, (int32_t) second, status);
}
if (!set_gregorian_calendar_time_zone(gcal, status)) {
delete gcal;
goto cleanup;
}
object_init_ex(return_value, GregorianCalendar_ce_ptr);
co = Z_INTL_CALENDAR_P(return_value);
co->ucal = gcal;
cleanup:
zend_restore_error_handling(&error_handling);
}
U_CFUNC PHP_FUNCTION(intlgregcal_set_gregorian_change)
{
double date;

View File

@ -9,9 +9,9 @@ intl
ini_set("intl.error_level", E_WARNING);
ini_set("intl.default_locale", "nl");
$intlcal1 = new IntlGregorianCalendar(2012, 1, 29, 16, 59, 59);
$intlcal1 = IntlGregorianCalendar::createFromDateTime(2012, 1, 29, 16, 59, 59);
$intlcal2 = IntlCalendar::createInstance(null, '@calendar=japanese');
$intlcal3 = new IntlGregorianCalendar(2012, 1, 29, 17, 00, 00);
$intlcal3 = IntlGregorianCalendar::createFromDateTime(2012, 1, 29, 17, 00, 00);
$intlcal2->setTime($intlcal1->getTime());
var_dump($intlcal2->getType());
@ -52,4 +52,4 @@ bool(false)
string(10) "3 before 2"
bool(false)
string(9) "3 after 2"
bool(true)
bool(true)

View File

@ -9,7 +9,7 @@ intl
ini_set("intl.error_level", E_WARNING);
ini_set("intl.default_locale", "nl");
$intlcal = new IntlGregorianCalendar(2012, 1, 29);
$intlcal = IntlGregorianCalendar::createFromDate(2012, 1, 29);
var_dump(
$intlcal->getErrorCode(),
intlcal_get_error_code($intlcal),

View File

@ -10,7 +10,7 @@ ini_set("intl.default_locale", "nl");
date_default_timezone_set('Europe/Amsterdam');
//28 October 2012, transition from DST
$intlcal = new IntlGregorianCalendar(2012, 9, 28, 0, 0, 0);
$intlcal = IntlGregorianCalendar::createFromDateTime(2012, 9, 28, 0, 0, 0);
var_dump($intlcal->setRepeatedWallTimeOption(IntlCalendar::WALLTIME_LAST));
var_dump($intlcal->getRepeatedWallTimeOption());
$intlcal->set(IntlCalendar::FIELD_HOUR_OF_DAY, 2);

View File

@ -5,6 +5,9 @@ intl
--FILE--
<?php
$intlcal = IntlCalendar::createInstance('UTC');
$intlcal->clear();
//two minutes to midnight!
$intlcal->setDateTime(2012, 1, 29, 23, 58);
var_dump($intlcal->getTime(), strtotime('2012-02-29 23:58:00 +0000') * 1000.);

View File

@ -0,0 +1,31 @@
--TEST--
IntlGregorianCalendar::setDate(): error cases
--EXTENSIONS--
intl
--SKIPIF--
<?php if (PHP_INT_SIZE != 8) die("skip: 64-bit only"); ?>
--FILE--
<?php
try {
var_dump(IntlGregorianCalendar::createFromDate(99999999999, 1, 1));
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
try {
var_dump(IntlGregorianCalendar::createFromDate(1, 99999999999, 1));
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
try {
var_dump(IntlGregorianCalendar::createFromDate(1, 1, 99999999999));
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
IntlGregorianCalendar::createFromDate(): Argument #1 ($year) must be between -2147483648 and 2147483647
IntlGregorianCalendar::createFromDate(): Argument #2 ($month) must be between -2147483648 and 2147483647
IntlGregorianCalendar::createFromDate(): Argument #3 ($dayOfMonth) must be between -2147483648 and 2147483647

View File

@ -0,0 +1,52 @@
--TEST--
IntlGregorianCalendar::setDateTime(): error cases
--EXTENSIONS--
intl
--SKIPIF--
<?php if (PHP_INT_SIZE != 8) die("skip: 64-bit only"); ?>
--FILE--
<?php
try {
var_dump(IntlGregorianCalendar::createFromDateTime(99999999999, 1, 1, 1, 1, 1));
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
try {
var_dump(IntlGregorianCalendar::createFromDateTime(1, 99999999999, 1, 1, 1, 1));
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
try {
var_dump(IntlGregorianCalendar::createFromDateTime(1, 1, 99999999999, 1, 1, 1));
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
try {
var_dump(IntlGregorianCalendar::createFromDateTime(1, 1, 1, 99999999999, 1, 1));
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
try {
var_dump(IntlGregorianCalendar::createFromDateTime(1, 1, 1, 1, 99999999999, 1));
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
try {
var_dump(IntlGregorianCalendar::createFromDateTime(1, 1, 1, 1, 1, 99999999999));
} catch (ValueError $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
IntlGregorianCalendar::createFromDateTime(): Argument #1 ($year) must be between -2147483648 and 2147483647
IntlGregorianCalendar::createFromDateTime(): Argument #2 ($month) must be between -2147483648 and 2147483647
IntlGregorianCalendar::createFromDateTime(): Argument #3 ($dayOfMonth) must be between -2147483648 and 2147483647
IntlGregorianCalendar::createFromDateTime(): Argument #4 ($hour) must be between -2147483648 and 2147483647
IntlGregorianCalendar::createFromDateTime(): Argument #5 ($minute) must be between -2147483648 and 2147483647
IntlGregorianCalendar::createFromDateTime(): Argument #6 ($second) must be between -2147483648 and 2147483647

View File

@ -10,7 +10,7 @@ ini_set("intl.error_level", E_WARNING);
//ini_set("intl.default_locale", "nl");
ini_set('date.timezone', 'Europe/Lisbon');
$cal = new IntlGregorianCalendar(2012,04,17,17,35,36);
$cal = IntlGregorianCalendar::createFromDateTime(2012,04,17,17,35,36);
$msgf = new MessageFormatter('pt_PT', '{0,date,full} {0,time,h:m:s a V}');
echo $msgf->format(array($cal)), "\n";