Comments 108
1) Присоединился дебаггером к процессу
2) *какая-то магия с ассемблером*
3) Профит!
А сейчас что? Скачал исходники, поставил току останова и отладил. Скукота!
Компилировать чужие исходники (иногда свои) бывает очень трудно.
И надо знать, куда ставить breakpoint.
Создаётся впечатление, что код на 99% состоит из «воздуха», который реально ни во что не компилируется.Такой же точно калькулятор, написанный в девяностых, был бы раз в сто меньше, как по объему бинарника, так и по потребляемой памяти.
У меня впечатление что этот код не только компилируется, но еще и пару миллионов пустых циклов добавляет.
до исправления было лучше — сразу видно что ошибка.
а щас никто не заметит ошибку и будет использовать неправильный результат :(
Математически операция складывания месяцев с конкретным числом — это бред. Без уточнений по крайне менее. Мы можем взять месяц как стандартные 30 дней (что является математическим округлением среднего ~30.44), тогда, например, мы будем "пропускать" февраль: 31 января + 1 месяц = 1~2 марта. Можно "обрезать" месяц, тогда операция теряет ассоциативность: (31 января + 1 месяц) + 1 месяц != 31 января + (1 месяц + 1 месяц). Можно "сохранять" число при обрезании, но тогда повляются "странные" элементы: 28</28> февраля, 28</29> февраля, 28</30> февраля, 28</31> февраля (да и стремление максимально узаконить такие операции — странная, так как есть же ещё разные календари со свойствами транзитивности — всё это учитывать… непонятно зачем).
Так что. Не факт, что это ошибка. Возможно именно это и есть ожидаемое поведение, проблема в том, что ожидаемых поведений может быть много. И вот здесь уже калькулятор плох даже тем, что просто определяет операцию даже не уточняя, а что он имеет ввиду.
— такая же команда есть в языках программирования, например в 1С Предприятие.
Лень проверять, но думаю что 1С скажет что это точно ошибка :)
Лучше проверить. Я проверил на python — у него в timedelta нельзя определить именно месяц. А вычитание конкретных дат приводит к конкретному количеству дней.
Дата = Дата("20190731");
Дата = ДобавитьМесяц(Дата, 4); //30.11.2019 0:00:00
А «следующая зарплата через месяц после 30 января» — тоже бред?
Но с математической это действительно бред, т.к. понятие месяц = неизвестная величина (28, 29, 30, 31), и в этом случае можно прибавлять только кол-во дней или недель (часов, минут и т.д.), т.е. строго детерминированные величины.
А что с человеческой точки зрения будет 31 января + месяц?
Если же с точки зрения «дней», то надо определить значение «месяц» в днях. Думаю для большинства месяц = 30 дней, что даёт нам 1-2 марта.
Когда вы прибавляете к дате один месяц, то вы прибавляете следующий месяц, а не средний.
Потому, для 31 января длина следующего месяца не 30, а 28 (29) дней, и именно 28 (29) дней и следует прибавить. Получим 28 (29) февраля.
то вы прибавляете следующий месяц
Или же текущий. 1 июня + 1 месяц — я ожидаю 1-е июля, а не 2-е (в июле 31 день, в июне 30).
Всё таки стоит исходить из того, что нет однозначно верного ответа, что такое «через месяц после 31 января». И так как калькулятор не может знать, что ожидает пользователь, любой ответ может оказаться неверным для задачи пользователя.
Может показаться странно, но я ни разу не слышал — через месяц после 30 января. Через месяц — да, но это подразумевает уже само по себе ± пара дней. Но вообще контекст в таких вещах плохо работает и часто уточняют — в конце контекст.следующий_месяц, и даже в этом случае 146% переспросят "то есть в январе?". А именно такая формулировка встречается разве что в анекдотах про математиков (и задачах по спортивному программированию).
28/29/30/31 января + 1 месяц = 28 февраля
(дата + N месяцев) - N месяцев
не всегда равно дата
(дата + 1 месяц) + 1 месяц
не всегда равно дата + 2 месяца
и т.п.
Потому что сложение коммутативно, ассоциативно и дистрибутивно (с умножением), и все слишком сильно к этому привыкли.
Лучше назвать ее как-нибудь по-другому, и не пытаться использовать здесь знаки "+" и "-", чтобы не вводить людей в заблуждение.
Неверно — это ожидать от операций с временными величинами результатов, как от обычных десятичных чисел.
Тогда можно определить значения типа timeinterval: '1 second', '1 day', '1 week'.
Но нельзя определить значения '1 month', '1 year', '1 century'.
И снова все будет работать.
Математически операция складывания месяцев с конкретным числом — это бред. Без уточнений по крайне менее.
Не бред, а недостаточная проработка логики. Если уж введена сама возможность складывать месяцы, то при выполнении такой операции должны фоном проводиться проверки: заданное условие DD.MM.YYYY проверяется на високосность, месяц точки отсчёта, таким образом прибавка 3х месяцев учитывает календарный состав следующих за заданным трёх месяцев и система точно знает — сколько там на самом деле дней. Это не так сложно, календарь всегда доступен для обращения за актуальными данными.
Тут важно, для чего считать. Месяц на бытовом уровне может быть примерным, особенно если речь идет о десятилетия.
А если о платежах каких-то — то может быть важен каждый день.
Но это всегда может быть какой-то перевёрнутый бит каким-то высокоэнергетическим лучом от какого-то дружественного космического соседа.
Я конечно понимаю, что это шутка, но вероятность того, что из-за этого повредится какой-то бит в памяти компьютера крайне мала, не говоря о вероятности повреждения конкретного. Скорее всего произойдте просто синий экран и компьютер перезгарузится. Ну или в какой-нибудь картинке пиксель или блок пикселей неправильно отобразится.
Исследования, проведенные IBM в 1990-х годах, показывают, что компьютеры обычно испытывают около одной ошибки, вызванной космическим лучом, на 256 мегабайт оперативной памяти в месяц.
Ок, наверное был не прав. Кстати, на хабре уже была статья про статистику отказов в серверной памяти.
Вентилятор на 286? Откуда?
2. В разнице между дат в программах всегда надо учитывать что именно за даты, какого года. Дальше уже понятно что +30 дней это +30 дней, а плюс месяц это плюс месяц…
В целом очень молодец.
Поэтому угождать надо человеку и 5 мая + месяц должно быть 5 июня…
А чему в таком случае будет равно «31 января + 1 месяц»?
И срабатывает валидация результата, которая говорит, что как-бы нет такой даты.
Куда интереснее, чему должно быть равно (31 января + 1 месяц) + 1 месяц. Нужно ли помнить предысторию получения текущего значения даты.
31 января + (1 месяц + 1 месяц) = 31 января + 2 месяца = 31 марта
Достаточно логично, как по мне. Ассоциативность придётся закопать, конечно, но у нас ведь тут особое сложение.
И да, я согласен, что вешать такое поведение на стандартный оператор сложения — готовить почву для багов
если так идти назад по 1 дню, то 3 января + 1 месяц = 1 февраля?
31 января + 1 месяц = 28/29 февраля
30 января + 1 месяц = 28/29 февраля
...
28 января + 1 месяц = 28 февраля
27 января + 1 месяц = 27 февраля
Это более-менее соответствует моему повседневному опыту работы с датами и хорошо формализуется. Но, повторюсь, сложением в обычном математическом смысле это не является. А пытаться натянуть даты на обычное сложение — плохая затея, получится только хуже.
Но 15 февраля — 2я половина месяца, а 15 марта — 1я половина месяца! Выходит что мы прыгнули меньше чем на 1 месяц!
Должно быть 15 февраля + 1 месяц = 16/17 марта
Выше уже обсудили
Ага, вижу. Я просто задал вопрос, когда обсуждения ещё не было.
как правило, 31 января + 1 месяц = 28/29 февраля, в зависимости от года.
Куда интереснее, чему должно быть равно (31 января + 1 месяц) + 1 месяц. Нужно ли помнить предысторию получения текущего значения даты.
К этому я и вёл. :) То, что прибавить 2 месяца и два раза прибавить месяц — это разные вещи, может привести к трудноуловимым багам. Если уж делать так, то не называть операцию сложением.
А почему некто не догадался представить мечюсяцы как замкнутый двухсторонний цикл?
И прибавление месяца, это просто переход на новое значение в списке…
С годом тоде самое, но список не замкнутый
Пример 1:
10 февраля 2018
- 5 месяцев
должно работать вот как:
Сохраняем в буфер количество дней
Передвигаем замкнутый список месяцев на 5 позиций возвращаем дни.
Пример 2:
10 февраля 2018
- 5 месяцев 25дней
Повторяем все что в примере 1, а дальше
Сохраняем сумму дней в переменную
После чего
Вытягиваем из двух связного списка количество дней в получившимся месяце
И вычитаем из получившегося дней, если разность больше 0 переключаем месяц и добавляем разность, если меньше просто добавляем сумму дней
Вроде всё логично кроме одного понятия: сначала прибавлять дни, а уже потом месяцы или наоборот?
Лучший вариант-сделать галочку, что переключает это состояние
Уххх, а еще есть боль от номеров недель. "Надо сравнить продажи за 2 года с группировкой по номерам недель. ....wtf!!! почему один год у тебя начинается с последних чисел декабря предыдущего, а другой не с первого января????" :)
Вы слишком категоричны. Данная закавыка может возникнуть в любой конторе, в любой стране и никто не говорит, что она не решаема. Я говорю, что она решается через некоторую боль и страдания.
И разработчик все же должен включать мозги, не имплементировать тупо по "наиподробнейшему" ТЗ. Иначе это мартышка, а не девелопер
astro.ins.urfu.ru/sites/default/files/chair/study/docs/gost_8601-2001.pdf
dotat.at/tmp/ISO_8601-2004_E.pdf
и более новые
Но ведь это неверно, результат меньше 5 месяцев
Если по человечески подумать, то 31.07 + 5 месяцем, это 31.12,
а тут 30.12. По мне это 5 месяцев без одного дня,
а не 5 месяцев и 6 дней
31.07.19 + 5m6d = 06.01.20
30.12.19 - 5m6d = 24.07.19
КОМУ, Карл! :)
var first = LocalDate.of(2019, JULY, 31);
var second = LocalDate.of(2019, DECEMBER, 30);
print(Period.between(first, second));
Выведет
P4M30D
, что очень близко с результатом после починки (т.к. P30D
равен P4W2D
). Может это и «неправильно» — но неправильно скорее думать об этой операции как о каноничном сложении. Зато теперь калькулятор выдаёт тот же ответ, что многие другие приложения (в частности, написанные на Java, да).Смотрите, похоже, с годами тоже аналогичная проблема проявляется
Исправляя мелкий баг в calc.exe