Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > Java: Общие вопросы > Стандартные календарные расчеты |
Автор: arcsupport 7.1.2012, 22:35 |
Существуют ли стандартные (встроенные в JRE) классы, объекты и методы, которые позволят рассчитать количество лет, месяцев, недель и дней между двумя датами по грегорианскому календарю? По юлианскому календарю? |
Автор: jk1 8.1.2012, 10:17 | ||
Это как бы основа
А конкретный тип календаря определяется конкретным наследником java.util.Calendar. Например там есть GregorianCalendar. |
Автор: arcsupport 8.1.2012, 11:24 |
jk1, меня больше интересует не общее количество дней, а количество лет, месяцев, недель и дней между двумя датами. |
Автор: Stolzen 9.1.2012, 14:56 | ||||
arcsupport, можно примерно так
Оно? Добавлено через 5 минут и 2 секунды Года не добавлял, т.к. они могут быть високосные и невисокосные. Если это не важно, то в массив MILILIS можно добавить на первое место еще одну константу MILLIS_PER_YEAR = 355 * MILLIS_PER_DAY; и в MessageFormat.format("{0} days, {1} hours, {2} minutes", res); добавить {0} years |
Автор: jk1 9.1.2012, 16:39 | ||
Не надо изобретать велосипедов. Класс TimeUnit, о котором я говорил выше, умеет выполнять преобразования единиц времени. В данном случае это будут преобразования милисекунд в минуты, часы, дни. |
Автор: Stolzen 9.1.2012, 17:00 |
Преобразование он делать умеет, а вот делить с остатком - нет |
Автор: jk1 9.1.2012, 17:09 | ||||
Умеет
дает в результате 1 |
Автор: Stolzen 9.1.2012, 17:09 |
А остаток? |
Автор: jk1 9.1.2012, 17:32 | ||
Вуаля
|
Автор: Mirkes 10.1.2012, 17:42 | ||
По моему умудренные советчики не поняли вопроса. Вопрос в исчислении стажа того или иного вида. То есть следует указать сколько лет, месяцев и дней прошло от одной даты до другой. Нельзя говорить 100 дней, нужно сказать три месяца и ? дней. С этого момента начинаются неприятности при подсчете стажей, поскольку разные месяцы содержат разное число дней. Далее вопрос о том, какая точность нужна. Ниже пример для грубого подсчета без делений, а средствами стандартных библиотек. Еще ниже (по тексту) пример точного подсчета. Текст не оптимизирован. Если задача регулярная, то стоит написать отдельную функцию и почистить код.
пример вывода 1257 вариант jk1 - просто число дней. 11 days 5 month 3 years грубый расчет. 9 days 5 month 3 years точный расчет Забавно, но куда-то потерялось два дня. Я думал что разность будет в один день. |
Автор: arcsupport 10.1.2012, 18:13 |
Mirkes, спасибо. |
Автор: Dummy 10.1.2012, 22:09 | ||
Mirkes, в вашей реализации я вижу пару моментов, с которыми хочется поспорить.
На моей машине и с моим самопальным тестом (не претендующим на особую прецизионность), скорость вычисления увеличивается раза в полтора-два. |
Автор: Mirkes 11.1.2012, 03:22 | ||
Есть подозрение, что вот этот фрагмент внесет погрешность. К сожалению пора на работу и нет времени проверить. Как только буду готов - напишу. По поводу эффективности алгоритмов - я уже написал, что алгоритм не оптимизирован. Успел набросать на основе метода Dummy свой метод.
Нужно провести тест для проверки возникновения различий. Разница точно возникла, но мое время истекло. Вернусь - проверю что правильно. |
Автор: Mirkes 11.1.2012, 13:29 | ||
Принял экзамен, теперь можно сдавать экзамен на владение датами. В ходе ряда экспериментов установил, что пользоваться функцией TimeUnit.DAYS.convert для вычисления числа дней надо предельно акуратно, поскольку она имеет дурную привычку учитывать переоз на зимнее/летнее время, даже если его не было. Или перевод времени можно отключить? Сравнение FastDiff Dummy, и моего stag в следующей редакции:
Сравнение производилось следующим способом. Начальная дата выбиралась и задавалась вручную. В качестве конечной даты рассматривались все даты начиная с 11.01.2012 в течении 370 дней - чуть больше года. Все совпало при дате начала 01.08.2008. Различия установлены в следующих случаях: стартовая дата 02.08.2008 отличия в случаях (плюс по одному случаю совпадения) Дата конца FastDiff Dummy Мой stag 29.02.2012 27 days 6 month 3 years 27 days 6 month 3 years 01.03.2012 30 days 6 month 3 years 28 days 6 month 3 years 02.03.2012 0 days 7 month 3 years 0 days 7 month 3 years 30.04.2012 28 days 8 month 3 years 28 days 8 month 3 years 01.05.2012 30 days 8 month 3 years 29 days 8 month 3 years 02.05.2012 0 days 9 month 3 years 0 days 9 month 3 years Дальше видимо можно не продолжать. Вопрос о том, что есть правильно, конечно спорный, но вывод моего метода мне гораздо приятней. Если это нужно, то описание расчетов разницы дат можно добавить в статью по работе с датами в Java в FAQ |
Автор: jk1 11.1.2012, 15:01 | ||
Это определяется установленой TimeZone, конкретно её параметром DST. Не уверен, вышел ли уже патч на отмену перехода на летнее(или зимнее, не помню) время. В качестве быстрого фикса я как-то раз подбирал зону UTC+4 без DST, результат совпадал с часами на стене. Но такой хак уберет переход вообще, то есть время в прошлом (когда переход еще имел место быть) может считаться ошибочно. |
Автор: Dummy 11.1.2012, 16:16 | ||
Поправил свой метод подсчета (который по историческим причинам носит название fastDiff ![]()
|
Автор: Mirkes 11.1.2012, 19:31 |
Полностью согласен ![]() |
Автор: jk1 11.1.2012, 20:39 | ||
Просто для сравнения приведу пример, как сделать тоже самое библиотекой Joda Time
Почувствуйте разницу) |
Автор: Dummy 12.1.2012, 00:25 |
Круто ![]() ![]() Добавлено @ 00:30 Не знаю, часто ли бывает полезен полученный результат в других задачах, помимо расчета стажа ![]() Оффтоп: попытался плюсануть участникам дискуссии за полезный тред. Но что-то плюсовалка не работает... |
Автор: jk1 12.1.2012, 08:32 | ||||||
Топикстартер хотел только стандартными средствами. Даже жирным шрифтом выделил в первом посте.
Не знаю, как именно Вы меряете, но с динамически компилируемыми языками не все так просто. У меня как раз завалялась http://www.ibm.com/developerworks/ru/library/j-jtp12214/index.html
Репутация на форуме совсем отвалилась: ни посмотреть, ни плюсануть( |
Автор: Mirkes 12.1.2012, 09:18 | ||
Провел полный тест по точности. Ниже код тестера (возможно не самый оптимальный, но вплне соответсвующий требованиям ![]()
Вывод организован так, чтобы при простом копировании в Excel получать внятную таблицу Результаты сравнения При началной дате 01.08.2008 Все результаты, кроме грубых совпали. При начальной дате 02.08.2008 ошибка в выводе fastDiff для 01.08.2012: 29 дней 11 месяцев 4 года. Правильный ответ 30 дней 11 месяцев 3 года. При начальной дате 02.08.2008 ошибка в выводе fastDiff для 1 и 2 августа 2012. Число дней на 1 меньше, а число лет на 1 больше. При всех начальных датах до 29.08.2008 ошибка в выводе fastDiff для дней с 01 по дату начала-1 августа 2012. Ошибка та же. Решил перепроверить последнюю версию fastDiff. Все точно. По крайней мере я ошибок не нашел. В чем глюк - не знаю. При начальной дате 30.08.2008 число ошибок возрастает. неправильно подсчитано числ дней за все дни с 01.03.2012 по 29.03.2012 и с 01.08.2012 по 29.08.2012. При начальной дате 30.08.2008 возникло одн разночтение между stag и Joda Time: 29.02.2012 stag вывел 30 дней 5 месяцев и 3 года, а Joda Time - 0 дней 6 месяцев и 3 года. Что из этих ответов считать правильным весьма спорный вопрос. При начальной дате 31.08.2008 fastDiff врет в числе дней во всех месяцах, которые короче 31 дня. Разночтения между stag и Joda Time возникают в последний день каждого месяца, который короче чем 31 день. Если разночтения между stag и Joda Time носят спорный характер, то с fastDiff нужно разобраться. Вернусь с работы - попробую выяснить причину. |
Автор: Dummy 12.1.2012, 11:15 | ||||
Знаю. Недавно читал вот такой замечательный материал: http://people.apache.org/~shade/talks/j1-April2011-benchmarking.pdf. Поэтому постоянно говорю, что мои подсчеты, скорее всего, грубоваты, хотя перед тестами я прогреваю JVM.
Спасибо за тщательность! Поковыряю код, как будет время. |
Автор: Stolzen 12.1.2012, 11:32 |
Ага, что-то шаманят... |
Автор: Mirkes 12.1.2012, 12:43 | ||||
В одном месте был мой глюк - перепутал дни и годы ![]() Второе чудо - свойство Calendar при установке, например, 30 февраля. Он не ругается, но и не ставит ![]() Чуть-чуть подшаманил код, теперь все работает одинаково, за исключением спорного момента по поводу 30 дней 2 месяцев 3 лет и 0 дней 3 месяцев 3 лет. Вот окончательный код fastDiff
По поводу скорости. Сначала прочитал предложенные Вами статьи. Это оказывается одна статья. Подготовил тестер вроде бы нормально. Проведу тестирование - напишу. |
Автор: Dummy 12.1.2012, 13:35 | ||
Да, вы меня опередили. Я тоже нашел это и уже писал гневный и полный язвительности ответ про неравную конкуренцию и жалобу в антимонопольный комитет ![]()
Это чудо называется lenience. Контролируется через Calendar.setLenient(). Когда lenient = true (для Calendar это значение по умолчанию), календарь автоматически пытается транслировать некорректные даты. Например, 35 декабря 2008 года будет переведено в 4 января 2009. А 31 февраля 2012 - во 2-е марта 2012. Естественно, в fastDiff() это приводило к сползанию месяцев и чисел и краху моего алгоритма в ряде случаев. Поэтому лучше явно выставить при таких расчетах Calendar.setLenient(false) для всех используемых календарей. Тогда при установке неправильной даты мы сразу получим IllegalArgumentException. |
Автор: Mirkes 12.1.2012, 14:45 | ||
Подвел результаты теста на скорость. Для разогрева JVM использовал предыдущий вариант сраввения расчетов по всем четырем методам за год run(). в качестве самого теста - следующий код speed().
Результат обработал статистически. Стандартное отклонение менее 2% от средней величины. Расход времени на один расчет в мс: robust fastDiff stage joda 231 433 430 276 Так что по моим данным Joda Time работает на треть быстрее fastDiff и лишь чуть медленнее чем robust. Так что выбор достаточно прост: Если отклонение в пару дней роли не играет - используем грубый метод. Если нужно быстро и точно - Joda Time Если строгое ограничение - стандартные библиотеки и нужна точность - fastDiff. Теперь видимо окончательно все. ![]() ![]() |
Автор: arcsupport 12.1.2012, 21:16 |
Mirkes, не могли бы Вы, пожалуйста, привести аналитические выражения и численный пример для выполненных Вами статистических обработок результатов. |
Автор: Mirkes 14.1.2012, 10:43 | ||
По сути ничего особого не делал. Взял результат выполнения программы приведенной выше. Для чистоты эксперимента прилагаю полностью текст программы
Время счета на моем буке составило 20 минут. Дальше в MS EXCEL отсортировал вывод по первому полю и отделил выводы всех методов друг от друга. Для результатов каждого метода выполнил следующую обработку: 1. Отсортировал по времени выполнения. 2. Выкинул самый маленький и самый большой результаты. 3. Для оставшихся 100 значений рассчитал среднее и стандартное отклонение Собственно и вся обработка ![]() Каждого было по 102 штуки. |
Автор: arcsupport 14.1.2012, 14:17 |
Mirkes, Вас не смущает, что формулы для расчетов стандартной ошибки и стандартного отклонения, реализованные в Excel, отличаются от традиционных русских формул, приведенных в учебниках? |
Автор: Mirkes 14.1.2012, 15:28 | ||||
Нет, не смущает. Во первых среднее там считалось так же, во вторых стандартное отклонение при 100 запусках будет отличаться от канонической формулы в корень из 99/100=0.9949 раза, что никак не влияет на корректность результата. Господа, большое спасибо за ссылку на статью по измерению скорости. Автор оказался парнем, которого я когда-то учил информатике в КЛШ. Мы с ним списались и он по собственной инициативе провел серию экспериментов с нашими методами. Его мнение и его результаты привожу ниже
Если кому-то интересно, логи могу выложить. Еще раз спасибо за ссылку. |
Автор: Mirkes 14.1.2012, 16:53 | ||||
Уточнил смысл приведенных величин и получил вот какую статистику
означает следующее
Результат убойный - почти с точностью до наоборот по отношению к моим выводам ![]() |
Автор: Dummy 15.1.2012, 21:48 | ||
Хм. Прочитав, сначала был эмоционально напуган, а потом возник вопрос:
- это все-таки величины в каких единицах? Количество отработавших вызовов за миллисекунду? |
Автор: Mirkes 16.1.2012, 18:46 | ||
Наконец разобрался с этими величинами и провел свои тесты. Результаты разборки я суммировал в следующей теме http://forum.vingrad.ru/index.php?showtopic=345406&view=findpost&p=2449161. Измеряется действительно число вызовов за милисекунду. Относительно наших методов мой результат следующий (в порядке убывания скорости) robust 0.780+-0.009 fastDiff 0.762+-0.004 joda 0.712+-0.005 stag 0.525+-0.002 результаты Алексея Шепилева абсолютно аналогичны. Так что объединенный разум VinGrad побил JodaTime! Ура Нам!! ![]() ![]() |