| 
| 
 | Вопрос # 5 370/ вопрос открыт / | 
 |  Здравствуйте!Проблема, вероятно, не из-за БД, а из-за говнокода, но всё может быть. Мне требуется сравнить 2 даты - одна из MonthCalendar, другую вытаскиваю из Access в переменную типа TDate. И на сравнение MonthCalender1.Date=AccessDate никакой реакции, хотя обе даты "29.05.2011". Только если поставить MonthCalender1.Date>=AccessDate, но это не подходит. Возиожно, дело в том, что Аксесс хранит ещё и время? Хотя это вряд ли
 
|  |   Вопрос задал: nickel-j (статус: Посетитель)Вопрос отправлен: 3 июня 2011, 14:04
 Состояние вопроса: открыт, ответов: 2.
 |  Ответ #1. Отвечает эксперт: min@y™ Всё понятно. TDate, TTime и TDateTime - это тот же Double. Сравнивать на равенство такие вещи нельзя, т.к. дробные числа хранятся в памяти в двоичном виде с погрешностью. Так что, если надо узнать, равны ли 2 даты, то:
 if Trunc(Date1) = Trunc(Date2)
  then ... 
|  | Ответ отправил: min@y™ (статус: Доктор наук)Время отправки: 3 июня 2011, 14:09
 Оценка за ответ: 5
 |  Ответ #2. Отвечает эксперт: Вадим К Здравствуйте, nickel-j!Посмотрите внимательней, что такое тип TDate - это обычное вещественное число. А их просто так сравнивать на равенство нельзя.
 В Вашем случае наиболее правильно сделать где то так
 abs(MonthCalender1.Date - AccessDate) < 0.1
 вместо проверки на равенство. константу 0.1 подбираете самостоятельно, Если там будут только даты записаны, то думаю 0.1 --- 0.01 будет предостаточно. разница в 1 - это один день.
 А вот Trunc, как  советует min@y™, может давать неверный результат в некоторых случаях. Ведь число 5 может быть записано как 4.999999 и trunc сделает с него 4. а может быть записано в виде 5.0000001 и trunc сделает 5.
 
|  | Ответ отправил: Вадим К (статус: Академик)Время отправки: 3 июня 2011, 14:14
 Оценка за ответ: 5
 |  
 Мини-форум вопросаВсего сообщений: 34; последнее сообщение — 7 июня 2011, 21:57; участников в обсуждении: 6. Страницы: [1] [2] [Следующая »]  
|   | min@y™ (статус: Доктор наук), 3 июня 2011, 14:23 [#1]: Цитата (Вадим К): А вот Trunc, как советует min@y™, может давать неверный результат в некоторых случаях. Ведь число 5 может быть записано как 4.999999 и trunc сделает с него 4. а может быть записано в виде 5.0000001 и trunc сделает 5. Интересно, в каких случаях при работе с TDateTime может возникнуть такая ситуация?
 Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп! |  
|   | Вадим К (статус: Академик), 3 июня 2011, 14:33 [#2]:Может быть. Совсем на ровном месте. Галочка "подтверждения прочтения" - вселенское зло. |  
|   | min@y™ (статус: Доктор наук), 3 июня 2011, 14:33 [#3]:Я постоянно пользуюсь такой конструкцией при сравнении двух дат, и она меня ни разу не подводила. В качестве примера я щас написал маленькую демку: 
 program p5370;
 
{$APPTYPE CONSOLE}
 
uses
  SysUtils;
 
const
  dt1 = 4.999999;
  dt2 = 5.000001;
  dt3 = 5.000100;
 
begin
  WriteLn(dt1: 9: 6, ' ---> ', DateTimeToStr(dt1));
  WriteLn(dt2: 9: 6, ' ---> ', DateTimeToStr(dt2));
  WriteLn(dt3: 9: 6, ' ---> ', DateTimeToStr(dt3));
 
  ReadLn;
end.Вот её консоль:
 
  4.999999 ---> 03.01.1900 23:59:59
 5.000001 ---> 04.01.1900
 5.000100 ---> 04.01.1900 0:00:08 2Вадим К: Заберёшь свои слова назад, как истинный джентльмен?
 Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп! |  
|   | min@y™ (статус: Доктор наук), 3 июня 2011, 14:35 [#4]: Цитата (Вадим К): Может быть. Совсем на ровном месте.  Приведи пример, пожалуйста.
 Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп! |  
|   | nickel-j (статус: Посетитель), 3 июня 2011, 14:51 [#5]:Спасибо! Поставил trunc - пока адекватен. Если начнёт тупить воспользуюсь abs |  
|   | Вадим К (статус: Академик), 3 июня 2011, 14:53 [#6]:А ты пару девяток добавь  у меня при 4.999999999 весьма интересные результаты  - "3-1-00 24:00:00" Ошибка может возникнуть, если начнем немножко делать рассчеты с датами. То есть, посчитать разницу между двумя датами, а потом эту разницу прибавить к третьей. В этих случаях может возникать ошибка округления, которая в конце концов может привести к весьма интересным результатам.
 Галочка "подтверждения прочтения" - вселенское зло. |  
|   | min@y™ (статус: Доктор наук), 3 июня 2011, 14:55 [#7]: Цитата (nickel-j): Поставил trunc - пока адекватен. Если начнёт тупить воспользуюсь abs  При работе с датами Trunc() тупить не начнёт. Из моего примера видно, что 4.999999 и 5.000001 - это разные даты, ведь дата - это целая часть дробного числа, как ни крути.
 Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп! |  
|   | Вадим К (статус: Академик), 3 июня 2011, 14:55 [#8]:to nickel-j: замечу - ошибка не будет возникать регулярно. Она будет возникать достаточно редко. Но будет ой как не приятно. Галочка "подтверждения прочтения" - вселенское зло. |  
|   | min@y™ (статус: Доктор наук), 3 июня 2011, 14:56 [#9]: Цитата (Вадим К): А ты пару девяток добавь Э, не, так не пойдёт! Ты сделай пример, чтобы даты были и на выходе, и на входе!
 Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп! |  
|   | Вадим К (статус: Академик), 3 июня 2011, 15:27 [#10]:Вот пример. может быть он немножко надуманный, но это как один с примеров. 
 program test;                                                                 
uses SysUtils, DateUtils;                                                     
var                                                                           
  dt1, dt2, dt3, dt4:TDateTime;                                               
  i:longint;                                                                  
  r:double;                                                                   
begin        
  // это типа ввод даты.                                                                 
  dt1 := StrToDateTime('20-1-99 00:00:00');                                   
  // скопируем
  dt2 := dt1;                                                                 
  dt3 := dt1; 
  // просто найдем дату следующего                                                      
  dt1:= incDay(dt1);               
  // а тут немножко извращенно - просто добавим  посекундно
  // это же тоже самое:)                               
  for i := 1 to 60*60*24 do                                                   
        dt2 := incSecond(dt2);
  // dt2 и dt3 - это одно и тоже - просто на одни сутки вперед
  // поэтому, добавляя вычитая одно и тоже, должны получить прежний результат
  // то есть dt3 и dt4   - одно и тоже.
  dt4 := dt3 - dt2 + dt1;                                                     
  if trunc(dt4) = trunc(dt3) then                                             
        WriteLn('good')                                                       
  else                                                                        
        WriteLn('bad');                                                       
  writeln(dt3, ' ', dt4,' ', dt4-dt3)                                         
end.У меня выводит bad...(код должен быть сильно компиляторозависимым, так что в делфи может не повториться, но может взять 2 суток или чуточку поиграться слагаемыми).
 
 Конечно, можно заявить, что в реальной жизни такого не будет. Возможно. Но я пишу - что оно будет возникать, как только начнем манипулировать датами. Такой манипуляцией может быть сохранение данных в базу.
 Галочка "подтверждения прочтения" - вселенское зло. |  
|   | min@y™ (статус: Доктор наук), 3 июня 2011, 15:47 [#11]: Цитата (Вадим К): Вот пример. может быть он немножко надуманный Это перебор. Нахрена IncSecond(), когда идёт работа с датами? Естественно, в цикле набежит погрешность, ещё бы, 86400 раз делать операцию с плавающей точкой.
 Пусть ошибка возникает у тех, кто будет писать программы таким образом. А у нормальных людей ошибка не вылезет.
 Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп! |  
|   | Вадим К (статус: Академик), 3 июня 2011, 16:00 [#12]:я не писал, что ошибка будет возникать всегда. Она будет возникать редко, но ой как будет неприятно. В данном случае я специально сделал много выражений, что бы ошибка вылазила гарантировано.
 Нормальные люди знают, что такое вещественные числа, и как их правильно сравнивать.
 Галочка "подтверждения прочтения" - вселенское зло. |  
|   | nickel-j (статус: Посетитель), 3 июня 2011, 17:24 [#13]:Вообще-то Вадим прав - по транку равенство выдает 30.05.2011 и 2.06.2011, а реальные равенства типа 30 и 30 не выдает вообще. Т.е. стабильность никакущая. С другой стороны модуль тоже не радует. Странно, кажется - простенькая задачка - а сколько заморочек |  
|   | min@y™ (статус: Доктор наук), 3 июня 2011, 17:29 [#14]: Цитата (nickel-j): транку равенство выдает 30.05.2011 и 2.06.2011 Такого быть не может. Показывай код.
 Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп! |  
|   | Вадим К (статус: Академик), 3 июня 2011, 18:11 [#15]:в модуле DateUtils есть функция function CompareDate(const A, B: TDateTime): TValueRelationship; которая умеет сравнивать две даты. Возвращает 0, если даты равны и -1 и +1 в других случаях. 
 Также есть функция function SameDate(const A: TDateTime; const B: TDateTime):Boolean;
 которая как раз делает то, что нужно.
 
 Попробуйте на ваших примерах
   Галочка "подтверждения прочтения" - вселенское зло. |  
|   | nickel-j (статус: Посетитель), 3 июня 2011, 18:45 [#16]:Ага, там же я нашел DateOf - но SameDate конечно поэстетичней. Код приблизительно такой - суть в начислении % по вкладам, событие идёт по таймеру, увеличивающему дату на 1 день.
 
  if SameDate(MonthCalendar1.Date,inv[j].getdate)=true then
  begin
    if inv[j].Cap=true then
     begin 
     с капитализацией
     end
    else //без капитализации
     begin
     end
 ADOQuery1.SQL.Text:='update Вклад set
Сумма='+inttostr(profit)+',Датаначисления="'+datetostr(incmonth(MonthCalendar1.Date))+'" where счёт='+inv[j].contract;
  ADOQuery1.ExecSQL;
           Memo1.Lines.Add(datetostr(MonthCalendar1.date)+'счёт '+inv[j].contract+' сумма =  '+inttostr(profit));
  endИ иногда выводится начисление на одну дату, а затем еще через день - и потом уже дата начисления уходит на 2 месяца вперед. Скорее всего это не из-за Trunc - что бы не подставлял - порой возникает. |  
|   | min@y™ (статус: Доктор наук), 3 июня 2011, 19:00 [#17]: Цитата (nickel-j): Код приблизительно такой Покажи, как с Trunc() было!
 Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп! |  
|   | nickel-j (статус: Посетитель), 3 июня 2011, 19:25 [#18]:с Trunc() вместо  if SameDate(MonthCalendar1.Date,inv[j].getdate)=true thenбыло if Trunc(MonthCalendar1.Date)=Trunc(inv[j].getdate) then |  
|   | min@y™ (статус: Доктор наук), 3 июня 2011, 19:32 [#19]:Понятно. Посмотрел бы в отладчике, чему равно то и другое. Для начала. Делаю лабы и курсачи по Delphi и Turbo Pascal. За ПИВО! Пишите в личку, а лучше в аську. А ещё лучше - звоните в скайп! |  
|   | bugmenot (статус: 3-ий класс), 3 июня 2011, 19:40 [#20]: Цитата (Вадим К): abs(MonthCalender1.Date - AccessDate) < 0.1вместо проверки на равенство. константу 0.1 подбираете
 Грубо, см. Math.SameValue для оценки бесконечно малых
 
 
 Цитата (nickel-j): if SameDate(MonthCalendar1.Date,inv[j].getdate)=true then 
 if 2 * 2 = 4 = True <> False then Beep(); виконання програми розпочинається з того самого мiсця, де призупинилося.
 
 |  Страницы: [1] [2] [Следующая »]  Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте. |