close
The Wayback Machine - https://web.archive.org/web/20210614090245/https://github.com/php/php-src/pull/6771
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: add IntlDatePatternGenerator #6771

Merged
merged 9 commits into from Jun 10, 2021

Conversation

@deltragon
Copy link
Contributor

@deltragon deltragon commented Mar 14, 2021

ICU exposes the DateTimePatternGenerator class that allows generating a formatting pattern from a given "skeleton" for a given locale.
This allows more flexibility than the current $format argument of IntlDateFormatter::format(), which takes either a few pre-defined constants or a hard-coded format.
This functionality also has been requested in the past.

For example, if an application requires a format that will always use the long version of a year, but the short version of a month (eg. "MM/dd/YYYY" for "en_US", or "dd.MM.YYYY" for "de_DE"), one is out of luck.
IntlDateFormatter::SHORT will use the short year for "de_DE" ("dd.MM.YY"), and IntlDateFormatter::MEDIUM will use the long version of a month for "en_US" ("MMM dd, YYYY").

With IntlDatePatternGenerator::getBestPattern(), a skeleton can be provided to generate the appropriate pattern for a given locale.
In the use-case given above, the skeleton "YYYYMMdd" will generate the desired patterns for each locale, which can then be passed to IntlDateFormatter::format().

Note about the implementation:
This had been previously implemented as a static method on IntlDateFormatter, creating a new DateTimePatternGenerator on every call.
It is now a separate class that has to be instantiated before use. This leaves us open to implementing other methods/options provided by the underlying DateTimePatternGenerator. I am not sure if there is any use in those, however, as getBestPattern() seems to be the only one with a real-world use-case to me.

(Note: I am aware that this needs to go to internals/an RFC first. I'm currently having issues with my email provider that prevent me from subscribing to/sending to the list. I will send this proposal once these are resolved).

--SKIPIF--
<?php
if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?>
<?php if (version_compare(INTL_ICU_VERSION, '50.1.2', '<')) die('skip for ICU < 50.1.2'); ?>

This comment has been minimized.

@deltragon

deltragon Mar 14, 2021
Author Contributor

As an aside:
This line is included in almost all intl dateformat tests - there, it is used in this form:

<?php if (version_compare(INTL_ICU_VERSION, '50.1.2') >=  0) die('skip for ICU < 50.1.2'); ?>

This, however, ends up skipping the test on my machine (INTL_ICU_VERSION === '67.1'). I assume version_compare is used incorrectly here? Should this be fixed for all other tests as well (and switched to what is the more readable form in my opinion)? Is this line still necessary (as PHP 7.4 requires ICU 50.1)?

This comment has been minimized.

@kocsismate

kocsismate Mar 14, 2021
Member

Skip messages are a bit deceptive: if fact, die('skip for ICU < 50.1.2'); means that the test is skipped because it's for ICU < 50.1.2. It also took quite some time for me to get accustomed to this.

If ICU 50.1 is required now then these tests are probably not in use anymore 🤔

This comment has been minimized.

@kocsismate

kocsismate Mar 14, 2021
Member

And I don't think the version_compare is necessary for this test.

This comment has been minimized.

@deltragon

deltragon Mar 14, 2021
Author Contributor

Ah yes, that explains the message.
I still dont understand why these tests are not required for newer ICU versions - I would expect basic tests like

<?php if (version_compare(INTL_ICU_VERSION, '50.1.2') >= 0) die('skip for ICU < 50.1.2'); ?>

to still be in use for newer versions/after PHP 7.4.
(For an incomplete list of affected tests, this is the commit that starts skipping a bunch of them for ICU >= 51.2 (was later changed to 50.1): 4840b0a)

This comment has been minimized.

@nikic

nikic Mar 15, 2021
Member

Tests for other ICU versions are under _variant2, _variant3 etc.

This comment has been minimized.

@nikic

nikic Mar 15, 2021
Member

If ICU 50.1 is required now then these tests are probably not in use anymore thinking

I believe the reason why these tests still exist is that our requirement is ICU 50.1, while this checks for ICU 50.1.2.

Though maybe we can bump up the requirements slightly, it looks like RHEL 7 nowadays ships with ICU 50.2 (according to https://rpms.remirepo.net/rpmphp/zoom.php?rpm=icu). @remicollet Would a 50.2 requirement be safe?

This comment has been minimized.

@MaxSem

MaxSem Mar 28, 2021
Contributor

The same RHEL version comes with PHP 5: https://rpms.remirepo.net/rpmphp/zoom.php?rpm=php so is RHEL even supposed to dictate what PHP 8 should do?

Copy link
Member

@kocsismate kocsismate left a comment

Personally, I'd look forward to an IntlDateTimePatternGenerator class, even though it would expose only a small subset of all the ICU capabilities. IMO the static method on IntlDateFormatter doesn't really fit there.

ext/intl/dateformat/dateformat.stub.php Outdated Show resolved Hide resolved
ext/intl/tests/dateformat_get_best_pattern.phpt Outdated Show resolved Hide resolved
--SKIPIF--
<?php
if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?>
<?php if (version_compare(INTL_ICU_VERSION, '50.1.2', '<')) die('skip for ICU < 50.1.2'); ?>

This comment has been minimized.

@kocsismate

kocsismate Mar 14, 2021
Member

And I don't think the version_compare is necessary for this test.

@deltragon deltragon force-pushed the deltragon:intl-datetime-patterngenerator branch from d3bb97c to 2eaed3c Mar 28, 2021
@deltragon
Copy link
Contributor Author

@deltragon deltragon commented Mar 28, 2021

@kocsismate Thank you for the review!
I have switched to a IntlDateTimePatternGenerator class as suggested, and addressed your other comments as well.
I have put the new files in ext/intl/dateformat/ as well, since they belong there thematically - or would you put them in a separate folder?
There are also some names of internal methods/macros I am not entirely sure about, is it okay to leave them as is or should I try to be more consistent there? "DateTimePatternGenerator" is a rather unwieldy name, unfortunately.

@deltragon deltragon changed the title Proposal: add IntlDateFormatter::getBestPattern() Proposal: add IntlDateTimePatternGenerator Mar 28, 2021
DTPATTERNGEN_METHOD_FETCH_OBJECT_NO_CHECK;

if (dtpgo->dtpg != NULL) {
intl_errors_set(DTPATTERNGEN_ERROR_P(dtpgo), U_ILLEGAL_ARGUMENT_ERROR, "datetimepatterngenerator_create: cannot call constructor twice", 0);

This comment has been minimized.

@kocsismate

kocsismate Mar 28, 2021
Member

What's the reason of this? I see that the check is from IntlDateFormatter, but I don't know why these are necessary.

This comment has been minimized.

@deltragon

deltragon Mar 28, 2021
Author Contributor

For IntlDateFormatter, there is this test:

$x = new IntlDateFormatter('en', 1, 1);
var_dump($x->__construct('en', 1, 1));

How is that (anti-)pattern handled in other cases?


intl_stringFromChar(skeleton_uncleaned, skeleton_str, skeleton_len, DTPATTERNGEN_ERROR_CODE_P(dtpgo));

INTL_METHOD_CHECK_STATUS(dtpgo, "datetimepatterngenerator_get_best_pattern: skeleton was not a valid UTF-8 string");

This comment has been minimized.

@kocsismate

kocsismate Mar 28, 2021
Member

Suggested change
INTL_METHOD_CHECK_STATUS(dtpgo, "datetimepatterngenerator_get_best_pattern: skeleton was not a valid UTF-8 string");
INTL_METHOD_CHECK_STATUS(dtpgo, "Skeleton is not a valid UTF-8 string");

So the function name shouldn't be added (it's superfluous), the message should start with a capital character, and AFAIR we don't use past tense

This comment has been minimized.

@kocsismate

kocsismate Mar 28, 2021
Member

Also, I'm wondering if we can consider an invalid UTF-8 skeleton a programmatic error? As far as I see, we can, because the input should come from the programmer, not from the user. If my hypothesis is ok, then we could throw an exception instead of these warnings, like this:
zend_argument_value_error(2, "must be a valid UTF-8 string") (the 2 should be adapted in case of the method counterpart, since $skeleton is the 1st parameter there).

Last year, we migrated hundreds of warnings to exceptions, so it would be nice to follow this practice here as well, if possible.

This comment has been minimized.

@deltragon

deltragon Mar 28, 2021
Author Contributor

I have adjusted the messages.
I would agree that this is most likely a programming error - I used a warning here for consistency with similar cases (eg. IntlDateFormatter::setPattern()), but I can switch to using an exception if preferred.

@deltragon deltragon force-pushed the deltragon:intl-datetime-patterngenerator branch 2 times, most recently from cb8c1c5 to 5084f32 Mar 28, 2021
@deltragon deltragon force-pushed the deltragon:intl-datetime-patterngenerator branch from 5084f32 to 068cceb Apr 30, 2021
@deltragon deltragon changed the title Proposal: add IntlDateTimePatternGenerator Proposal: add IntlDatePatternGenerator Apr 30, 2021
@deltragon deltragon force-pushed the deltragon:intl-datetime-patterngenerator branch from 05f307a to 5b74562 Jun 7, 2021
@deltragon
Copy link
Contributor Author

@deltragon deltragon commented Jun 7, 2021

Rebased and removed the legacy procedural API.

@nikic
nikic approved these changes Jun 8, 2021
@nikic
Copy link
Member

@nikic nikic commented Jun 8, 2021

Looks like this needs a rebase. While at it, it would be great to add a mention of the new class in the UPGRADING file.

@deltragon deltragon force-pushed the deltragon:intl-datetime-patterngenerator branch from 7e50a00 to 19ffd97 Jun 8, 2021
@deltragon
Copy link
Contributor Author

@deltragon deltragon commented Jun 8, 2021

Rebased.

@nikic nikic merged commit ae9f6e7 into php:master Jun 10, 2021
6 checks passed
6 checks passed
@cirrus-ci
FREEBSD_DEBUG_NTS Task Summary
Details
@azure-pipelines
php.php-src Build #20210608.14 succeeded
Details
@azure-pipelines
php.php-src (DEBUG_NTS) DEBUG_NTS succeeded
Details
@azure-pipelines
php.php-src (I386_DEBUG_ZTS) I386_DEBUG_ZTS succeeded
Details
@azure-pipelines
php.php-src (MACOS_DEBUG_NTS) MACOS_DEBUG_NTS succeeded
Details
@azure-pipelines
php.php-src (RELEASE_ZTS) RELEASE_ZTS succeeded
Details
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
5 participants