PHP date format with intl-aware day suffix? - php

Sorry if this is a dupe - lots of similar questions but obviously if I could find an exact answer I wouldn't be asking :)
Note I'm coming from .Net and am a PHP newbie, so there may be noob-scale errors.
I would like to be able to output e.g. new DateTime('2014-01-01 13:15:00') as:
'Wednesday the 1st of January 2014 at 1:15PM' (possible - non-localized) or 'Mercredi 1er Janvier 2014 à 13h15' (not possible?).
Basically, there seems to be no ISO formatting equivalent to PHP's 'S' date format specifier, nor is there one for strftime?
The IntlDateFormatter::FULL comes close - but 'Wednesday, 1 January' or 'mercredi 1 janvier' is not good English (or French) - but it seems to be the closest that I can get? I could live without the 'on', 'the' and 'at' if I had to, but ordinal suffixes would be nice. ('Wednesday one January' - what's that, the beginning to a poem?)
I did see one example on the strftime section comments on PHP.net addressing this issue (which seems to suggest that it is an issue) - however it only seemed to add the English suffixes, which didn't seem much use? I'd like a simple method that takes a UTC datetime, a locale and a timezone and outputs a localized string - preferably in 'proper' human-readable format (as above) as is possible in English. I'd like to achieve this without writing a format string for every language in the world. It would also be nice if it worked on my Windows dev box as well as the *nix production box.
<?php
$utcdate = new DateTime('2014-01-01 13:15:00', new DateTimeZone('UTC'));
echo $utcdate->format('l \t\h\e jS \o\f F Y \a\t g:ia') . "<br>";
function dumpDates($date, $locale, $tz){
$date->setTimeZone(new DateTimeZone($tz));
$fmt = new IntlDateFormatter( $locale, IntlDateFormatter::FULL, IntlDateFormatter::FULL,
$tz, IntlDateFormatter::GREGORIAN );
echo $fmt->format($date) . "<br>";
// doesn't work under windows?
setLocale(LC_TIME, $locale);
echo strftime('%A, %#d %B %Y %I:%M:%S %p', $date->getTimeStamp()) . "<br>";
}
dumpDates($utcdate, 'en_GB', 'Europe/London');
dumpDates($utcdate, 'de_DE', 'Europe/Berlin');
dumpDates($utcdate, 'fr_FR', 'Europe/Paris');
?>

The full part of this question - including full grammatical legibility - would be very difficult to do without either, as you say, writing a format string for every language in the world, or finding a library that contains such strings. MomentJs seems to provide great intl support, but after a cursory search, I haven't been able to find a PHP equivalent, other than the intl extension.
You could get to the stage of providing an internationalised form including ordinal-based number by using a combination of IntlDateFormatter and NumberFormatter, by first using NumberFormatter to get the pattern for the date's ordinal suffix/prefix:
$numFmt = new NumberFormatter('fr_FR', NumberFormatter::ORDINAL);
$ordinalDay = $numFmt->format($date->format('j'));
You could then create a IntlDateFormatter that allows you to retrieve the pattern for the Full date format of the target language:
$dateFormat = new IntlDateFormatter('fr_FR', IntlDateFormatter::FULL, IntlDateFormatter::FULL, $tz, IntlDateFormatter::GREGORIAN);
$datePattern = $dateFormat->getPattern();
Finally, you would need to replace the section in the $datePattern representing the day with the escaped ordinal day pattern:
$datePattern = preg_replace('/d+', "'"+$ordinalDay+"'", $datePattern);
$dateFormat->setPattern($datePattern);
$outputDate = $dateFormat->format($date);
Note that the pattern used by IntlDateFormatter is different from the usual PHP date formatting format codes, here is the documentation for the codes recognised.
A warning; in internationalised formats that are fairly rigidly standardized, an ordinal number would look out of place. For example in chinese, the format is:
y年M月d日EEEE
and inserting the ordinal prefix that exists for written Chinese before the day value may look odd to a Chinese reader.

Related

Php mysql date in table

I have a date field in my table material
id: int;
dat_operation : date;
name : varchar(255);
I would like to know how to translate date format in French
I tried with:
<?php echo date("F j Y",strtotime($var['date_operation']));?>
But i have this result
June 14 2016
First, you'll have to set the "locale information", to specify which language you want to use. Keep in mind, that even though you set that language, it needs to be installed on the server you're running on. It most likely is, but you'll notice if the setlocale has no effect (default is English).
The second thing you'll need to know, is that date() isn't affected by this, you'll have to use strftime() instead, which has a slightly different formatting, which you'll find on the documentation.
An example of using French dates with these two functions:
setlocale(LC_ALL, 'fr_FR');
echo strftime("%B %e %Y", strtotime($var['date_operation']));
Reference and documentation:
http://php.net/manual/en/function.setlocale.php
http://php.net/manual/en/function.strftime.php
The modern and rock-solid approach is the intl (from "Internationalization") extension, which offers e.g. the IntlDateFormatter class:
$date = new DateTime('2016-06-14');
$fmt = new IntlDateFormatter('fr_FR', IntlDateFormatter::LONG, IntlDateFormatter::NONE, 'Europe/Paris', IntlDateFormatter::GREGORIAN);
var_dump($fmt->format($date));
string(12) "14 juin 2016"
If you think it's overkill for your project, you can use the legacy strftime() function but you need to change current locale:
$date = strtotime('2016-06-14');
var_dump(setlocale(LC_TIME, 'fr_FR', 'fr')); // Need to try values until you get true
var_dump(strftime('%B %e %Y', $date));
You need to have French locale data installed. In my experience, this works better on Unix-like systems than on Windows.

PHP: How to get timezone value (ex: Eastern Standard Time) from timezone name (ex: America/New_York)?

Is there a PHP function anywhere which converts between the timezone name (such as those found here: http://php.net/manual/en/timezones.america.php) and the "value" such as Eastern Standard Time, or Pacific Daylight Time?
Not looking to convert between zones, just get the EST, PDT, etc. names given the America/New_York (or other) name. The only similar question I found is for a different language.
If you you install the PHP Internationalization Package, you can do the following:
IntlTimeZone::createTimeZone('America/New_York')->getDisplayName()
This will return the CLDR English standard-long form by default, which is "Eastern Standard Time" in this case. You can find the other options available here. For example:
IntlTimeZone::createTimeZone('Europe/Paris')->getDisplayName(true, IntlTimeZone::DISPLAY_LONG, 'fr_FR')
The above will return "heure avancée d’Europe centrale" which is French for Central European Summer Time.
Be careful to pass the first parameter as true if DST is in effect for the date and time in question, or false otherwise. This is illustrated by the following technique:
$tz = 'America/New_York';
$dt = new DateTime('2016-01-01 00:00:00', new DateTimeZone($tz));
$dst = $dt->format('I');
$text = IntlTimeZone::createTimeZone($tz)->getDisplayName($dst);
echo($text); // "Eastern Standard Time"
Working PHP Fiddle Here
Please note that these strings are intended for display to an end user. If your intent is to use them for some programmatically purpose, such as calling into another API, then they are not appropriate - even if the English versions of some of the strings happen to align. For example, if you are sending the time zone to a Windows or .NET API, or to a Ruby on Rails API, these strings will not work.
If you know the value from your list at (http://php.net/manual/en/timezones.america.php) you can do something like.
<?php
$dateTime = new DateTime();
$dateTime->setTimeZone(new DateTimeZone('America/New_York'));
echo $dateTime->format('T');
?>

PHP DateTime::createFromFormat and multi languages

I'm using DateTime::createFromFormat() on a date that use some text like "April 15, 2016"
It's perfectly working as long as I'm using English culture.
April 15, 2016 -> ok
My code is set as WordPress plugin. Please understand that I have no control over the component that give me the date (as text) and the WordPress settings. If user set the WordPress installation on another language, the date will change from "April 15, 2016" to let's say (if French) "Avril 15, 2016".
It looks like that DateTime::createFromFormat() don't support other language than English so "April 15, 2016" will end up with:
"Fatal error: Call to a member function format() on boolean"
Did somebody have an idea how we can handle month date as text in several language? Using DateTime::createFromFormat() or another method in php?
Thanks
There are two ways to do this, but both depend on how exactly the input is generated ...
Maintain an array of month name translations
This is the simple/naive and most obvious way, I don't think the way to do this needs explaining.
However, depending on how WordPress (and/or the plugin that you're using) works, it may also be your only option.
The IntlDateFormatter class
This is the purely programmatic way and therefore what would be considered the "proper" one, but unfortunately this class comes as part of a PECL extension - intl - and isn't bundled with PHP.
It also requires that you know the language being used before parsing the date, but that shouldn't be a problem as that is how all localization solutions should work in the first place, so I assume this information is available to you in WordPress.
pickdate.js also seems to work with standard locales by default.
That being said, here's how it works:
// See http://userguide.icu-project.org/formatparse/datetime
$inputFormat = 'MMMM dd, yyyy';
$inputDate = 'Avril 15, 2016';
$locale = 'fr_FR';
// Here comes the magic ...
$dateFormatter = new IntlDateFormatter(
$locale,
IntlDateFormatter::LONG, // Not really important, may even be NONE
IntlDateFormatter::NONE, // Time ... we're not using it
NULL, // Will use date_default_timezone_get()
NULL, // Calendar; we don't need it
$inputFormat
);
// Will return bool(false) on failure, use getErrorMessage() for debugging
$unixTimestamp = $dateFormatter->parse($inputDate);
It's worth noting that IntlDateFormatter is designed mainly for creating localised outputs (and rightly so - you shouldn't be parsing a translated month name in the first place, or a name at all for that matter; numbers FTW), so we are indeed writing a lot of irrelevant stuff here.
For your use case, only the $locale and $inputFormat parameters matter.
There's one more caveat though - we need a timezone!
You can get it via (preferrably) $dateFormatter->getTimeZone() or in this case just date_default_timezone_get(), but you do need the timezone for two reasons:
You don't have a time value in your input, so IntlDateFormatter::parse() assumes "00:00:00".
UNIX timestamps are always in UTC, so IntlDateFormatter::parse() will take that into account and DateTime::createFromFormat('U', $unixTimestamp) will set the object's timezone to UTC!
If you're in another timezone, and more specifically in one on the east side of the world, this will happen:
$dateTime = DateTime::createFromFormat('U', $unixTimestamp);
var_dump($dateTime->format('Y-m-d')); // string(10) "2016-04-14" !!!
$dateTime->setTimeZone(new DateTimeZone($timezone));
var_dump($dateTime->format('Y-m-d')); // string(10) "2016-04-15"
You could tell IntlDateFormatter that you're in UTC in the first place, but that's technically cheating (wink) and depending on what you're using $dateTime for later, it may cause side-effects.

Generate appropriate pattern for IntlDateFormatter()->format() based on locale in PHP

I'm trying to rely on IntlDateFormatter to return the current date in a locale-based format. This means DD/MM/YYYY for it_IT, de_DE, fr_FR... or MM/DD/YYYY for en_US and so on for all the possible locales.
I'm trying to follow the same solution I used to retrieve the current month's name:
$formatter = new IntlDateFormatter("it_IT", IntlDateFormatter::FULL, IntlDateFormatter::FULL);
$formatter->setPattern("MMMM");
return $formatter->format($this);
This code correctly returns "January", "gennaio" etc.
The problem is, I cannot figure out the right pattern to get the date in the current locale format. The ICU User Guide mentions the DateTimePatternGenerator class, and this looks like it! But I cannot find it in PHP.
I would like to avoid a custom, huge switch-case and rely on built-in function instead.
It must be pure PHP.
Any help?
I'm not sure if you want to get pattern from ICU itself, or just format your DateTime. Here you have both solutions.
Basics:
You're doing well with you actual code for retrieving month name. IntlDateFormatter is pretty customizable, and it all depend on options you gave it.
Getting date with year, month and day depending on locale:
$locale = "en_GB";
$dateType = IntlDateFormatter::SHORT;//type of date formatting
$timeType = IntlDateFormatter::NONE;//type of time formatting setting to none, will give you date itself
$formatter =new IntlDateFormatter($locale, $dateType, $timeType);
$dateTime = new DateTime("2015-02-28");
echo $formatter->format($dateTime);
will give you
28/02/2015
You can play with other options of IntlDateFormatter. There's SHORT, MEDIUM, LONG, FULL and NONE - you can read about them and check example ouput here.
Getting pattern from formatter
$formatter->->getPattern();
will give you something like
dd/MM/yyyy
I hope this will help you. I've been struggling with it for a long time to do it properly :)

IntlDateFormatter return a wrong pattern

I am using IntlDateFormatter/i18n for the first time. When I want to format a date in french, I get the right format (dd/MM/yyyy) but when I am looking for an italian format (for exemple) I get a wrong format (dd/MM/yy) instead of the same as the french one.
Is that a bug of IntlDateFormatter? Do I do something wrong?
Here is a code sample :
<?php
$fmt = new IntlDateFormatter('fr_FR', IntlDateFormatter::SHORT, IntlDateFormatter::NONE);
echo $fmt->getPattern(); // return dd/MM/yyyy
$fmt = new IntlDateFormatter('it_IT', IntlDateFormatter::SHORT, IntlDateFormatter::NONE);
echo $fmt->getPattern(); // return dd/MM/yy instead of dd/MM/yyyy
Here are where I picked up my information, for the date format in France and Italy and for the IntlDateFormatter patterns
for me with php5.5 and lib-icu 4.2.1 both nations give me dd/MM/yy, so if you want a different pattern I suggest to create an array upstream and go with your calling setPattern.
I guess the problem comes from the version of libicu in your system but I'm not 100% sure.

Categories