Вариант искажения для endoftheday

(Предпосылки) У меня есть программа, которая использует параметризованный запрос в базе данных для поиска по времени суток. Требуемая функциональность означает, что я выполняю несколько поисков, где время является концом дня, поэтому у меня, естественно, есть код, подобный следующему

Query.Parameters[3].Value := TimeOf(EnfOfTheDay(ToTime));

Это не срабатывает из-за ошибки форматирования, потому что время отображается как «31/12/1899». Это демонстрирует следующая консольная программа, которая выводит "31/12/1899" на системную консоль (или эквивалент в ваших региональных настройках).

program Project1;
{$APPTYPE CONSOLE}
{$R *.res}

uses
  System.SysUtils, System.DateUtils;
var
  V: variant;
  Time: TDateTime;
begin
    Time := TimeOf(EndOfTheDay(EncodeDate(2017,1,26)));
    V := Time;
    Write(Output, V);
    ReadLn;
end.

Это достаточно легко обойти, но мой вопрос в том, следует ли сообщать в Embarcadero об этой ошибке. Я вижу, что проблема конкретно возникает, когда значение только времени в типе TDateTime сохраняется в варианте, который затем переводится в текст. Времена ранее в течение дня работают нормально, получая строки вроде «23:59».

Если я изменю переменную Time на TTime, тогда результирующая строка будет числовой дробью (т. Е. Вариант не был настроен как значение datetime), но я не понимаю, почему конкретная дробь равна 23:59:59 ( который генерирует EndOfTheDay) интерпретируется как дата 1899 года. Я от природы подозрительно отношусь ко всему, что дает дату пролонгации 1899 из-за особых проблем с продуктами Microsoft, которые могут означать, что это сделано намеренно.


person Kanitatlan    schedule 26.01.2017    source источник
comment
Вы можете избежать этого варианта, используя функции .Asxxxx для установки значения параметра вместо использования .Value   -  person whosrdaddy    schedule 26.01.2017
comment
На самом деле вам следует избегать использования функции TimeOf и просто использовать функцию FormatDateTime   -  person Fero    schedule 26.01.2017


Ответы (2)


Вариантное представление будет установлено на varDate, которое внутренне является переменной TDateTime. Так что информация по-прежнему хранится внутри варианта.

WriteLn(Output, FormatDateTime('hh:nn:ss',V)); 

выходы

23:59:59

Ошибка обнаруживается в вариантной подпрограмме VarToStr, которая в конечном итоге вызывает :

function DateToWStrViaOS(const AValue: TDateTime): WideString;
begin
  VarResultCheck(VarBStrFromDate(AValue, VAR_LOCALE_USER_DEFAULT, 0, Result),
                 varDate, varOleStr);
end;

VarBStrFromDate - это вызов ОС, который каким-то образом округляет значение до 1, отсюда и дата «31/12/1899».


Заключение: если вы по-прежнему хотите использовать варианты в своей базе данных, не используйте встроенный вариант для преобразования текста для TDateTime, предоставляемый библиотекой вариантов.


Обновление: EndOfTheDay вернет время, равное 1 миллисекунде до полуночи. Кажется, что разрешение варианта при преобразовании в текст основано на секундах (дизайн Windows). Установка времени на 1 и вычитание с помощью Time := IncMilliSecond(Time,-501) вернет правильное значение. (Или Time := IncSecond(Time,-1), если хотите.)

person LU RD    schedule 26.01.2017
comment
Таким образом, фактический ответ заключается в том, что API операционной системы VarBStrFromDate выполняет неправильное преобразование, и это определенно не проблема Delphi. - person Kanitatlan; 26.01.2017
comment
Смотрите мое обновление, окна округляются до ближайшей секунды при преобразовании в текст. - person LU RD; 27.01.2017

Если вы перепишете свой пример как

begin
    Time := Now; //TimeOf(EndOfTheDay(EncodeDate(2017,1,26)));
    Time := EndOfTheDay(Time);
    V := Time;
    WriteLn(Output, V);
    Time := TimeOf(Time);
    V := Time;
    WriteLn(Output, V);
    ReadLn;
end.

установите наблюдение за переменной Time и пошагово выполните ее в отладчике, очевидно, что именно вызов TimeOf вызывает бесполезный результат. Причина указана в ответе LU RD, который появился, когда я писал это, но с тех пор был удален (если он появится снова, я сниму это), а именно, что TimeOf устанавливает неотъемлемую часть значения datetime равной нулю, что является по умолчанию отображается как «31/12/1899». Но проблема не в этой причине: проблема в том, чтобы присвоить результат TimeOf варианту и оставить его RTL для создания его представления.

Возвращаясь к вашей проблеме, которая в первую очередь побудила ваш q, возможно, лучший способ решить эту проблему, если ваша реализация SQL SQL поддерживает это, - это использовать конструкцию / функцию, которая имеет дело только с компонентом времени, однако его даты / время хранятся.

Кстати, я думаю, что ваш q-заголовок неверен, проблема не в «искажении варианта на конец дня».

person MartynA    schedule 26.01.2017