company_banner

Некоторые программистские заблуждения о времени


    Наверняка вы уверены, что какие-то — а то и все — из этих утверждений истинны всегда и везде:

    • В сутках всегда 24 часа.
    • Неделя всегда начинается и заканчивается в том же месяце.
    • Неделя (или месяц) всегда начинается и заканчивается в том же году.
    • У времени нет начала и конца.
    • В месяце может быть 28, 29, 30 или 31 день.
    • Високосный год бывает раз в 4 года.
    • В каждом месяце везде всегда одно и то же количество дней.
    • На сервере и на клиенте всегда будет одно и то же время.
    • Можно легко вычислить количество часов и минут начиная с какого-то момента времени.
    • В каждой минуте 60 секунд.

    Хотя практика показывает, что полагаться на истинность этих заявлений при разработке ПО — заблуждение. Ну ладно, не все эти пункты относятся к заблуждениям. Некоторые утверждения были бы верны, если бы не досадные баги и пограничные случаи, которых за историю ПО накопилось несметное количество.

    В каждой минуте 60 секунд, а в сутках всегда 24 часа


    К примеру, в старых версиях KVM в CentOS был любопытный баг — минута могла длиться сколько угодно. Дело в том, что KVM не знала, что она исполняется не на физическом оборудовании, и если хостинговая ОС временно приостанавливала виртуальную машину, то виртуализированные часы продолжали идти с момента приостановки. Например, если машина вставала на паузу в 13:00 и активировалась в 15:00, системные часы в ней продолжали показывать 13:00. В результате, после каждой приостановки возникало расхождение с реальным временем. Была даже cron-задача, которую можно было установить для синхронизации виртуализированных часов с аппаратными. Но при создании новой виртуалки легко можно было об этом забыть, и это было весело. Позднее баг исправили.

    Если же отвлечься от багов, то существует ещё и високосная (дополнительная) секунда: она добавляется к UTC (всемирному скоординированному времени) в конце суток 30 июня или 31 декабря, чтобы компенсировать постепенное замедление вращения Земли и накопление разницы между сутками в системе СИ и астрономическими — солнечными — сутками.

    Что же касается длительности суток, то главный враг программиста — переход на летнее время, который где-то есть, где-то его нет, причём раньше его могли отменить или ввести. Это отдельная большая проблема исторических данных — учёт летнего времени.

    А если прибавить к этому смену часовых поясов…


    Про недели, месяцы и годы


    А вот истории насчёт разной длительности месяцев и лет связаны с разным календарным исчислением у разных народов мира. К примеру, еврейский календарь оперирует лунными месяцами: то есть начало и конец месяца привязаны к фазам Луны. Думаете, можно это легко учесть, добавив для Израиля корректировку с подгрузкой лунных фаз? Не выйдет. В еврейском високосном году добавляется лишний месяц. Более того, — следите за руками — и простой, и високосный год в еврейском календаре может иметь три разных длительности. Итого год может иметь шесть разных длительностей от 354 до 383 дней. Думаете, на этом отличия от нашего календаря закончились? Куда там: в еврейском календаре сутки имеют разную длительность и отсчитываются от заката до заката (формально, когда на небе становятся видны три звезды).

    Считаете, что это только у евреев всё не как в григорианском календаре (к вопросу о Великой октябрьской социалистической революции, которую в СССР праздновали 7 ноября)? В арабских странах:

    • Неделя начинается не в понедельник, а в воскресенье.
    • Выходными считаются пятница и суббота, а в некоторых странах — четверг и пятница. За последние 10 лет некоторые государства перенесли выходные на пятницу и субботу, чтобы способствовать международной торговле. А ещё не во всех арабских странах выходные состоят из двух дней.
    • Религиозные праздники зависят от наблюдений за лунным циклом, поэтому их сроки наступления и завершения не могут быть точно предсказаны.

    Что касается того, что недели и месяцы могут заканчиваться в следующем году, то это опять же характерно для стран с лунными и солнечно-лунными календарями — в них новый год наступает не 1 января. Навскидку можно сразу вспомнить китайцев, у которых новый год наступает в промежуток с 21 января по 21 февраля по григорианскому календарю. А в эфиопском календаре новый год вообще 29 или 30 августа, да ещё и количество лет у них на 8 меньше, чем в системе «от рождества Христова».

    На сервере и на клиенте всегда будет одно и то же время


    А вот интересный факт, из-за которого время на разных компьютерах может не просто не совпадать, но даже размер расхождения может меняться. При своей загрузке Linux берёт текущее аппаратное время, а затем отсчитывает его дальше, добавляя данные от внутренних часов процессора (TSC). Эти часы могут быть весьма неточны. Например, из-за масштабирования тактовой частоты, которая динамически меняет частоту TSC. И если поменять частоту часов на хосте, то все гостевые аккаунты даже не заметят этого. Если масштабировать частоту TSC на 50 %, время начнёт идти вдвое медленнее. Кроме того, на некоторых серверах BIOS может масштабировать частоту процессора, не уведомляя ОС, что тоже добавляет погрешность. На более современных процессорах частоты TSC теперь фиксированы. К слову, Windows не использует TSC, так что в этой ОС у отсчёта времени другие проблемы :)

    Можно легко вычислить количество часов и минут начиная с какого-то момента времени


    Если в языке программирования нет чего-то вроде питоновской tzinfo(), вы не сможете получить конкретную дату и время, просто добавив часы и минуты к какой-нибудь дате в прошлом. Нужно учесть временные зоны (и их возможную смену, как это не раз происходило в истории), затем учесть все изменения в переходе на летнее время… В Windows это вообще невозможно, потому что Microsoft предоставляет только начало и конец текущего года. Удивительно, что после стольких патчей обработки перехода на летнее время компания до сих пор не реализовала в Windows аналог tzinfo(). Вероятно, и не реализует.

    У времени нет начала и конца


    «Вашей программе никогда не понадобится обрабатывать даты до 1970 года». В Unix-системах (в том числе Linux и iOS) время отсчитывается в количестве секунд начиная с 00:00:00 1 января 1970 года по UTC (Всемирному скоординированному времени). Всё, что раньше, в Unix будет уже отрицательным временем. Причём время представлено в 32-битном целочисленном выражении, и самой ранней датой, возможной в Unix-системах, является 13 декабря 1901 года. А «сверху» Unix-время ограничено 19 января 2038 года, когда количество секунд с начала отсчёта достигнет 231, а это число системы могут посчитать отрицательным.



    Всё это — лишь малая часть огромного количества нюансов и багов, с которыми приходится справляться разработчикам любых продуктов, использующих время. Наверняка вам тоже есть что рассказать из своего опыта, пишите в комментариях.
    Mail.ru Group
    Строим Интернет

    Комментарии 57

      +14
      1) Здесь не указано, что это перевод (или вольный пересказ) вот этой статьи.
      2) Ее уже переводили на Хабре минимум два раза: раз, два.
      Upd. Второе — это перевод более поздней и расширенной версии.
      Upd2. При более внимательном прочтении — не перевод.
        0
        Так в расширенной версии добавили что-то полезное? В любом случае, хорошим тоном для перевода является указывать, что это вторая версия оригинала, и.т.п.
        0
        del
          +14
          Предыдущие статьи на эту тему:
          habr.com/ru/post/146109
          habr.com/ru/post/313274
          habr.com/ru/post/452584

          Заблуждения об именах:
          habr.com/ru/post/431866
          habr.com/ru/post/146901

          Адреса:
          habr.com/ru/company/hflabs/blog/260601
            0
            Заблуждение о дате рождения в паспорте:
            что там будут обязательно три числа: год, месяц и день и не будет букв.
              0

              простите, это как ?

                +1
                Бывают варианты, когда дата рождения в паспорте указана только месяцем и годом или только годом.
                При этом, если установлен только год, то днём рождения считают 1 июля, если первая половина года, то 1 апреля, вторая половина года — 1 октября, если установлен месяц, то 15 число этого месяца.
                Ну и, теоретически, может быть указана неверная дата, например 31 июня.
                  0

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

                  0
                  Пример от балды на основе реального тикета на исправление информционной системы: XX.XX.1930
                  Да, там именно «XX» — буквами латинского алфавита. Не РФный.

                  С учетом, сколько сейчас всего завязано на жесткое соответствие данных с данными из паспорта…
                  0
                  Доп.заблуждение о паспорте.
                  «примеры замены даты рождения являются одинаковыми во всём мире». Встречал год рождения 9999, возникал при оцифровке архива
                +5
                Автор ждёт комментарий? Пожалуйста: в 2012 году 30 июня 23:59:60 UTC была добавлена секунда координации. Один крупный международный вендор телеком-оборудования, зная о наличии бага в своём софте, разослал всем клиентам настоятельную рекомендацию выключить синхронизацию от внешнего источника по NTP за 12 часов до этого момента, и включить через 12 часов после полуночи 1 июля по UTC.
                Я выполнил эту несложную рекомендацию, на моей сети ничего не упало.
                  0
                  Поэтому у программистов никогда нет времени на то, чтобы сделать все правильно, но всегда есть время на то, чтобы сделать этого больше)
                    +2
                    При своей загрузке Linux берёт текущее аппаратное время,

                    откуда берется текущее аппаратное время и почему его дальше нельзя использовать блог компании Mail.ru Group не уточняет.
                    И что различие времен на клиенте и сервере связано как раз с различием этого самого аппаратного времени т.е. часов CMOS, если не стоит периодическая синхронизация, не говорится.

                    Так же граждане из Mail.ru Group не упоминают о наличии милли- и микросекунд, с которыми тоже не все так однозначно и что помимо персональных и не очень персональных компьютеров есть микропроцессоры, в которых может быть несколько таймеров и там со временем тоже случаются приключения.

                      0
                      В месяце может быть 28, 29, 30 или 31 день.

                      А с этим что не так?
                        +3
                        1582 год. У католиков в октябре после 4-го числа сразу настало 15-е.
                        1918 год. В России после 31-го января наступило 14-е февраля.
                          0

                          1892 год. Западное Самоа меняет часовой пояс и празднует 4 июля дважды. В их 1892 году было 367 дней, из них 32 — в июле.

                            0
                            А в 2011 Самоа меняет пояс обратно с UTC-11 на UTC+13 и после 29-го декабря наступает сразу 31-е.
                              0
                              30 дней — всё норм, хоть и нетипично для Декабря (ну да, ещё 30-е «потерялось», а 31-е — «лишнее» (для месяца с 30 днями))
                                0
                                И в году было 364 дня.
                          0

                          Если я всё правильно помню, то у китайцев например бывает 13-й месяц. И это тоже иногда нужно учитывать.

                            +1
                            Он и в российских банках раньше был без всяких там китайцев. На него в отчетности «садились» приходы, отправленные в декабре и принятые в январе. Их надо было учитывать отдельно от тех приходов, которые полностью шли в текущий год.
                              0

                              Такое тоже бывает. Но тут уже сложнее на грабли наступить так как это часть ТЗ.

                            0
                            Ответ на этот и на другие вопросы:
                            Переменная может вызываться из программы, которая работает в виртуальной машине. Виртуальная машина засаспенжена, а потом внезапно восстанавливает работу через неделю и сразу же синхронизирует время.
                              0
                              Вот этого не понял.
                              Ну, проработала она физически 3 недели из месяца — что это меняет? Если ее спросят (условно) «Что ты делал месяц назад?» ей же дату календарную определять, а не по своей фактической работе.
                                0

                                Это если она дату календарную записывает, а не количество тиков от старта

                                  0
                                  Какое отношение все это имеет к количеству дней в месяце?
                                  Это даже не неверная работа со временем, а замена реального времени каким-то левым параметром.
                                    0

                                    А причём тут количество дней в месяце? )


                                    Но программист может не знать, что на системе не реальное время. Более того, основное заблуждение программиста, имхо, при работе со временем это то, что он (его программа) может его получить

                                      0
                                      Это понятно, но он просто получит неправильное количество секунд (или чего-то еще), по ним вычислит неправильное количество минут-часов-дней-месяцев и т.д. Это не имеет отношения к вопросу про количество дней в месяце.

                                      А так — да, можно к списку в статье добавить такую проблему:
                                      — То, что программист считает временем — не обязательно им является.
                                        0

                                        Вам же привели примеры, когда количество дней в месяце официально не было 28-31.


                                        И к этому вдобавок ответ на запрос "какая дата была месяц назад" может вернуть неожиданный результат по множеству аппаратных и программных причин, начиная с неверной текущей даты из-за неточных RTC

                                          0
                                          «Месяц назад» и «месяц вперёд» — это вообще не строго определённые понятия. Месяц назад от 31 марта — это какое число? Предположим, 28 февраля. Тогда месяц вперёд от 28 февраля — это 28 или 31 марта?
                                            0
                                            Не правильные примеры) С последними днями месяца ещё норм, тут лучше 29 марта и 26 февраля)
                                            0
                                            Вам же привели примеры, когда количество дней в месяце официально не было 28-31.

                                            Мы же сейчас не об этих примерах, а о влиянии простоя. С этими примерами я не спорю, хотя они тоже довольно специфические.

                                            может вернуть неожиданный результат по множеству аппаратных и программных причин,

                                            Может, но с количеством дней в месяце эти причины не связаны.
                                    0
                                    Как правильно ответили внизу: если программа учитывает количество тиков таймера, он может жутко не соответствовать реальности.
                                    Так же довольно часто возникают проблемы, если в процессе работы программы поменялось время и «Время начала работы процесса» становится позже, чем «Время конца работы процесса»
                                      0
                                      Какое отношение все это имеет к количеству дней в месяце?
                                +7
                                Вы забыли самое главное заблуждение «Вся Россия живет по Московскому времени».

                                Особенно этим страдают разработчики в Москве. Когда не задумываясь использует формат без TZ в API.
                                Когда начинаешь спрашивать «А в какой зоне у вас время в API?», то очень удивляются и… часто отвечают «Время то оно одно у всех» (С)

                                А еще требования
                                • «Не хотим что бы в АРМ отображалось время с TZ места события».
                                • «Хотим что бы оператору было интуитивно понятно в какой момент произошло событие»


                                А потом начинается… «Тут в чеке у клиента указано что он оплатил в 16:30», а у меня время в АРМ 09:30. Незя так!!! (а ничо, что клиен платил не Москве, а оператор в Москве пялится в экран)
                                Хорошо… незя так незя… Показываешь время в таймзоне события (без показа таймзоны ибо этот показ вызывает перегрев мозгов операторов).

                                «А Почему у вас время показывается 16:30, а клиент сделал операцию в 09:30 по Москве.»

                                После требований «давайте не будем показывать TZ, операторы не понимают что это такое», я теряю веру в человечество.
                                  +1
                                  Надо для дураков выводить оба времени, и четко приписать — это по Москве, а вот это — местное. Хотя дураки все равно найдут где запутаться…
                                    0

                                    Особенно если "местное" для них по жизни синоним "по Москве"

                                  +2

                                  Эм, человек, который считает, что на сервере и на клиенте время одинаковое, никогда не пробовал общаться с сервером, у которого скорость измеряется в процентах от c. Дальше там есть школьный учебник с сложной формулой перевода времени из одного frame of reference в другой.

                                    +10

                                    Программистские заблуждения, что этого ещё не было на Хабре.

                                      +1
                                      В арабских странах:
                                      • Неделя начинается не в понедельник, а в воскресенье.
                                      • Выходными считаются пятница и суббота

                                      Из пункта 1 следует, что США — арабская страна, равно как и Израиль из второго пункта?
                                        +5

                                        Не следует. Есть утверждение: Вася любит пиццу. Из этого утверждения не следует, что я Вася, так как я люблю пиццу.

                                        +5
                                        Самое вредное и самое распространённое заблуждение программистов по поводу времени: «я сделаю лучше чем в стандартной библиотеке»

                                        А «сверху» Unix-время ограничено 19 января 2038 года, когда количество секунд с начала отсчёта достигнет 2^31, а это число системы могут посчитать отрицательным

                                        В Linux с незапамятных времён на 64-х битных системах time_t — 64 бита. А, начиная с версии 5.6, и в 32-х битных системах, тоже.
                                          0
                                          А, начиная с версии 5.6, и в 32-х битных системах, тоже.

                                          Это только касательно внутреннего представления и новых системных вызовов, в которые новая libc заботливо транслирует библиотечные вызовы. А вот старые бинарники, собранные со старой libc, всё ещё пользуются старыми системными вызовами, потому что их бинарный код считал и считает time_t 32-битным.

                                            0
                                            не обязательно старые. Есть некоторое количество линуксов, рт-осов или вообще безоперационных систем на 32битных мелкопроцесорах, там тоже sizeof(time_t) = 4

                                            Зы: посмотрел, что там у меня в мелкопроцессорном це для time_t

                                            typedef unsigned int time_t; /* date/time in unix secs past 1-Jan-70 */

                                            т.е. для мелкопроцессорной железяки нет проблемы 2038 года. Зато будут у компьютерной программы на писи, которая это время читает в свое time_t, которое чтобы не заморачиваться использует #define _USE_32BIT_TIME_T
                                          +5

                                          Мой любимый абзац tzdata:


                                          В Саудовской Аравии учёт времени вёлся по-всякому, в зависимости от того, кто вы и где находитесь. В 1969 году Элаяс Антар пишет, что обычай предписывал устанавливать часы на 12:00 (т. е., полночь) на закате — что для жителей гор означало существенно разное время, в зависимости от того, на каком склоне они находятся — однако многие иностранцы свои часы устанавливали в 6 вечера, в то время как авиалинии последовательно использовали UTC+3 (кроме Дахрана, где использовался UTC+4), АРАМКО использовала UTC+3 и летнее время, а Трансаравийская Нефтяная Компания использовала правила АРАМКО в восточной части страны и авиационные правила на западе. (Американская консультационная группа по вопросам военной помощи использовала просто UTC, без сдвигов.) Антар пишет: «Местной электростанцией в то время заведовал человек по имени Хиггинс. Однажды ему это всё так надоело, что он собрал персонал и объявил закон: “С меня хватит,” — воскликнул он, — “Сейчас ровно 12 часов по времени Хиггинса. Отныне эта станция работает по времени Хиггинса.” И с тех пор, до прошлого года, она так и работала.»
                                          Antar E. Dinner at When? Saudi Aramco World, 1969 March/April. 2-3
                                          http://archive.aramcoworld.com/issue/196902/dinner.at.when.htm
                                          0

                                          Помню как завис знакомый mysql когда в России отменили переход на летнее время. В смысле, когда настал день. когда должны были перейти, но с новым законом не стали. Обновления tzdata в ОС стояли, но у мускуля свои отношения с tz

                                            0
                                            А еще в виртуальной машине время может отставать :-E
                                              0
                                              до сих пор не реализовала в Windows аналог tzinfo()

                                              В WinAPI есть timezoneapi.h, в котором как будто бы есть нужные функции и структуры.
                                                +1
                                                Замечательная статья и особенно комментарии. Мне как исследователю времени это очень интересно. В свое время я написал статью Эссе на тему единого универсального общепланетарного времени
                                                  –1
                                                  Факты из личной практики:

                                                  В одном из проектов мне нужно было считать время, я чтобы не заморачиваться, захардкодил длину года как 365.25 дней. В принципе это работало, грабли не вылезли (вроде бы).

                                                  Как то в одном проекте мы делали продажу ЖД билетов, мой коллега все реализовал и все работало ОК. Как то к нам поступила заявка, проверить один странный баг. Клиент жаловался на то, что он покупал билет на одну дату а ему продали на другую. Мой коллега так и не смог воспроизвести этот баг. Я при изучении тестовых логов, обнаружил, что время было сдвинуто на 3 часа. Как оказалось, это было смещение UTC+3. То есть, если покупать билет ночью, то есть шанс купить билет на другие сутки из-за локального расчета времени как UTC+0.
                                                  По результатам проверки бага, техподдержка написала клиенту отписку в стиле «сам балбес», стоимость билета компенсировать отказались, а историю тихо замяли. Как то так.
                                                    0

                                                    365.24 же по феншую

                                                    0
                                                    Сталкивался с забавной ситуацией некорректный часовых поясов.
                                                    Юзер пользуется почтовой программой и через маил.ру в 13:03 МСК (10:03 по гринвичу) отправляет письмо.

                                                    Но, у него стоит неправильный часовой пояс +4, при правильном времени (13:03).
                                                    Получается, что письмо он отправил в 09:03 по гринвичу.

                                                    Сервер это все перерабатывает и получателю показывает письмо отправленное в 12:03 МСК, за час до написания. И с таким же временем кладет письмо в папку отправленные, юзер его потом не может найти («я же писал его в 13, ну никак не раньше»).

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

                                                      Про разницу сервера и клиента.
                                                      Несколько лет назад в России перестали переводить время зимой/летом. Мой коллега сидел на Windows XP, патча для которой не было. Чтобы видеть правильное время на экране при использовании неправильного (непропатченного) часового пояса, он просто перевел системные часы на час.


                                                      И все работало хорошо. До тех пор, пока ему не понадобилось сделать аплоад файла из браузера на AWS S3. Клиентский модуль в браузере добавляет заголовок "x-amz-date" со временем браузера. Это время не соответствует подписи, сделанной на бэкенде. В итоге S3 отвечает ошибкой.


                                                      Было очень непросто найти источник проблемы :)

                                                        0

                                                        При сильной разнице во времени у клиента и сервера, всегда будет ошибка валидации на https.

                                                        0

                                                        Сколько бы можно было избежать если бы работал Удобный Шестидневный календарь

                                                          +1
                                                          А сколько бы проблем добавилось по поддержке еще одного календаря и конвертации даты между ними… Если к власти придут программисты из 1с, они наверное именно это и сделают.

                                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                        Самое читаемое