Манипулирането на дата/час със сигурност е болка. :)
Предложено решение
1. Локални данни
Току-що посетих SVN хранилището на Yii и безсрамно копирах тези:
$locales = array(
'pt' => array(
'monthNames' => array(
'wide' => array (
1 => 'janeiro',
2 => 'fevereiro',
3 => 'marco',
4 => 'abril',
5 => 'maio',
6 => 'junho',
7 => 'julho',
8 => 'agosto',
9 => 'setembro',
10 => 'outubro',
11 => 'novembro',
12 => 'dezembro',
),
'abbreviated' => array(
1 => 'jan',
2 => 'fev',
3 => 'mar',
4 => 'abr',
5 => 'mai',
6 => 'jun',
7 => 'jul',
8 => 'ago',
9 => 'set',
10 => 'out',
11 => 'nov',
12 => 'dez',
),
),
'weekDayNames' => array(
'wide' => array (
0 => 'domingo',
1 => 'segunda-feira',
2 => 'terca-feira',
3 => 'quarta-feira',
4 => 'quinta-feira',
5 => 'sexta-feira',
6 => 'sabado',
),
'abbreviated' => array(
0 => 'dom',
1 => 'seg',
2 => 'ter',
3 => 'qua',
4 => 'qui',
5 => 'sex',
6 => 'sab',
),
),
),
'en' => array(
'monthNames' => array(
'wide' => array (
1 => 'January',
2 => 'February',
3 => 'March',
4 => 'April',
5 => 'May',
6 => 'June',
7 => 'July',
8 => 'August',
9 => 'September',
10 => 'October',
11 => 'November',
12 => 'December',
),
'abbreviated' => array(
1 => 'Jan',
2 => 'Feb',
3 => 'Mar',
4 => 'Apr',
5 => 'May',
6 => 'Jun',
7 => 'Jul',
8 => 'Aug',
9 => 'Sep',
10 => 'Oct',
11 => 'Nov',
12 => 'Dec',
),
),
'weekDayNames' => array(
'wide' => array (
0 => 'Sunday',
1 => 'Monday',
2 => 'Tuesday',
3 => 'Wednesday',
4 => 'Thursday',
5 => 'Friday',
6 => 'Saturday',
),
'abbreviated' => array(
0 => 'Sun',
1 => 'Mon',
2 => 'Tue',
3 => 'Wed',
4 => 'Thu',
5 => 'Fri',
6 => 'Sat',
),
),
),
);
2. Грубо налагане на проблема
Ако приемем, че приложението ви не прекарва цялото си време в конвертиране на дати, които могат да се четат от човека, скоростта не би трябвало да има значение. Затова избрах кратко решение с добра разширяемост, с цената на това да не се опитвам да оптимизирам и да съм малко загадъчен.
function strtotimeIntl($timeString, $locales, $normalizeCallback = 'strtolower') {
// STEP 1 -- TRY ENGLISH
$ts = strtotime($timeString);
if ($ts !== false) {
return $ts;
}
// STEP 2 -- BRUTE FORCE
$english = $locales['en'];
foreach($locales as $code => $localeInfo) {
if($code == 'en') {
continue; // don't try english again
}
$subject = $normalizeCallback($timeString); // reset
// These reflect the structure of $localeInfo
$replacementKeys = array(
array('monthNames', 'wide'),
array('monthNames', 'abbreviated'),
array('weekDayNames', 'wide'),
array('weekDayNames', 'abbreviated'),
);
// Replace everything present in the string with english equivalents
foreach($replacementKeys as $keys) {
$map = array_map($normalizeCallback, $localeInfo[$keys[0]][$keys[1]]);
$flipped = array_flip($map);
$subject = preg_replace('/\b('.implode('|', $map).')\b/e',
'$english[$keys[0]][$keys[1]][$flipped[\'$1\']]',
$subject);
}
// Does this look right?
$ts = strtotime($subject);
if ($ts !== false) {
return $ts;
}
}
// Give up, it's not like we didn't try
return false;
}
Това вътрешно foreach
наистина изглежда миризливо, но мисля, че е приемливо. Това, което прави, е да се опита да замени всеки подниз, който изглежда като един от елементите в подмасива на $localeInfo
(текущият локал, който се тества), идентифициран от индексите $keys[0]
и $keys[1]
. За да направи заместването възможно най-кратко, той използва спомагателен масив $flipped
и preg_replace
в режим на оценка; ако не харесвате този вид код, той със сигурност може да бъде заменен с по-познат подход, базиран на цикъл.
3. Как да го използвама
$timeString = '22 Feb 2011';
echo strtotimeIntl($timeString, $locales);
$timeString = '22 Fev 2011';
echo strtotimeIntl($timeString, $locales);
4. Какво става с третия аргумент?
Е, би било хубаво, ако замяната работи по начин, който не е чувствителен към главни и малки букви. Проблемът с това е, че не можете сляпо да използвате strtolower
и модификатора на regex /i
, защото поне първият няма да работи, освен ако не промените локала LC_TEXT
, което е болезнено изискване и не е надеждно за зареждане (имената на локалите зависят от операционната система ). И аргументът с пушка е, че дори и всичко да върви добре дотук, все още трябва да запазите вашите данни за локал в ANSI кодиране (което означава, че не можете да ги запазите всички в един и същ файл).
Следователно повикващият има възможност да предостави своя собствена функция за нормализиране, ако е необходимо; mb_strtolower
би бил отличен избор тук, ако данните ви се записват в UTF-8.
5. Това изобщо работи ли?
Разбира се, че е така.
6. И няма никакви уговорки?
Е, освен функцията за нормализиране, има още една, за която се сещам: strtotime
вътрешно използва местната часова зона, за да преобразува анализираната дата в клеймо за време. Това означава, че дата в напр. Френският ще бъде анализиран правилно при съответните данни за локализация, но клеймото за време ще бъде произведено за местната часова зона, а не за CET/CEST (часовата зона, която Франция използва). В зависимост от вашите изисквания може също да искате да отчетете разликата в часовата зона.
person
Jon
schedule
23.09.2011
Имам следното наследяване на обект, нека започнем с корена (това е просто таблица с автоматично нарастващо цяло число):
OrganizationalUnit
е само име плюс наследен ID:Organization
е просто празен обект (за справка):Не на последно място:
Това кара компанията да има 3 колони/полета: ID, име и тип dnd.
Всяка компания се позовава от обект на документ в системата (всеки документ има компания собственик):
Обърнете внимание на
@ManyToOne(fetch = FetchType.LAZY, optional = false)
във връзката с компанията. Задължително е да нямате LAZY тук, използването на EAGER тип извличане тук ПОКРЕБЯВА проблема, за който говоря тук (но искам да го накарам да работи с LAZY, също и защото използвам генератор на код за повторно генериране на класовете обекти... и трябва да работи!)Позовавам се на
dndType
наCompany
за всеки документ в JSF страница pq-edit.xhtml като:Те просто се предават на подизглед
/subviews/repo-filename-dnd-panel.xhtml
, където просто препращам към параметрите, напр.documentCompanyDndSuffix
, за да създадете някакъв HTML.Това, което получавам за
#{doc.company.dndType}
сега е:Нямам представа защо получавам LazyInitializationException тук, като се има предвид, че обектът Company не подсказва LAZY на Hibernate 4 и предишното извикване на
#{doc.company.name}
работи добре.Актуализация:
Наскоро тествах това на GlassFish 3.1.2 и всичко е наред. Така че започвам да вярвам, че това е някакъв JBoss AS 7 или по-скоро JBoss AS проблем с управлението на транзакциите/несъответствие в политиката.
Моето предположение е, че името на компанията е било достъпно и непроксирано по време на някаква транзакция и след като мениджърът на обекти е бил затворен, се осъществява достъп до
dndType
, което все още не е отменено (докатоname
е)... изглежда ми като EJB транзакция проблем с боравене (където наистина все още имам дефицит)...Ще добавя повече информация тук скоро.
- person Ilia Choly   schedule 24.09.2011