Ошибка в вычислениях в модуле DateUtils
, Ошибка вычисления времени между переменными типа TDateTime
![]() |
Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
|
| ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
| [216.73.216.81] |
|
|
ПРАВИЛА РАЗДЕЛА · FAQ раздела Delphi · Книги по Delphi
Ошибка в вычислениях в модуле DateUtils
, Ошибка вычисления времени между переменными типа TDateTime
|
Сообщ.
#1
,
|
|
|
|
Всем доброго времени суток.
Обнаружил интересную ошибку(может фича такая) при вычислении количества часов, минут, секунд между датами и временем... использовал функции из модуля DateUtils: HoursBetween, MinutesBetween, SecondsBetween. var D1,D2: TDateTime; begin D1 := StrToTime(Edit1.text); D2 := StrToTime(Edit2.text); Edit3.text := IntToStr(SecondsBetween(D1,D2)); end; например, если D1 = 13:00:00, а D2 = 14:00:00, то все нормально(3600 секунд между ними) а если D1=17:00:00, а D2 = 18:00:00 , то между ними 3599 секунд. Соответственно функция HoursBetween выдает 0 часов. И как с этим работать дальше ??? Кто сталкивался - пишите варианты решения... |
|
Сообщ.
#2
,
|
|
|
|
Пошарил по коду DateUtils и получается, что ошибка закрадывается при отнимании от одной даты другой...
вот тут: function SpanOfNowAndThen(const ANow, AThen: TDateTime): TDateTime; begin if ANow < AThen then Result := AThen - ANow else Result := ANow - AThen; end; В другом месте ошибке взяться вроде неоткуда... |
|
Сообщ.
#3
,
|
|
|
|
Во-первых, глянь в описание - SecondsBetween возвращает целое число секунд в интервале, т.е. не округляет, а обрезает рез-т до целого (несмотря на остаток миллисекунд 0.999..). Во-вторых, время в TDateTime хранится в виде дробной части суток в формате double и соот-но, как при любой работе с вещественными числами, при представлении дробей могут возникать "микроскопические" погрешности, связанные с ограниченной разрядностью представления мантиссы числа. Вот у тебя и получается разница в 3599.999.., которую SecondsBetween обрезает до целого числа 3599. Если тебе нужно не обрезать, а округлять секунды, то юзай
round(SecondSpan(D1,D2)); |
|
Сообщ.
#4
,
|
|
|
|
Да про Round() я уже думал.
Вообще непонятно как с модулем раньше работали и работают, если он точно ничего не определяет... Ведь как панацея на форумах пишется. Я уже и код под это дело набацал... Сейчас все переделывать придется На PHP и Perl я с таким не сталкивался.. Добавлено Вердикт однозначный. Для расчетов с временем использовать модуль DateUtils в принципе нельзя. Точности нет. И это в датах-то... Выход нашел. Надо было количество часов получать между датами. Получаю округленное количество секунд round(SecondSpan(D1,D2))и делю на 3600. Лучшего пока-что не придумал. А функцию HoursBetween для этого не подходит. |
|
Сообщ.
#5
,
|
|
|
|
Цитата agesandr @ Вообще непонятно как с модулем раньше работали и работают, если он точно ничего не определяет... Справку нужно внимательно читать |
|
Сообщ.
#6
,
|
|
|
|
Читал справку и примеры смотрел.
Вот сколько часов должно быть между 17:00:00 и 18:00:00?? Один час вроде бы... но никак не ноль. Глючные функции и модуль. |
|
Сообщ.
#7
,
|
|
|
|
Сам неоднократно сталкивался с такими "глюками". Увы, ничего лучше с существующим видом TDateTime придумать нельзя
. Вот если бы оно было по принципу FILETIME (два DWORD), тогда всё было бы наишоколаднейше. А так - приходится извращаться. Добавлено Цитата agesandr @ Глючные функции и модуль "Глючное" само представление. Вернее, это багофича, а еще вернее - фича, которая приводит к багам. |
|
Сообщ.
#8
,
|
|
|
|
Время это в принципе вещь неточная и приблизительная, тем более когда речь идет об округлении к часам, минутам и т.п. И круглые цифры типа 17:00:00:000 и 18:00:00:000 - это чистые абстракции, которые можно получить только через EncodeTime, а любое реальное время всегда определяется с некоторой погрешностью и соотв-но работать с ним нужно по правилам вещественных чисел - через round-ы и т.п. А если тебя это не устраивает, то и незачем юзать вещественный тип TDateTime - работай с целочисленными TSystemTime и TTimeStamp
|
|
Сообщ.
#9
,
|
|
|
|
Цитата leo @ Время это в принципе вещь неточная и приблизительная, тем более когда речь идет об округлении к часам, минутам и т.п. И круглые цифры типа 17:00:00:000 и 18:00:00:000 - это чистые абстракции, которые можно получить только через EncodeTime, а любое реальное время всегда определяется с некоторой погрешностью и соотв-но работать с ним нужно по правилам вещественных чисел - через round-ы и т.п. А если тебя это не устраивает, то и незачем юзать вещественный тип TDateTime - работай с целочисленными TSystemTime и TTimeStamp Вот за TSystemTime и TTimeStamp спасибо огромное ))) Совсем про них забыл ... Помнил только TDateTime... Вот что значит много на PHP работать ))) Буду дальше колупать... |
|
Сообщ.
#10
,
|
|
|
|
Резюме - дело не в модуле DateUtils, а в представлении TDateTime в виде вещественного числа double, в котором невозможно абсолютно точно представить казалось бы "круглые" цифры типа 17:00:00. Тех, кто это понимает и учитывает, вещественный формат вполне устраивает. И модуль DateUtils сделан с учетом этого: хочешь обрезать дроби - юзай функции ..Between, хочешь с дробями - юзай ..Span и дальше по усмотрению round, trunc или ва-аще sin\cos
|
|
Сообщ.
#11
,
|
|
|
|
Цитата agesandr @ а если D1=17:00:00, а D2 = 18:00:00 Приведи точное время, хотя бы с точностью до микросекунды, а еще лучше все 15 знаков. |
|
Сообщ.
#12
,
|
|
|
|
Цитата Anatoly Podgoretsky @ Цитата agesandr @ а если D1=17:00:00, а D2 = 18:00:00 Приведи точное время, хотя бы с точностью до микросекунды, а еще лучше все 15 знаков. Да с этим все понятно. Просто думал удобнее будет с TDateTime работать и модулем DateUtils... А оказалось проще делать все с TTimeStamp все делать... Давно просто не работал с Delphi и многое забыть успел ))) |
|
Сообщ.
#13
,
|
|
|
|
Цитата Anatoly Podgoretsky @ Приведи точное время, хотя бы с точностью до микросекунды, а еще лучше все 15 знаков А зачем, если и так "с этим все понятно" ![]() 18:00 как и все часы, кратные 3-м, представляются точно максимум 3-мя двоичными разрядами, т.к. 3*k/24 = k/8, где 8 = 23 А все часы, не кратные 3-м, будут иметь делитель = 3, который и в десятичной, и в двоичной системе будет давать бесконечную периодическую дробь, например, 17/24 = 0.7083(3) = 0.$B5(5) |
|
Сообщ.
#14
,
|
|
|
|
Цитата agesandr @ Просто думал удобнее будет с TDateTime работать и модулем DateUtils... А оказалось проще делать все с TTimeStamp все делать... Давно просто не работал с Delphi и многое забыть успел ))) Конечно проще с TDateTime, но надо учитывать его относительную точность, ведь реальный тип это Double, а прочие типы использовать только при крайней нужде. |