Неверный результат вычисления диапазона дат в Калькуляторе Windows
«Ну, это похоже на любопытную ошибку, интересно, что может её вызвать», — подумал я про себя. Количество недель, безусловно, делает баг похожим на какую-то ошибку переполнения или задания диапазона, ну вы знаете, типичные причины. Но это всегда может быть какой-то перевёрнутый бит каким-то высокоэнергетическим лучом от какого-то дружественного космического соседа.
Заинтересовавшись причиной, я сделал то, что вы делаете в таких случаях: попробовал на своей машине, чтобы запостить «У меня всё работает». И повторение ситуации из поста «31 июля ? 31 декабря» на моей машине дало правильный результат «5 месяцев». Но немного потестировав, я обнаружил, что «31 июля – 30 декабря» на самом деле вызывает ошибку. Выводится не совсем корректное значение «5 месяцев, 613566756 недель, 3 дня».
Я ещё не закончил расшатывать программу и тут вспомнил: «О, а разве калькулятор — не одна из тех вещей, для которых Microsoft открыла исходники?» И действительно. Эта ошибка не могла быть слишком сложной, поэтому я подумал, что попробую найти её. Скачать исходники было достаточно просто, и добавление требуемой рабочей нагрузки UWP в Visual Studio также прошло без сучка и задоринки.
Навигация по кодовым базам, с которыми вы не знакомы, — это то, к чему привыкаешь со временем. Особенно когда вы хотите внести вклад в проекты с открытым исходным кодом, где находите баг. Однако незнание XAML или WinRT, конечно, не облегчает дело.
Я открыл файл solution и заглянул в проект “Calculator” в поисках любого файла, который должен иметь отношение к багу. Нашёл
DateCalculator.xaml
, затем вроде бы подходящий по названию DateDiff_FromDate to DateCalculatorViewModel.cpp
и, наконец, DateCalculator.cpp
.Установив точку останова и посмотрев некоторые переменные, я увидел, что конечное значение
DateDifference
уже неверно. То есть это была не просто ошибка преобразования в строку, а ошибка фактического вычисления.Фактическое вычисление в упрощённом псевдокоде выглядит примерно так:
DateDifference calculate_difference(start_date, end_date) {
uint[] diff_types = [year, month, week, day]
uint[] typical_days_in_type = [365, 31, 7, 1]
uint[] calculated_difference = [0, 0, 0, 0]
date temp_pivot_date
date pivot_date = start_date
uint days_diff = calculate_days_difference(start_date, end_date)
for(type in differenceTypes) {
temp_pivot_date = pivot_date
uint current_guess = days_diff /typicalDaysInType[type]
if(current_guess !=0)
pivot_date = advance_date_by(pivot_date, type, current_guess)
int diff_remaining
bool best_guess_hit = false
do{
diff_remaining = calculate_days_difference(pivot_date, end_date)
if(diff_remaining < 0) {
// pivotDate has gone over the end date; start from the beginning of this unit
current_guess = current_guess - 1
pivot_date = temp_pivot_date
pivot_date = advance_date_by(pivot_date, type, current_guess)
best_guess_hit = true
} else if(diff_remaining > 0) {
// pivot_date is still below the end date
if(best_guess_hit)
break;
current_guess = current_guess + 1
pivot_date = advance_date_by(pivot_date, type, 1)
}
} while(diff_remaining!=0)
temp_pivot_date = advance_date_by(temp_pivot_date, type, current_guess)
pivot_date = temp_pivot_date
calculated_difference[type] = current_guess
days_diff = calculate_days_difference(pivot_date, end_date)
}
calculcated_difference[day] = days_diff
return calculcated_difference
}
Выглядит нормально. В логике проблем нет. По сути, функция делает следующее:
- отсчитывает полные годы от стартовой даты
- с момента даты последнего полного года отсчитывает месяцы
- с момента даты последнего полного месяца отсчитывает недели
- с момента даты последней полной недели отсчитывает оставшиеся дни
На самом деле проблема заключается в предположении, что последовательный запуск
date = advance_date_by(date, month, somenumber)
date = advance_date_by(date, month, 1)
равен
date = advance_date_by(date, month, somenumber + 1)
Обычно это одно и то же. Но возникает вопрос: «Если вы попали на 31-е число месяца, в следующем месяце 30 дней, вы прибавляете один месяц, то куда попадёте?»
Похоже, для Windows.Globalization.Calendar.AddMonths(Int32) ответ будет «на 30-е число».
А это значит, что:
«31 июля + 4 месяца = 30 ноября»
«30 ноября + 1 месяц = 30 декабря»
«31 июля + 5 месяцев = 31 декабря»
Таким образом, операция AddMonths не является ни дистрибутивной (с AddMonth-умножением), ни коммутативной, ни ассоциативной. Какой вообще-то должна быть операция «сложения». Разве не весело работать со временем и календарями?
Почему в данном случае ошибка задания диапазона приводит к такому огромному числу недель? Как вы могли догадаться, это возникает из-за того, что
days_diff
является беззнаковым типом. Это превращает -1 дней в огромное количество, которое затем передаётся на следующую итерацию цикла с неделями. Которая затем пытается исправить ситуацию, уменьшая current_guess
, но не уменьшая беззнаковую переменную.Что ж, это был интересный способ провести воскресенье. Я создал пулл-запрос на Github с минимальным «исправлением». Я ставлю «исправление» в кавычки, потому что теперь вычисление выглядит так:
Думаю, технически это правильный результат, если считать, что «31 июля + 4 месяца = 30 ноября». Хотя такой вариант не совсем согласуется с человеческой интуицией о разнице дат. Но в любом случае это менее неправильно, чем было.
Комментарии (101)
manyakRus
19.06.2019 13:03+1«31 июля + 4 месяца = 30 ноября»
до исправления было лучше — сразу видно что ошибка.
а щас никто не заметит ошибку и будет использовать неправильный результат :(Deerenaros
19.06.2019 14:04Математически операция складывания месяцев с конкретным числом — это бред. Без уточнений по крайне менее. Мы можем взять месяц как стандартные 30 дней (что является математическим округлением среднего ~30.44), тогда, например, мы будем "пропускать" февраль: 31 января + 1 месяц = 1~2 марта. Можно "обрезать" месяц, тогда операция теряет ассоциативность: (31 января + 1 месяц) + 1 месяц != 31 января + (1 месяц + 1 месяц). Можно "сохранять" число при обрезании, но тогда повляются "странные" элементы: 28</28> февраля, 28</29> февраля, 28</30> февраля, 28</31> февраля (да и стремление максимально узаконить такие операции — странная, так как есть же ещё разные календари со свойствами транзитивности — всё это учитывать… непонятно зачем).
Так что. Не факт, что это ошибка. Возможно именно это и есть ожидаемое поведение, проблема в том, что ожидаемых поведений может быть много. И вот здесь уже калькулятор плох даже тем, что просто определяет операцию даже не уточняя, а что он имеет ввиду.
manyakRus
19.06.2019 14:13+1"… Не факт, что это ошибка"
— такая же команда есть в языках программирования, например в 1С Предприятие.
Лень проверять, но думаю что 1С скажет что это точно ошибка :)Deerenaros
19.06.2019 15:24Лучше проверить. Я проверил на python — у него в timedelta нельзя определить именно месяц. А вычитание конкретных дат приводит к конкретному количеству дней.
Eldhenn
19.06.2019 14:36> Математически операция складывания месяцев с конкретным числом — это бред
А «следующая зарплата через месяц после 30 января» — тоже бред?InChaos
19.06.2019 14:43+1С обычной человеческой точки зрения это нормально, 5 февраля + месяц = 5 марта, и даже неважно високосный год или нет.
Но с математической это действительно бред, т.к. понятие месяц = неизвестная величина (28, 29, 30, 31), и в этом случае можно прибавлять только кол-во дней или недель (часов, минут и т.д.), т.е. строго детерминированные величины.Goron_Dekar
20.06.2019 08:07А что с человеческой точки зрения будет 31 января + месяц?
artoym
20.06.2019 09:18Если с точки зрения «месяцев», то «31 января» стоит читать как «конец января», значит через месяц — это «конец февраля», ну и это равно «28 (или 29) февраля».
Если же с точки зрения «дней», то надо определить значение «месяц» в днях. Думаю для большинства месяц = 30 дней, что даёт нам 1-2 марта.transcengopher
20.06.2019 15:35Не согласен.
Когда вы прибавляете к дате один месяц, то вы прибавляете следующий месяц, а не средний.
Потому, для 31 января длина следующего месяца не 30, а 28 (29) дней, и именно 28 (29) дней и следует прибавить. Получим 28 (29) февраля.Lissov
20.06.2019 16:16то вы прибавляете следующий месяц
Или же текущий. 1 июня + 1 месяц — я ожидаю 1-е июля, а не 2-е (в июле 31 день, в июне 30).ainoneko
20.06.2019 17:05«Прибавить длину текущего месяца (будет то же число месяца, если оно есть в следующем месяце), но остановиться на последнем дне при переполнении»?
Lissov
20.06.2019 18:35Вообще мне кажется логичным проинформировать пользователя. Например, надписью «результат был скорректирован» и явно описать, в чём проблема. Выдавать возможно неверный результат как будто он правильный — не лучшая идея.
Всё таки стоит исходить из того, что нет однозначно верного ответа, что такое «через месяц после 31 января». И так как калькулятор не может знать, что ожидает пользователь, любой ответ может оказаться неверным для задачи пользователя.
Deerenaros
19.06.2019 15:22Может показаться странно, но я ни разу не слышал — через месяц после 30 января. Через месяц — да, но это подразумевает уже само по себе ± пара дней. Но вообще контекст в таких вещах плохо работает и часто уточняют — в конце контекст.следующий_месяц, и даже в этом случае 146% переспросят "то есть в январе?". А именно такая формулировка встречается разве что в анекдотах про математиков (и задачах по спортивному программированию).
FYR
19.06.2019 15:50Нет не бред, потому что используют не просто «месяц», а «календарный месяц». Что уже сложнее ибо привязанно к календарю. И да скорее всего 28.01 +1 месяц = 28.02 а [29-31].01 + 1 месяц = 01.03
eranthis
19.06.2019 17:33В операциях с месяцами основная ошибка — это попытка «взвесить» месяц в днях, что по определению невозможно, да и в корне неверно. Чтобы избежать неоднозначности, этого делать не нужно. В этом плане, очень правильно эта математика реализована, например, в PostgreSQL. Чтобы понять логику прибавления месяцев, проще всего взять пример зарплаты. Если заплата выплачивается каждый месяц, то месяц не может быть пропущен по определению. Иными словами, при добавлении месяца к любой дате в январе должна быть дата в феврале, но никак не в марте. Отсюда правда вытекают такие порой неочевидные моменты, как например:
28/29/30/31 января + 1 месяц = 28 февраля
(дата + N месяцев) - N месяцев
не всегда равнодата
(дата + 1 месяц) + 1 месяц
не всегда равнодата + 2 месяца
и т.п.slonpts
19.06.2019 20:44В таком случае это очень плохая идея — называть такую операцию «сложением».
Потому что сложение коммутативно, ассоциативно и дистрибутивно (с умножением), и все слишком сильно к этому привыкли.
Лучше назвать ее как-нибудь по-другому, и не пытаться использовать здесь знаки "+" и "-", чтобы не вводить людей в заблуждение.tbl
20.06.2019 00:05но люди привыкли к дате прибавлять и вычитать периоды, поэтому "+" и "-", хоть это и не совсем те же ассоциативные операции
karavan_750
20.06.2019 02:08Неверно — это ожидать от операций с временными величинами результатов, как от обычных десятичных чисел.
slonpts
20.06.2019 03:11ОК, если ввести 2 типа time и timeinterval, то можно ввести операцию их сложения.
Тогда можно определить значения типа timeinterval: '1 second', '1 day', '1 week'.
Но нельзя определить значения '1 month', '1 year', '1 century'.
И снова все будет работать.eranthis
20.06.2019 13:46Почему нельзя определить месяц? Это единичный интервал, для которого год и столетие кратные. Выразить эти интервалы в днях нельзя, но это не мешает их определению.
kuza2000
20.06.2019 11:08Очень во многих языках операция конкатенации строк обозначается символом "+", что тоже ничего общего не имеет со сложением.
eranthis
20.06.2019 13:41Начнём с того, что не всегда. Второй момент, мы оперируем с разными величинами — датами и интервалами. Последние, в довершение всего, ещё и могут быть выражены в величинах, однозначно не приводимых друг к другу. А как называть/обозначать эту операцию — дело десятое.
KarasikovSergey
20.06.2019 10:32Математически операция складывания месяцев с конкретным числом — это бред. Без уточнений по крайне менее.
Не бред, а недостаточная проработка логики. Если уж введена сама возможность складывать месяцы, то при выполнении такой операции должны фоном проводиться проверки: заданное условие DD.MM.YYYY проверяется на високосность, месяц точки отсчёта, таким образом прибавка 3х месяцев учитывает календарный состав следующих за заданным трёх месяцев и система точно знает — сколько там на самом деле дней. Это не так сложно, календарь всегда доступен для обращения за актуальными данными.
vlivyur
20.06.2019 11:28Когда дело касается дат и времени, ожидаемо что правила математики перестают работать. Можно ещё вспомнить что к дате прибавить 1 год 1 месяц и 1 день не то же самое, что к той же дате прибавить 1 месяц 1 день 1 год.
ksr123
20.06.2019 23:21Тут важно, для чего считать. Месяц на бытовом уровне может быть примерным, особенно если речь идет о десятилетия.
А если о платежах каких-то — то может быть важен каждый день.
KvanTTT
19.06.2019 13:22-2Но это всегда может быть какой-то перевёрнутый бит каким-то высокоэнергетическим лучом от какого-то дружественного космического соседа.
Я конечно понимаю, что это шутка, но вероятность того, что из-за этого повредится какой-то бит в памяти компьютера крайне мала, не говоря о вероятности повреждения конкретного. Скорее всего произойдте просто синий экран и компьютер перезгарузится. Ну или в какой-нибудь картинке пиксель или блок пикселей неправильно отобразится.
InChaos
19.06.2019 14:38+1В расчете на один бит вероятность очень мала, а если взять сервер с терабайтами памяти то вероятность далека не такая маленькая, поэтому и используют ECC память. Насчет синего экрана, то как раз процент занимаемой памяти процессами очень мал, относительно данных на таких огромных объемах, и скорее подпортятся данные, чем использующий их процесс.
Исследования, проведенные IBM в 1990-х годах, показывают, что компьютеры обычно испытывают около одной ошибки, вызванной космическим лучом, на 256 мегабайт оперативной памяти в месяц.KvanTTT
19.06.2019 15:22Ок, наверное был не прав. Кстати, на хабре уже была статья про статистику отказов в серверной памяти.
Lissov
20.06.2019 16:14Была даже статья (не могу найти) с переводом доклада с конференции, где это использовалось как реально работающий метод взлома. Зарегистрировать кучу похожих на google доменов с отличием в 1 бит, и из миллиардов запросов некоторые попадают.
izuware
19.06.2019 15:30+1Лично видел 2 + 2 = 5. В прошлом веке на 286м процессоре шлейфом от флоповода перекрыло вентилятор.
amarao
19.06.2019 15:58+1Вентилятор на 286? Откуда?
izuware
21.06.2019 10:29Точно помню кулер на процессоре, и рабочее место расчетника лимитов, на котором стоял писюк желтой сборки. А вот могли поставить чтото дороже 286/287 или нет — мой склероз сообщать отказывается.
Dmitri-D
19.06.2019 16:49даже если по мнению процессора 2 + 2 уже 5, сообщить об этом он не сможет, будет не в состоянии )))
CryptoPirate
19.06.2019 16:05Не совсем так. Есть атака RawHammer, есть ещё классная атака где в URL по одному биту меняют Bit-squatting. Ситуаций, когда меняется один бит очень много.
Dmitri-D
19.06.2019 16:45+1любопытно, а 29 февраля + 1год это будет 28е февраля?
alizar
19.06.2019 16:55Конечно!
developerxyz
19.06.2019 17:49Но тут появляется другая ошибка — ошибка локализации. Не «365 дни», а «365 дней».
danghyan
19.06.2019 18:501. Калькулятор нужен что бы работать на человека, а не на машину. Поэтому угождать надо человеку и 5 мая + месяц должно быть 5 июня… В целом математическую операцию +месяц в программировании можно свести к: берем число месяца, прибавляем 1, вставляем обратно.
2. В разнице между дат в программах всегда надо учитывать что именно за даты, какого года. Дальше уже понятно что +30 дней это +30 дней, а плюс месяц это плюс месяц…
В целом очень молодец.masai
19.06.2019 20:16+1Поэтому угождать надо человеку и 5 мая + месяц должно быть 5 июня…
А чему в таком случае будет равно «31 января + 1 месяц»?
TRTHHRTS
19.06.2019 20:4731 февраля, очевидно.
И срабатывает валидация результата, которая говорит, что как-бы нет такой даты.
begin_end
19.06.2019 20:59Тогда должна действовать поправка: если такого числа в новом месяце нет, берем раннее ближайшее существующее. Кстати, с +год для високосных и февраля то же нужно.
Osnovjansky
20.06.2019 09:01Выше уже обсудили, что как правило, 31 января + 1 месяц = 28/29 февраля, в зависимости от года.
Куда интереснее, чему должно быть равно (31 января + 1 месяц) + 1 месяц. Нужно ли помнить предысторию получения текущего значения даты.yea
20.06.2019 10:58(31 января + 1 месяц) + 1 месяц = 28/29 марта
31 января + (1 месяц + 1 месяц) = 31 января + 2 месяца = 31 марта
Достаточно логично, как по мне. Ассоциативность придётся закопать, конечно, но у нас ведь тут особое сложение.Osnovjansky
20.06.2019 12:38Да, пожалуй, хотелось бы видеть именно 31 марта, но если, например, плюсуем по месяцу в цикле, то при наивной реализации получим 28 марта. Я пытался обратить внимание на это
И да, я согласен, что вешать такое поведение на стандартный оператор сложения — готовить почву для багов
masai
20.06.2019 12:11Выше уже обсудили
Ага, вижу. Я просто задал вопрос, когда обсуждения ещё не было.
как правило, 31 января + 1 месяц = 28/29 февраля, в зависимости от года.
Куда интереснее, чему должно быть равно (31 января + 1 месяц) + 1 месяц. Нужно ли помнить предысторию получения текущего значения даты.К этому я и вёл. :) То, что прибавить 2 месяца и два раза прибавить месяц — это разные вещи, может привести к трудноуловимым багам. Если уж делать так, то не называть операцию сложением.
stuq1
19.06.2019 22:06+1В старом калькуляторе из Windows 7 есть аналогичный баг, но выдается немного иной ответ
Изображениеred_andr
19.06.2019 23:17Ох уж эти вечные проблемы с годом в 365,2425 дней. Когда я работал с климатическими моделями мы просто использовали 360-дневный год. 12 месяцев по 30 дней и всё. Красота!
Porohovnik
20.06.2019 00:04+2А почему некто не догадался представить мечюсяцы как замкнутый двухсторонний цикл?
И прибавление месяца, это просто переход на новое значение в списке…
С годом тоде самое, но список не замкнутый
Пример 1:
10 февраля 2018
- 5 месяцев
должно работать вот как:
Сохраняем в буфер количество дней
Передвигаем замкнутый список месяцев на 5 позиций возвращаем дни.
Пример 2:
10 февраля 2018
- 5 месяцев 25дней
Повторяем все что в примере 1, а дальше
Сохраняем сумму дней в переменную
После чего
Вытягиваем из двух связного списка количество дней в получившимся месяце
И вычитаем из получившегося дней, если разность больше 0 переключаем месяц и добавляем разность, если меньше просто добавляем сумму дней
Вроде всё логично кроме одного понятия: сначала прибавлять дни, а уже потом месяцы или наоборот?
Лучший вариант-сделать галочку, что переключает это состояние- 5 месяцев
prostofilya
20.06.2019 06:14А в чём проблема брать количество дней тех месяцев, которые мы складываем?
Пример: 20 июня 2019 + 3 месяца.
1) Берём текущий месяц + 2 следующих (июнь, июль, август).
2) Берём количество дней в этих месяцах и складываем (30 + 31 + 31, 92).
3) Прибавляем количество дней к дате отсчёта, предварительно преобразуя начальную дату в дни (01.01.1970), смотря как в каком ЯП реализована работа с датами.
4) Преобразуем кол-во дней в дату.
tuxi
20.06.2019 06:40Уххх, а еще есть боль от номеров недель. "Надо сравнить продажи за 2 года с группировкой по номерам недель. ....wtf!!! почему один год у тебя начинается с последних чисел декабря предыдущего, а другой не с первого января????" :)
prostofilya
20.06.2019 07:44Что-то не совсем понял, какие могут быть проблемы с номерами недель?
m1rko Автор
20.06.2019 07:50Предположим, что неделя должна начинаться с понедельника. Тогда если год начинается посреди недели, то первая неделя года начнётся в прошлом году. То же самое с месяцами. Первая неделя месяца начинается в прошлом месяце, отсюда и проблемы.
prostofilya
20.06.2019 07:56Ну это уже головная боль того, кто ставил задачу.
tuxi
20.06.2019 08:09Не совсем. «Бизнесу» часто нужно сравнить продажи по производственным неделям, но в рамках календарного года. И вот тут начинается самое интересное.
prostofilya
20.06.2019 08:20Как бы там бизнесу не было нужно, календарь не пододвинется. Если аналитики не могут в анализ информации и в грамотную постановку задач, хотя бы опуститься с недель до дней недели на необходимых участках, то это всё же их вина/проблема.
alizar
20.06.2019 08:17Ну он ставит задачу, типа, «Сравнить показатели первой недели месяца с предыдущим»
prostofilya
20.06.2019 08:25Я обязательно уточню что считать первой неделей месяца. Считаем с первого понедельника месяца? -Ок, не проблема.
alizar
20.06.2019 08:45+1При суммировании финансовых результатов всех недель месяца и всех дней месяца получатся разные цифры. Бизнес может выдвинуть претензии, что ваша программа глючная.
prostofilya
20.06.2019 08:58А это та контора, где надо семь перпендикулярных линий прозрачным маркером нарисовать? У меня шмотки собраны, резюме разослано.
tuxi
20.06.2019 09:40Вы слишком категоричны. Данная закавыка может возникнуть в любой конторе, в любой стране и никто не говорит, что она не решаема. Я говорю, что она решается через некоторую боль и страдания.
И разработчик все же должен включать мозги, не имплементировать тупо по "наиподробнейшему" ТЗ. Иначе это мартышка, а не девелоперprostofilya
20.06.2019 10:09+1Разработчик должен включать мозги, но если разработчик не понимает бизнес-процесс, то он должен довериться тому, кто поставил задачу. А если логика бизнес-процесса была донесена ему неверно и все уточняющие вопросы от разработчика игнорировались, то это не вина разработчика.
ainoneko
20.06.2019 17:10+1"Девятый вагон — это тот, который сразу после восьмого, а не тот, который перед десятым".
tuxi
20.06.2019 22:12Очень точная характеристика ситуации, как я мог забыть эту эпохальную вещь :) прям в точку!
Kemet
20.06.2019 10:24Есть стандарты
astro.ins.urfu.ru/sites/default/files/chair/study/docs/gost_8601-2001.pdf
dotat.at/tmp/ISO_8601-2004_E.pdf
и более новые
Odrin
20.06.2019 11:19Первая неделя года — это неделя, которая включает в себя 4 января (в странах где неделя начинается с понедельника).
tuxi
20.06.2019 11:45У нас в стране есть такое понятие как «производственный календарь»
В его контексте, бывают не только недели которые начинаются с прошлого года, но даже года с разным кол-вом этих самых недель.
Эта тема не столько однозначная как может показаться на первый взгляд.
mikserok
20.06.2019 07:50У меня на семерке работает без аномалий — 5 месяцев и 6 дней. Видимо у автора виндовс 10.
Skerrigan
20.06.2019 07:53Оффтоп не по темеЭх, прекрасное стекло в интерфейсе. Как же я тоскую по нему…anton9843
20.06.2019 08:54+2По идее между 31.07 и 30.12 не должно быть больше 5 месяцев
Если по человечески подумать, то 31.07 + 5 месяцем, это 31.12,
а тут 30.12. По мне это 5 месяцев без одного дня,
а не 5 месяцев и 6 днейunC0Rr
20.06.2019 11:23Кроме того, 152 дня никак не соответствуют 5 месяцам и 6 дням. В месяце получается 29,2 дня в таком случае.
developerxyz
21.06.2019 11:04Как раз 5 месяцев 6 дней — аномалия.
31.07.19 + 5m6d = 06.01.20
30.12.19 - 5m6d = 24.07.19
Alex023
20.06.2019 12:05Да у калькулятора и перевод на русский знатный: From — От, To — КОМУ.
КОМУ, Карл! :)
Eldhenn
20.06.2019 13:40И до сих пор никто не вспомнил про таймзоны, летнее время, советский революционный календарь и особенности счёта времени при движении вокруг света. А кто-то даже до сих пор настолько наивен, что недели, дни, часы и минуты считает точно определёнными и неизменными величинами.
transcengopher
20.06.2019 15:24В Java:
var first = LocalDate.of(2019, JULY, 31); var second = LocalDate.of(2019, DECEMBER, 30); print(Period.between(first, second));
ВыведетP4M30D
, что очень близко с результатом после починки (т.к.P30D
равенP4W2D
). Может это и «неправильно» — но неправильно скорее думать об этой операции как о каноничном сложении. Зато теперь калькулятор выдаёт тот же ответ, что многие другие приложения (в частности, написанные на Java, да).
sergey-b
22.06.2019 15:55Смотрите, похоже, с годами тоже аналогичная проблема проявляется
28.02.2016
vlivyur
22.06.2019 16:55Вроде всё правильно.
developerxyz
22.06.2019 17:52Тогда от 28 февраля 2016 года до 29 февраля 2016 года всего 0 дней, таким образом мы доказали, что 28 и 29 февраля 2016 года — один и тот же день.
inoyakaigor
Когда исходники были закрытыми подобные посты читать было интереснее. Раньше ведь как:
1) Присоединился дебаггером к процессу
2) *какая-то магия с ассемблером*
3) Профит!
А сейчас что? Скачал исходники, поставил току останова и отладил. Скукота!
developerxyz
Так ведь исходники могут не совпадать с версией, которая уже установлена.
Компилировать чужие исходники (иногда свои) бывает очень трудно.
И надо знать, куда ставить breakpoint.
inoyakaigor
Я писал это с некоей долей иронии если что)
Alexey2005
Да зачастую в дебаггере с дизассемблером баг найти не в пример проще, чем в исходниках. Которые разбиты на 100500 файлов, и алгоритм размазан по нескольким десяткам из них. Вот так смотришь, что куда передаётся — а там интерфейсы поверх интерфейсов, и тонны абстрактных фабрик фабрик, за которыми понять, как и где конкретно происходит собственно расчёт, не так-то просто. Создаётся впечатление, что код на 99% состоит из «воздуха», который реально ни во что не компилируется.
barbanel
У меня впечатление что этот код не только компилируется, но еще и пару миллионов пустых циклов добавляет.
Victor_koly
Калькулятор Win XP мог посчитать 250000! (по логике — суммирование логарифмов с достаточной точностью). Но функции типа a^b считал до куда меньшего предела.
А вот в Win 7 уже не воспринимает результаты размером 1010000 и более.
ainoneko
vlivyur
Думаю у этого калькулятора и внутри ассемблерного кода фабрика фабрик с воздухом внутри.
undbsd
ну почему сразу «какая-то магия», просто переписал калькулятор с нуля и готово :D