yakoffka.ru
    грабли, костыли и велосипеды php, css, html, js и прочего

    Локализация функции date

    Локализация функции date
    			«Ходы кривые роет подземный умный крот. Нормальные герои всегда идут в обход.
    			В обход идти понятно не очень-то легко. Не очень то приятно и очень далеко.
    			Зато так поступают одни лишь мудрецы, Зато так наступают одни лишь храбрецы.
    			И мы с пути кривого обратно не свернем. А надо будет снова пойдем другим путем!»
    		

    песня из к/ф "Айболит-66"

    01
    Введение

    «Информация о локали модифицируется во всем процессе, а не по каждому потоку отдельно. Если вы используете PHP на многопоточном сервере, таком как IIS, HHVM или Apache под Windows, вы можете обнаружить неожиданные изменения в настройках локали во время выполнения скриптов, никогда и не вызывавших setlocale(). Это происходит из-за того, что другие скрипты, запущенные в параллельных потоках данного процесса, в то же самое время поменяли настройки локали для всего процесса с помощью setlocale()» setlocale

    Для того, чтобы внезапно не обнаружить неожиданные изменения в работе скриптов, и показать себя настоящим героем, завсегда идущим в обход, я не стал менять настройки локали, а в очередной раз сел за изобретение велосипеда.

    02
    Определение управляющих символов

    Определим управляющие символы в шаблоне, заданном строкой $format для переопределения их значений в результирующей строке ("l", "F", "D", "M", "S", "a", "A",) и запишем их в массив $arr_simbols:

    Code:
    1
      if(true){} $arr_simbols=array("l""F""D""M""S""a""A",);

    Важно соблюсти правильную последовательность: сначала производим замену полных наименований, затем сокращений, затем суффиксов.

    03
    Определяем замены

    Для каждого управляющего символа (кроме 'S') запишем массив замен значений, выдаваемых функцией data():

    Code:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
      $arr_l=array(// массив замен полного наименования дня недели
        
    "Sunday"=>"Воскресенье",
        
    "Monday"=>"Понедельник",
        
    "Tuesday"=>"Вторник",
        
    "Wednesday"=>"Среда",
        
    "Thursday"=>"Четверг",
        
    "Friday"=>"Пятница",
        
    "Saturday"=>"Суббота",
      );

      
    $arr_F=array(// массив замен полного наименования месяца
        
    "January"=>"января",
        
    "February"=>"февраля",
        
    "March"=>"марта",
        
    "April"=>"апреля",
        
    "May"=>"мая",
        
    "June"=>"июня",
        
    "July"=>"июля",
        
    "August"=>"августа",
        
    "September"=>"сентября",
        
    "October"=>"октября",
        
    "November"=>"ноября",
        
    "December"=>"декабря",
      );

      
    $arr_D=array(// массив замен cокращенного представления дня недели, 3 символа
        
    "Sun"=>"Вс",
        
    "Mon"=>"Пн",
        
    "Tue"=>"Вт",
        
    "Wed"=>"Ср",
        
    "Thu"=>"Чт",
        
    "Fri"=>"Пт",
        
    "Sat"=>"Сб",
      );

      
    $arr_M=array(// массив замен cокращенного наименования месяца, 3 символа
        
    "Jan"=>"янв",
        
    "Feb"=>"фев",
        
    "Mar"=>"мар",
        
    "Apr"=>"апр",
        
    "May"=>"май",
        
    "Jun"=>"июн",
        
    "Jul"=>"июл",
        
    "Aug"=>"авг",
        
    "Sep"=>"сен",
        
    "Oct"=>"окт",
        
    "Nov"=>"ноя",
        
    "Dec"=>"дек",
      );
      
    $arr_a=array(// до/после полудня
        
    "am"=>"до полудня",
        
    "pm"=>"после полудня",
      );

      
    $arr_A=array(// ДО/ПОСЛЕ ПОЛУДНЯ
        
    "AM"=>"ДО ПОЛУДНЯ",
        
    "PM"=>"ПОСЛЕ ПОЛУДНЯ",
      );

    04
    Получение оригинальной строки

    Получаем системную дату/время с помощью оригинальной функции:

    Code:
    1
      $res=date($format,$timestamp);

    05
    Замены подстрок

    Для начала следует определить наличие управляющих символов в строке $format, учитывая возможность встречи закомментированных.

    Производим необходимые замены, уменьшая значение переменной $c на количество произведенных замен.

    Code:
    1
    2
    3
    4
    5
      foreach(${"arr_".$s} as $search=>$replace){
        
    $res=str_replace($search,$replace,$res,$count);
        
    $c=$c-$count;
      }unset(
    $search,$replace);
      

    Выполняем проверку: при корректной работе скрипта значение $c должно быть равным нулю.

    Code:
    1
    2
    3
    4
    5
    6
    7

      
    if($c===0){
        unset(
    $c);
      }else{
        
    $res=$res."\n<p class='error'>error#".__line__." in ".__function__.": \$s='$s'; \$c='$c'.</p>\n";
      }unset(
    $s,$c);
      

    06
    Замена суффиксов

    Здесь дело обстоит немногим сложнее: массив замен значений будет состоять из двух подмассивов - для суффиксов и окончаний.

    Code:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
      $arr_S=array(
        
    // массив суффиксов в порядке убывания частоты употребления 2 символа
        
    array("th""st""rd""nd",),
        
    // массив окончаний числительных в диапазоне от 1 до 31
        
    array(
          
    "01"=>"-ое",
          
    "02"=>"-ое",
          
    "03"=>"-ье",
          
    "04"=>"-ое",
          
    "05"=>"-ое",
          
    "06"=>"-ое",
          
    "07"=>"-ое",
          
    "08"=>"-ое",
          
    "09"=>"-ое",
          
    "10"=>"-ое",
          
    "11"=>"-ое",
          
    "12"=>"-ое",
          
    "13"=>"-ое",
          
    "14"=>"-ое",
          
    "15"=>"-ое",
          
    "16"=>"-ое",
          
    "17"=>"-ое",
          
    "18"=>"-ое",
          
    "19"=>"-ое",
          
    "20"=>"-ое",
          
    "21"=>"-ое",
          
    "22"=>"-ое",
          
    "23"=>"-ье",
          
    "24"=>"-ое",
          
    "25"=>"-ое",
          
    "26"=>"-ое",
          
    "27"=>"-ое",
          
    "28"=>"-ое",
          
    "29"=>"-ое",
          
    "30"=>"-ое",
          
    "31"=>"-ое",
        ),
      );

    Я не особо силён в правиле буквенных наращиваний после цифр, так что здесь до проверки правильности пока оставлю так.

    Так как связь между английскими суффиксами и русскими окончаниями числительных косвенная, соответствие будем проводить через число месяца:

    Code:
    1
    2
    3
    4
    5
    6
      foreach(${"arr_".$s}[0] as $search){
        
    $replace=${"arr_".$s}[1][date('d',$timestamp)];
        
    $res=str_replace($search,$replace,$res,$count);
        
    $c=$c-$count;
      }unset(
    $search,$replace);
      

    07
    Добавление циклов

    Собственно теперь собираем всё вместе, добавив циклы и условия, и радуемся.

    Финальная версия скрипта:

    Code:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
      // 2018_09_06 вывод системной даты/времени на русском языке (кроме 'r' Дата в формате RFC 2822)
      //==================================================================================================
      
    function date_ru($format,$timestamp=FALSE){

        
    // 2 определим символы в шаблоне результирующей строки, значение которых необходимо переопределить
        // порядок ВАЖЕН!!! сначала полные названия, затем сокращённые!
        
    $arr_simbols=array("l""F""D""M""S""a""A",);

        
    // 3 определяем замены для каждого символа
        
    $arr_l=array(// Полное наименование дня недели
          
    "Sunday"=>"Воскресенье""Monday"=>"Понедельник""Tuesday"=>"Вторник""Wednesday"=>"Среда",
          
    "Thursday"=>"Четверг""Friday"=>"Пятница""Saturday"=>"Суббота",
        );
        
    $arr_F=array(// Полное наименование месяца
          
    "January"=>"января""February"=>"февраля""March"=>"марта""April"=>"апреля""May"=>"мая",
          
    "June"=>"июня""July"=>"июля""August"=>"августа""September"=>"сентября""October"=>"октября",
          
    "November"=>"ноября""December"=>"декабря",
        );
        
    $arr_D=array(// Сокращенное представление дня недели, 3 символа
          
    "Sun"=>"Вс""Mon"=>"Пн""Tue"=>"Вт""Wed"=>"Ср""Thu"=>"Чт""Fri"=>"Пт""Sat"=>"Сб",
        );
        
    $arr_M=array(// Сокращенное наименование месяца, 3 символа
          
    "Jan"=>"янв""Feb"=>"фев""Mar"=>"мар""Apr"=>"апр""May"=>"май""Jun"=>"июн""Jul"=>"июл",
          
    "Aug"=>"авг""Sep"=>"сен""Oct"=>"окт""Nov"=>"ноя""Dec"=>"дек",
        );
        
    $arr_S=array(
          
    // массив суффиксов в порядке убывания частоты употребления, 2 символа
          
    array("th""st""rd""nd",),
          
    // массив окончаний числительных в диапазоне от 1 до 31666
          
    array(
            
    "01"=>"-ое""02"=>"-ое""03"=>"-ье""04"=>"-ое""05"=>"-ое""06"=>"-ое""07"=>"-ое",
            
    "08"=>"-ое""09"=>"-ое""10"=>"-ое""11"=>"-ое""12"=>"-ое""13"=>"-ое""14"=>"-ое",
            
    "15"=>"-ое""16"=>"-ое""17"=>"-ое""18"=>"-ое""19"=>"-ое""20"=>"-ое""21"=>"-ое",
            
    "22"=>"-ое""23"=>"-ье""24"=>"-ое""25"=>"-ое""26"=>"-ое""27"=>"-ое""28"=>"-ое",
            
    "29"=>"-ое""30"=>"-ое""31"=>"-ое",
          ),
        );
        
    $arr_a=array(// до/после полудня
          
    "am"=>"до полудня""pm"=>"после полудня",
        );
        
    $arr_A=array(// ДО/ПОСЛЕ ПОЛУДНЯ
          
    "AM"=>"ДО ПОЛУДНЯ""PM"=>"ПОСЛЕ ПОЛУДНЯ",
        );

        
    // 4 получаем системную дату/время на англицком:
        
    $res=date($format,$timestamp);

        
    // 5 смотрим содержимое переменной $format на предмет наличия 'D l j S F M'
        
    foreach($arr_simbols as $s){
          
    //количество вхождений незаэкранированных искомых символов
          
    $c=substr_count($format,$s)-substr_count($format,"\".$s);
          if(
    $c>0){// незаэкранированный искомый символ найден!

            // 6/1 производим замену, если символ - не суффикс (с ним у нас будет разговор попозже)
            if(
    $s!=="S"){
              foreach(
    ${"arr_".$s} as $search=>$replace){
                
    $res=str_replace($search,$replace,$res,$count);
                
    $c=$c-$count;
              }unset(
    $search,$replace);

            // 6/2 производим замену для суффикса
            }else{
              foreach(
    ${"arr_".$s}[0] as $search){
                
    $replace=${"arr_".$s}[1][date('d',$timestamp)];
                
    $res=str_replace($search,$replace,$res,$count);
                
    $c=$c-$count;
              }unset(
    $search,$replace);
            }

            // 7 проверка количества произведенных замен
            if(
    $c!==0){
              
    $res=$res."\n<class='error'>error#".__line__." in ".__function__.": \$s='$s'; \$c='$c'.</p>\n";
            
    }
            unset(
    $s,$c);
          }
        }

        
    // выводим результат
        
    return $res;
      }
      

    08
    Проверка работы функции

    Заключительный этап - проверка правильности работы функции.

    Code:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
      $arr_test=array(
        array(
    "полного наименования дня недели""l",(60*60*24),7),
        array(
    "сокращенного наименования дня недели""D",(60*60*24),7),
        array(
    "полного наименования месяца""F",(60*60*24*31),12),
        array(
    "сокращенного наименования месяца""M",(60*60*24*31),12),
        array(
    "Ante/Post Meridiem""A",(60*60*12),2),
        array(
    "ante/post meridiem""a",(60*60*12),2),
        array(
    "суффикса/окончания""S",(60*60*24),31),
        array(
    "неверного параметра \$timestamp='string'""S""string",1),
        array(
    "неверного параметра: дата выше 19 января 2038""S",(60*60*24*31*200),10),
        array(
    "неверного параметра: дата ниже 13 декабря 1901""S",(-60*60*24*31*200),10),
        array(
    "полноформатной даты""D, jS F Y, h:i a",(60*60*36),7),
      );

      foreach(
    $arr_test as $arr_test){
        
    $timestamp=1536322980;// Пт, 7-ое сентября 2018, 03:23 после полудня
        
    if($arr_test[1]=="S"){$b="j";}else{$b="";}// дописываем день месяца для проверки окончаний
        
    echo "\t<table class='blue_table'>\n<caption>проверка преобразования ".$arr_test[0]."</caption>";
        echo 
    "\t\t\t<tr><th>№</th><th>timestamp</th><th>date('Y.m.d')</th><th>date('$b".$arr_test[1]."')</th><th>date_ru('$b".$arr_test[1]."')</th></tr>\n";
        for(
    $i=0;$i<$arr_test[3];++$i){
          echo 
    "\t\t\t<tr><td>".($i+1)."</td><td>$timestamp</td><td>".date('Y.m.d',$timestamp)."</td><td>".date($b.$arr_test[1],$timestamp)."$ae</td><td>".date_ru($b.$arr_test[1],$timestamp).$ar</td></tr>\n";
          
    $timestamp=$timestamp+$arr_test[2];
        }
        echo 
    "\t\t</table>\n";
      }unset(
    $arr_test);



      
    проверка преобразования полного наименования дня недели
    timestampdate('Y.m.d')date('l')date_ru('l')
    115363229802018.09.07FridayПятница
    215364093802018.09.08SaturdayСуббота
    315364957802018.09.09SundayВоскресенье
    415365821802018.09.10MondayПонедельник
    515366685802018.09.11TuesdayВторник
    615367549802018.09.12WednesdayСреда
    715368413802018.09.13ThursdayЧетверг
    проверка преобразования сокращенного наименования дня недели
    timestampdate('Y.m.d')date('D')date_ru('D')
    115363229802018.09.07FriПт
    215364093802018.09.08SatСб
    315364957802018.09.09SunВс
    415365821802018.09.10MonПн
    515366685802018.09.11TueВт
    615367549802018.09.12WedСр
    715368413802018.09.13ThuЧт
    проверка преобразования полного наименования месяца
    timestampdate('Y.m.d')date('F')date_ru('F')
    115363229802018.09.07Septemberсентября
    215390013802018.10.08Octoberоктября
    315416797802018.11.08Novemberноября
    415443581802018.12.09Decemberдекабря
    515470365802019.01.09Januaryянваря
    615497149802019.02.09Februaryфевраля
    715523933802019.03.12Marchмарта
    815550717802019.04.12Aprilапреля
    915577501802019.05.13Mayмая
    1015604285802019.06.13Juneиюня
    1115631069802019.07.14Julyиюля
    1215657853802019.08.14Augustавгуста
    проверка преобразования сокращенного наименования месяца
    timestampdate('Y.m.d')date('M')date_ru('M')
    115363229802018.09.07Sepсен
    215390013802018.10.08Octокт
    315416797802018.11.08Novноя
    415443581802018.12.09Decдек
    515470365802019.01.09Janянв
    615497149802019.02.09Febфев
    715523933802019.03.12Marмар
    815550717802019.04.12Aprапр
    915577501802019.05.13Mayмай
    1015604285802019.06.13Junиюн
    1115631069802019.07.14Julиюл
    1215657853802019.08.14Augавг
    проверка преобразования Ante/Post Meridiem
    timestampdate('Y.m.d')date('A')date_ru('A')
    115363229802018.09.07PMПОСЛЕ ПОЛУДНЯ
    215363661802018.09.08AMДО ПОЛУДНЯ
    проверка преобразования ante/post meridiem
    timestampdate('Y.m.d')date('a')date_ru('a')
    115363229802018.09.07pmпосле полудня
    215363661802018.09.08amдо полудня
    проверка преобразования суффикса/окончания
    timestampdate('Y.m.d')date('jS')date_ru('jS')
    115363229802018.09.077th7-ое
    215364093802018.09.088th8-ое
    315364957802018.09.099th9-ое
    415365821802018.09.1010th10-ое
    515366685802018.09.1111th11-ое
    615367549802018.09.1212th12-ое
    715368413802018.09.1313th13-ое
    815369277802018.09.1414th14-ое
    915370141802018.09.1515th15-ое
    1015371005802018.09.1616th16-ое
    1115371869802018.09.1717th17-ое
    1215372733802018.09.1818th18-ое
    1315373597802018.09.1919th19-ое
    1415374461802018.09.2020th20-ое
    1515375325802018.09.2121st21-ое
    1615376189802018.09.2222nd22-ое
    1715377053802018.09.2323rd23-ье
    1815377917802018.09.2424th24-ое
    1915378781802018.09.2525th25-ое
    2015379645802018.09.2626th26-ое
    2115380509802018.09.2727th27-ое
    2215381373802018.09.2828th28-ое
    2315382237802018.09.2929th29-ое
    2415383101802018.09.3030th30-ое
    2515383965802018.10.011st1-ое
    2615384829802018.10.022nd2-ое
    2715385693802018.10.033rd3-ье
    2815386557802018.10.044th4-ое
    2915387421802018.10.055th5-ое
    3015388285802018.10.066th6-ое
    3115389149802018.10.077th7-ое
    проверка преобразования неверного параметра: дата позже 19 января 2038
    timestampdate('Y.m.d')date('jS')date_ru('jS')
    115363229802018.09.077th7-ое
    220720029802035.08.2929th29-ое
    326076829802052.08.1919th19-ое
    431433629802069.08.1010th10-ое
    536790429802086.08.011st1-ое
    642147229802103.07.2424th24-ое
    747504029802120.07.1414th14-ое
    852860829802137.07.055th5-ое
    958217629802154.06.2626th26-ое
    1063574429802171.06.1717th17-ое
    проверка преобразования неверного параметра: дата раньше 13 декабря 1901
    timestampdate('Y.m.d')date('jS')date_ru('jS')
    115363229802018.09.077th7-ое
    210006429802001.09.1616th16-ое
    34649629801984.09.2525th25-ое
    4-707170201967.10.055th5-ое
    5-6063970201950.10.1414th14-ое
    6-11420770201933.10.2323rd23-ье
    7-16777570201916.11.011st1-ое
    8-22134370201899.11.1010th10-ое
    9-27491170201882.11.1919th19-ое
    10-32847970201865.11.2828th28-ое
    проверка преобразования полноформатной даты
    timestampdate('Y.m.d')date('D, jS F Y, h:i a')date_ru('D, jS F Y, h:i a')
    115363229802018.09.07Fri, 7th September 2018, 03:23 pmПт, 7-ое сентября 2018, 03:23 после полудня
    215364525802018.09.09Sun, 9th September 2018, 03:23 amВс, 9-ое сентября 2018, 03:23 до полудня
    315365821802018.09.10Mon, 10th September 2018, 03:23 pmПн, 10-ое сентября 2018, 03:23 после полудня
    415367117802018.09.12Wed, 12th September 2018, 03:23 amСр, 12-ое сентября 2018, 03:23 до полудня
    515368413802018.09.13Thu, 13th September 2018, 03:23 pmЧт, 13-ое сентября 2018, 03:23 после полудня
    615369709802018.09.15Sat, 15th September 2018, 03:23 amСб, 15-ое сентября 2018, 03:23 до полудня
    715371005802018.09.16Sun, 16th September 2018, 03:23 pmВс, 16-ое сентября 2018, 03:23 после полудня

    09
    Заключение

    Получена функция, локализирующая результаты вывода встроенной функции date(). Возвращает отформатированную строку с датой на русском языке. Капустин Яков (Пятница, 7-ое сентября 2018, 15:23)