Проблема модификации даты через strtotime

    Добрый день хабравчане.

    Хочу поведать вам о небольшой проблеме с вычислением даты функцией strtotime.

    Суть в следующем. Мне нужно было получить название предыдущего месяца. Ничего сложного в этой задаче нет:
    $t = strtotime('-1 month');
    echo strftime('%B', $t);

    В большинстве случаев этот вариант отлично подходит. Но не сегодня. А ведь ещё даже не пятница =). Т.к. сегодня 31 мая, то вышеуказанный код вернул 1 мая. А +1 month вернул вообще 1 июля. Странно. В такие моменты каждый второй PHP-программист начинает изобретать следующий велосипед:
    $months = array(1=>'Январь','Февраль','Март'...);
    echo $months[date('m')-1];


    Но можно сделать намного проще:
    $t = strtotime('first day of previous month');
    echo strftime('%B', $t);

    Этот код как раз вернет 1 апреля. Подробнее о формате первого аргумента функции strtotime.

    Красивого кода и удачи.
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 60

      –8
      Ты не рассказал о том как получить русское название месяца, ну там про локаль и все такое.
        +5
        Это немного не по теме поста, но ничего сложного тут нет:
        setlocale(LC_ALL, "ru_RU.UTF-8");
          –1
          Ага, на сервере под Виндами 8-) Внезапно, не всё так просто.
            0
            Да ну эти Ваши извращения.
          –8
          Такие вещи разработчик обязан знать.
            –4
            Спасибо, ваше мнение важно для нас!
              –7
              А зачем вы здесь? Только рассказывать своё?
                +7
                Так стоп.
                Автор написал:
                >> Мне нужно было получить название предыдущего месяца.
                >> $months = array(1=>'Январь','Февраль','Март'...);

                А в альтернативном предложенном варианте, не рассказал про локаль и я просто ему об этом напомнил.
                Это не значит, что я не считаю это знание тайным или не всем доступным.

                Тут появился комментатор с неуместным на мой взгляд комментарием (в свете описанного выше).
                Я немного сыронизировал на эту тему, сорри, признаюсь немного раздражаюсь когда в мой с автором тред кто-то влезает с замечанием не по теме.

                Прошу прощения за недопонимание в треде и доставленные неудобства, так же приношу извинения всем, кому моя ирония показалась обидной.
                  0
                  Неуместные мнения, на мой взгляд, лучше игнорировать.
                  Как говорится, не трогай, вонять не будет.
          –5
          Боюсь, вас сейчас заминусуют. Поделиться одной маленькой проблемой и одним решением — это маловато.
          Совет: добавьте больше способов решения этой проблемы, например, через класс DateTime. Опишите плюсы и минусы каждого способа.
            0
            DateTime это тоже самое. Функции есть обертка над DateTime.
              0
              И я описал самое просто и быстрое решение.
                +3
                А помойму все ок. По делу, без лишней воды, всяких там сравнений очевидного бреда с нормальными решениями и прочей ереси
                  +7
                  Лол, совет как писать пост от юзера у которого 0 постов.
                  +5
                  Лично я пользуюсь подобной конструкцией:
                  date('F', mktime(0, 0, 0, date('n') - 1, 1));
                    0
                    Имеет право быть. Но на мой взглдят не так очевидно что будет в итоге.
                    Согласитесь, что «first day of previous month» на первый взгляд понятнее чем «0, 0, 0, date('n') — 1, 1».
                      0
                      Мне тоже больше нравится «first day of previous month»
                        +3
                        Я не собираюсь разводить халивар — кому что нравится, PQR просил добавить больше способов решения этой проблемы, вот я и написал как решаю данную проблему я.
                      0
                      Чтобы «обнулить» время можно так first day of previous month midnight
                        +1
                        31.05.2012 +1 month вполне верно выдало 01.07.2012, так как 31.06.2012 не существует.
                          +4
                          18 лет назад я получил первый штраф от полцаев — в протоколе стояло 31 июня. Указал им на ошибку, исправили на 30.06. А показав возможность расчётов количества дней в месяца по костяшкам пальцев — штраф был уменьшен в пару раз.
                            0
                            А показав возможность расчётов количества дней в месяца по костяшкам пальцев — штраф был уменьшен в пару раз.


                            Какой интеллектуальный штраф.
                          0
                          Если подумать, то правильно она работает. Она думает что 31 мая минус 1 месяц возвращает 31 апреля, которого нету и он понимается как 1ое мая.

                          Кстати например вот ORACLE 31 мая минус 1 месяц вернул 30 апреля.
                            0
                            Я не говорю, что функция работает не правильно. Просто при конструкции "-1 month" я ожидаю, что функция будет вычитать месяцы а не дни.
                              0
                              31.05 вычитаем месяц получаем 31.04 которое равно 1.05 — такая логика в этой функции видимо.
                                +3
                                Она вычитает именно месяц, а не дни. Она берет дату, отнимает от текущего месяца (а он 5) 1 и сохраняет ее такой же, а дни _совершенно_ не трогает, как вы и ожидали. и получается 2012-04-31. Или вы не ожидали что пхп нормализует неправильные даты?
                                –3
                                Вряд ли. Скорее, у них месяц равен 30 дням.
                                  +4
                                  Нет, человек прав, и месяц равен не 30 дням, иначе февраль бы вообще все вычисления портил. А вы вместо того чтоб подумать немного говорите глупости.
                                  Думаю JS вы понимаете и алгоритм функции прост:
                                  d = new Date()
                                  d.setMonth( d.getMonth() -1 )
                                  

                                  В итоге получается 31 апреля, как и сказал ohifck, но такого числа нет, поэтому эта дата нормализуется в 1 мая.
                                    0
                                    На похапе тож самое — вот с февралем (29 дней в этом году было)
                                    php > $dt = new DateTime('2012-03-31');
                                    php > $dt->modify('-1 month');
                                    php > var_dump($dt);
                                    object(DateTime)#2 (3) {
                                      ["date"]=>
                                      string(19) "2012-03-02 00:00:00"
                                      ["timezone_type"]=>
                                      int(3)
                                      ["timezone"]=>
                                      string(13) "Europe/Moscow"
                                    }
                                    
                                      0
                                      Дык это я человеку обьяснил как работает алгоритм этой функции =) Он просто похоже питоном занимается, и я выбрал JS как возможно общий язык. Так во многих языках, нормализация идет чтоб не вызывать ошибки неправильной даты, опять же этим удобно пользоваться:
                                      $dt = new DateTime("$tear-$month-00");
                                      var_dump($dt->format('Y-m-d'));
                                      и вуаля, у нас всегда будет последнее число предидущего месяца! дальше работаем с этми месяцем.
                                      Просто ошибка то на столько наивная =) Или я с JS переработал, там не так много методов для работы с датой.
                                +1
                                А так реально я представляю в скольких местах у меня код сегодня повалится и сколько плавающих багов объясняется этим. Спасибо за статью — раньше не обращал на это внимания.
                                  0
                                  =) я из JS взял в привычку: если работаешь с датой, работай только с первым числом, оно есть у всех. Там такая ошибка — нормальное состояние.
                                  0
                                  Вот из-за таких как вы, PHP-шников называют быдлокодерами. И я вас огорчу.

                                  «В такие моменты каждый PHP-программист начинает изобретать следующий велосипед:»
                                  Нет, в такие моменты php-программист начинает искать причину проблемы, а не изобретать велосипед. А судя из последующей фразы:
                                  «Но мне надоели эти костыльные решения»
                                  Вы именно к этой категории и относитесь, которые строят велосипеды даже не разобравшить в проблеме.
                                  Радует что вам это хоть надоело =)
                                    0
                                    Если бы все проекты оплачивались (ну или хотя бы мотивировались) достаточно, что бы разбираться в таких мелки проблемах, быдлокодеров было бы гораздо меньше.
                                    Если бы да кабы…
                                      +2
                                      Да, на хабре в рабочее время мы можем сидеть, а вот разобраться в проблеме, это мало денег платят и плохо мотивируют =)
                                      Или вас еще нужно мотивировать чтоб вы опыта набирались? Так и не будут платить пока не наберетесь, а набираться вы не хотите потому что недоплачивают… прям замкнутый круг какой-то.
                                        +1
                                        Вы утрируете.
                                          +2
                                          ua2.php.net/manual/ru/datetime.formats.relative.php
                                          «Замечание:

                                          Относительные значения в месяцах рассчитываются исходя из их продолжительности. Например из „+2 month 2011-11-30“ получится „2012-01-30“, т.к. ноябрь состоит из 30 дней, а декабрь из 31 дня, что в сумме дает 61 день. Это связано с ноября составляет 30 дней в длину, и в декабре является 31 дней в длине, производя в общей сложности 61 дней.»

                                          Даже по русски вам написали в мануале, и переводить не нужно.

                                          Да, я утрирую, просто имел ввиду что мануалы нужно читать, там больше инфы чем на хабре, особенно учитывая что это ваш хлеб
                                            0
                                            С чего Вы взяли что я на этом зарабатываю?
                                  • UFO just landed and posted this here
                                    • UFO just landed and posted this here
                                        0
                                        Демонстрация профессионального копипаста?
                                        • UFO just landed and posted this here
                                            0
                                            Почему же Вы не ведете мастер классы для быдлокодеров?
                                            Мне Вас очень не хватает.
                                            • UFO just landed and posted this here
                                                +2
                                                Институт закончил Веб программирования =)
                                                Пока Вы будете таким занятым, и не начнете просвещать темноту, 95% php-программистов, так и останутся быдлокодерами с массивами месяцев. Я потратил пол часа своего времени, что бы сообщить людям, которым лень читать мануалы, а более интересно читать о суде над Ассанжем, что есть такая вот возможность. В реальных проектах я очень редко встречаю использование strtotime и уж тем более strftime.
                                          0
                                          Человеку нужно было название предидущего месяца
                                          • UFO just landed and posted this here
                                              –2
                                              сарказм неуместен, на самом деле минимум 50% пхп-шников о проблеме "+1 месяц" не то что не знают, а не подозревают даже, возможно и вы тоже входите в те 50% незнающих, раз такая реакция пошла.
                                              • UFO just landed and posted this here
                                                  0
                                                  Я не в курсе, что есть такая проблема "+1 месяц".
                                                  Про проблемы масштабирования, репликации, дедлоков и всякого геммороя с сессиями при использовании кривых балансировщиков слышал. Про +1 месяц, нет.
                                            • UFO just landed and posted this here
                                                +3
                                                Я уже готовлю 10 постов. Начну с printf.
                                            0
                                            Немного не понял. Разве задача не стояла «узнать какое число было месяц назад»? Если так — то задача решается именно '-1 month', ведь для непоследнего дня месяца 'last day of month' вернет не тоже самое что минус месяц. Поясните?
                                              0
                                              Суть в следующем. Мне нужно было получить название предыдущего месяца.
                                                0
                                                Понял.
                                              +1
                                                0
                                                $t = strtotime(date('Y-m-01').' -1 month')
                                                Выдаёт такой же резульат, но без хитрых фразочек на английском ;)
                                                  0
                                                  Я, может, чего-то не понимаю, но «first day of previous month» это совсем не то же что "-1 month" во всех случаях, кроме первого числа месяца.
                                                    0
                                                    Может лучше ссылочку вот эту в конце поста поставить?
                                                    ru.php.net/manual/en/datetime.formats.relative.php
                                                    или
                                                    ru.php.net/manual/ru/datetime.formats.relative.php
                                                      0
                                                      Спасибо, добавил.

                                                    Only users with full accounts can post comments. Log in, please.