Как стать автором
Обновить

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

Спасибо! А на C++/Qt что-то такое со временем есть — тоже приходилось костыли писать для монотонного непрерывного времени.

Конечно есть.
Если проект с C++11, то в chrono есть std::chrono::steady_clock. Если на более старом стандарте, то нужно использовать системные вызовы целевой ОС (вроде clock_gettime с CLOCK_MONOTONIC на *nix или QueryPerformanceCounter на винде).


К слову, все это легко находится в гугле по запросу "monotonic time c++/c++03/windows/unix".

Здорово, спасибо, мы писали ещё в 2010-2011, тогда этого стандарта ещё не было.
А boost::chrono уже был)
Возможно, а может не хотели лишнюю библиотеку в инсталлятор тащить.
Отличная статья, для некоторых вещей, реализованных в этих библиотеках, писались свои велосипеды.
Спасибо.
P.S. Вторая часть очень опосредованно относится к времени)

С неправильным временем у пользователя был занятный кейс. Мы делали некий продукт для небогатых, в основном, компаний, и он был сильно завязан на корректное время. Дело было в начале '10-ых, в России недавно переколбасили часовые пояса, а у всех была пиратская винда, которая не обновлялась и в ней не было новой tzdata. Приходилось брать время из ответов сервера, вводить поправку на сетевую задержку, вычислять смещение часового пояса и плясать с бубном, потому что в JS есть getTimezoneOffset, но нет setTimeZoneOffset...

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

А как брали поправку на сетевую задержку?

Уже не помню:( Как-то костыльненько, но погрешность нас устраивала.

Спасибо за статью!
Работа со временем — очень важная вещь.
Сам давно задумывался над этим и пытался выработать решения на распространенные кейсы, плюс на обработку краевых случаев.
Радует, что одни и те же мысли приходят одновременно разным людям.


Хочется, однако, риторически прокомментировать вот это :


Так линейно вы, в общем, и пишете код. Дата начала — сегодня минус одни сутки, дата окончания — сегодня. Казалось бы, все работает, но потом ровно в полночь у вас случается странное. У вас дата начала вот здесь. Дата начала минус одни сутки — получается, такая. После этого почему-то дата окончания отчета совершенно другая.

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

Абсолютно типовая ошибка, проистекающая из неаккуратности, нежелании разобраться в природе вещей (времени в данном случае), низкой культуры кодирования, незнания и нежелания разобраться в реализации стандартной библиотеки, и как она (реализация) маппится на ту самую природу вещей.


Но… Скажите, где вы находите компании, где за такое просят покинуть команду или лучше компанию?
Сколько помню, везде в энтерпрайзе попадался именно код такого характера, со временем, включая прямо вот описаную вами ошибку, другие типовые ошибки со временем и другими сущностями.


И что характерно, авторы такого кода были всегда в почете (такой код можно писать быстро, перформанс наше все! — почти ж никто в ИТ не в курсе, что нужно перформанс для разрабов нужно переводить вторым главным значением — "эффективность", а не первым "производительность", интерпретируемую как скорость закрытия тасков на гора, а дальше не волнует).
Другое дело, что, как правило, авторы такого кода были уже далече, только не их попросили, а они на гребне волны славы сами успели уйти на повышение — в другие компании, реже в той же компании.

НЛО прилетело и опубликовало эту надпись здесь
«Перейти через шесть месяцев в другую команду» — это шутка, намёк на доклад с той же конференции об управлении командами разработчиков в большой компании. Прошу не воспринимать серьёзно и не принимать как руководство к действию.
И в других фреймворках с другими методами life cycle то же самое логично. Когда у вас происходит какой-то destruct, destroy, еще что-нибудь, unmount, надо корректно такие вещи не забывать чистить за собой.

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


слышали, хотя бы, про функциональный подход, про чистые функции, про отсутствие side-эффектов
эти два запроса асинхронных, они явно друг от друга не зависят

Вы бы хоть определение чистой функции почитали..


Изучайте качественный код, в том числе опенсорс — Lodash, RxJS и т. п.

https://github.com/lodash/lodash/blob/master/.internal/createRound.js
https://github.com/ReactiveX/rxjs/blob/master/src/internal/util/ArgumentOutOfRangeError.ts
Очень "качественный" код..

использвать фреймворки, которые чистят всё автоматически, а не полагаются, что разработчик ничего не забудет

Есть пример такого фреймворка? Если опишете, как там реализована очистка всего и защита от вызовов после разрушения компонентов — будет отличная статья.

$mol же. Реализовано просто — реактивные переменные отслеживают зависимости. Если значение ни кому не нужно — у него вызывается destructor. Пример.

А если я вызову обычный таймаут, то что?

Очевидно, ничего.

Ну то есть не работает оно в $mol. Тогда возвращаемся к предыдущему вопросу — а в каком фреймворке работает?

А вас кто-то заставляет вызывать setTimeout напрямую?

А в чём именно приведённый пример из лодаша некачественен?

То есть вас не смущает, что для всех округлений чисел там используются строковые операции?

А как бы вы это реализовали? Там 9 строк кода (если без скобок), думаю вам несложно будет показать лучшее решение, раз вы судя по всему его знаете
Со сравнением производительности
В идеале, конечно, сделать PR, но можно и просто здесь код показать
console.log(_.round(4.006, 2)); // 4.01
console.log(floor(4.006, 2)); // 4

console.log(_.round(4060, -2)); // 4100
console.log(floor(4060, -2)); // 4000



Вы точно проверяли свой код? Он работает неверно и не является заменой лодашевскому

Не понял, то есть вы недовольны одним кодом, а привели пример переписывания другого?

Я не бросаюсь её защищать. Я прошу обосновать утверждение о том, что приведённый пример действительно плох.
Вы привели в пример функцию, которая используется в round. В floor тоже, да. Но ещё и в round. И если я правильно понял, вы хотели показать пример как переписать её "красиво". Естественно, не лишая её имеющейся функциональности
Может я не увидел чего-то по вашей ссылке, или не понял. Но мне показалось, что функциональности round в вашем коде нет. Прошу прощения, если не заметил, и ткните тогда пальцем в виде размещения этого кода здесь

НЛО прилетело и опубликовало эту надпись здесь

А давайте вы самостоятельно замените floor на round в обеих реализациях и убедитесь, что ничего принципиально не изменится? Продолжать этот диалог мне не интересно, удачи вам по жизни.

Ещё непонятнее стало. Где ваш код round то? По вашей ссылке я вижу тот же "говнокод" со строками. Что на что вы предлагаете мне заменить?
Я допускаю, что не могу посмотреть ваш код, и вижу только код лодаша, потому что либо не разобрался в интерфейсе, либо в мобильном браузере что-то идёт не так. Но вы упорно отказываетесь показать свой код прямо здесь, и поэтому я до сих пор так и не увидел вашу красивую версию ф-ии round

Почему для Math.round() использованы строковые операции, описано комментарием прямо в исходнике — из-за не всегда верного округления Math.round. Например, попытка округления 1.005 до двух знаков после запятой "решением в лоб" Math.round(1.005 * 100) / 100 даст ровно 1, а _.round(1.005, 2) даст правильное значение 1.01.

  1. Эта "точность" никому не нужна ценой 10-кратного падения производительности.
  2. А те, кому нужна, не используют вещественные числа так как _.round( 1.00003+0.00002, 4 ) даёт всё ту же единицу.
Часто забывают, что месяцы в Date считаются с 0 и до 11.

Ошибаются в инкрементах на неделю, месяц, год в последних днях месяца. 31-01-2019 + 1 месяц = 31-02-2019 = 03-03-2019. Иногда забывают про високосные годы.

Ошибки инкремента даты делают при переходах между зимним и летним временем. Мы же помним, что время подвластно политикам и правила могут меняться год от года.

Отдельная тема — часовые пояса. Какой сейчас день? А где именно? Вот уж действительно начинаешь понимать, что живёшь в пространственно-временном континууме. Чтобы определить «когда» нужно знать «во сколько» и «где». Хранить информацию о времени в unix timestamp может быть не лучшей идеей. Нужно сто раз взвесить, прежде чем решать приводить все к UTC.

На самом деле, не имея информации о часовом поясе и актуальной версии tzdata вы мало, что можете правильно посчитать. Рассчет на сервере и на клиенте может давать разные результаты. После очередного апдейта tzdata результаты могут сильно измениться.

Так же хлопот доставляют форматы записи дат в разных локалях. 10/11/12 без контекста не распарить.
Про даты я бы добавил про таймзону. Особенно весело когда на фрон приходит дата в timestamp и может показать каку, если таймзона на беке оказалась не та

Но ведь обычно timestamp это количество (милли) секунд, прошедших с конкретного момента времени. Для него нет таймзоны. Где бы я ни находился, в Сеуле или в Лондоне, количество секунд будет одинаково

Просто часто удобно задавать дату не в виде timestamp, а как строку — '09-02-2020'. Пример апи гугл анатилики. Важно чтобы не было рассинхрона таймзоны. Если у нас локальная таймзона Сеул, а в апи на беке таймзона — Москва. Тогда на бек дата должна прийти с учётом таймзоны. И если у нас нет нигде рассинхрона в таймзоне, значит мы распарсим дату в timestamp, запишем в БД его. И при обраотке в бд и последующем извлечении и отдаче куда-либо — у нас всё будет хорошо.
Когда дата приходит строкой без таймзоны это повод задуматься в любом случае. Либо имеется в виду gmt, либо важен только сам день (возможно само время), не важно в каком часовом поясе. А вообще желательно всегда использовать iso 8601
Но вы писали
на фрон приходит дата в timestamp… если таймзона на беке оказалась не та
— я только это и хотел отметить. У timestamp нет часового пояса

Странно что не упомянуты еще типичные грабли:


  1. Запрос в БД с указанием ТОЛЬКО дат диапазона, когда в поле в БД включает еще и время. Типично от получается в запросе поле времени по умолчанию 00:00:00 до 00:00:00, а нужно, как правило, от 00:00:00 до 23:59:59. Очень типичная ошибка.


  2. Запрос из браузера выборки по диапазону времени. А времени чего? От TZ браузера? От TZ БД? От Гринвича/MSK? А вообще чего нужно то? Полночь в России наступает в разное время.



Кстати, типично, что в большинстве протоколов "нарисованных" разработчиками из Москвы отсутствует таймзона как сущность воооообще.
А на вопрос "а в какой TZ время то указывать?" вызывает ступор. Как будто другой TZ кроме MSK не бывает и не важно где (TZ) по факту событие произошло. Особенно не важно это для фродмониторинга <сарказм>


Особенно весело, когда встает вопрос про отчеты для банка у которого филиалы и в Москве и Владивостоке и слияние недавно произошло. Вопрос не технический. Организационный.


И по моему мнению, больше всего проблем с TZ возникает.
Например в таком простом вопросе "Хочу видеть транзакции за вчера. Филиалы у нас в Барнауле, Москве и Владивостоке, а терминалы по всей России".


А вам слабо объяснить сотруднику в Барнауле, почему список транзакций за день у него и сотрудника в Москве разный при одном подходе (список исходя из TZ браузера оператора).
Или почему он не видит транзакцию сделанную сегодня ночью (вот же она на чеке) в списке транзакций за сутки при другом подходе (список, исходя из TZ головного филиала в Москве).
И начинается… ваш софт г-но и выдает ерунду. "Хотим кнопку счастья для любого тупого сотрудника."


Иногда кажется что у многих абстрактное мышление отсутствует как класс..

поле времени… нужно, как правило, от 00:00:00 до 23:59:59

Кстати, здесь может быть ещё одна ошибка, если время хранится не округлённым до секунды.


Например, если время хранится с точностью до миллисекунды, то надо задавать диапазон времени от 00:00:00 до 23:59:59.999


Как вариант, чтобы меньше вспоминать о точности хранения времени, можно задавать конечную дату в запросе как "меньше 00:00:00 следующего дня", а не "меньше или равно 23:59:59.999 текущего дня".

Эволюционно пришли к тому же выводу, иначе от базы к базе нужно каждый раз помнить как там дата и время храниться. Проще делать в WHERE условие datetime < '2019-08-13 00:00:00' чем выяснять про секунды и миллисекунды
Мне тут приходилось объяснять заказчику почему задача сохранения времени пользователя на сервере и построение отчетности в сервисе, расчитаным на весь мир, может занять много времени. А потом ещё разработчикам объяснять, почему мы будем хранить время в UTC, со сдвигом пользователя от UTC. Все (и заказчик, и разработчики) были в смятении, и смотрели на меня как на сказочника. Покажу им эту статью. Это я еще про летнее/зимнее время забыл, блин… Спасибо, что напомнили.
НЛО прилетело и опубликовало эту надпись здесь
Какое-то невнятное выступление на видео. В тексте лучше описано
Не то, чтобы я зануда, но зачем упомянута Java в тегах и списке хабов?
Если уж упомянули ее, было бы неплохо кратко хотя бы пробежаться по особенностям и фичам при работе с временем/календарем. Уж до 8й версии там было о чем рассказать, это точно. А это до сих пор больше половины всех проектов. А может и больше.
Микрософт не парится и в своих логах пишет локальное время, которая скачет при синхронизации с NTP и изменении таймзоны.
Можно было бы конечно реализовать временные отметки не в абсолютном времени, а в относительно, типа сколько времени прошло от старта большого процесса.
Но часто удобнее просто поискать по локальному времени, чем искать начало и считать смещение.

Есть ещё такая экзотическая штука как "высокосная секунда". Встречается нечасто и мало кому мешает. Но мне как-то изрядно выпила крови при синхронизации времени двух телецентров.

Про время — старое но не потерявшее актуальности видео. www.youtube.com/watch?v=-5wpm-gesOY С ним всё очень, очень грустно… Особенно, когда вспоминаешь, на что из списка натыкался
Зарегистрируйтесь на Хабре , чтобы оставить комментарий