#delphi #delphi-xe2
#delphi #delphi-xe2
Вопрос:
Есть ли какая-нибудь функция, которая может проверять дату в формате aaaa / mm / gg (ccyy / mm / dd), возвращая, True
если она действительна или False
иначе? Я имею в виду действительно действительную дату, а не только для выравнивания по синтаксису.
Ответ №1:
Является ли ‘aaaa’ годом и ‘gg’ днем?
var
MyString: string;
MyDate: TDateTime;
settings: TFormatSettings;
begin
settings.ShortDateFormat := 'yyyy/mm/dd';
settings.DateSeparator := '/';
MyString := '2011/15/15';
if TryStrToDateTime(MyString, MyDate, settings) then
Label1.Caption := 'correct date'
else
Label1.Caption := 'incorrect';
end;
Комментарии:
1. Примечание: «Если S содержит только два числа, это интерпретируется как дата (m / d или d / m) в текущем году». Таким образом, можно пройти проверку с неправильно сформированной строкой. Кроме того, часть времени тоже пройдет. Кроме того, функция пропускает начальные пробелы, давая еще больше ложных срабатываний.
2. Назначение только двух полей записи настроек может быть опасным. Я бы, вероятно, попытался сначала инициализировать его из текущих активных настроек: settings := TFormatSettings. Создать(GetUserDefaultLCID);
Ответ №2:
Это очень быстро, потому что сначала обнаруживаются самые простые ошибки.
function IsValidDate(const S: string): boolean;
var
y, m, d: Integer;
const
DAYS_OF_MONTH: array[1..12] of integer = (31, 29, 31, 30, 31, 30, 31, 31, 30,
31, 30, 31);
begin
result := false;
if length(S) <> 10 then Exit;
if (S[5] <> '/') or (S[8] <> '/') then Exit;
if not TryStrToInt(Copy(S, 1, 4), y) then Exit;
if not TryStrToInt(Copy(S, 6, 2), m) then Exit;
if not InRange(m, 1, 12) then Exit;
if not TryStrToInt(Copy(S, 9, 2), d) then Exit;
if not InRange(d, 1, DAYS_OF_MONTH[m]) then Exit;
if (not IsLeapYear(y)) and (m = 2) and (d = 29) then Exit;
result := true;
end;
Комментарии:
1. Извини, Андреас. 🙂 Это работает (и поэтому нет снижения), но запрашивающий использует XE2. Лучший ответ — это ответ @Arjen, который использует
TFormatSettings
. Я бы разрешил исключение, а не отбрасывал его, используяTryStrToDate
как это сделал Арьен.2. @Ken, спасибо. И об исключении: зависит от обстоятельств. Андреас, и НИКАКИХ отрицательных отзывов от меня.
3.
StrToDate(S, Format)
подход not way специфичен для XE2, но недостаточно строг. Положительный. И я думаю, что настоящий даунвотер пытается кого-то подставить.
Ответ №3:
Используйте перегруженную версию StrToDate(), которая имеет параметр TFormatSettings. Затем вы можете передать строку нужного формата для синтаксического анализа, и она вернет TDateTime после проверки проанализированных значений.
Ответ №4:
Пытаюсь сделать то же самое и наткнулся на этот старый поток. В итоге я написал свою собственную функцию и подумал, что опубликую ее. Как насчет этого?
function IsValidDate(const S: string): boolean;
var TestDate : tdatetime;
begin
Result := False;
if (LastDelimiter('/',S) >= 4)
and
(Length(S)-LastDelimiter('/',S) >= 4)
then
Result := TryStrToDate(S,TestDate);
end;
Прежде всего, я проверяю, находится ли второй разделитель (/), по крайней мере, достаточно далеко, чтобы представлять как день, так и месяц (4-я позиция). Затем я ввожу в них 4-значный год в следующей строке. Измените этот второй тест на > = 2 для двухзначного числа, но я просто полагаю, что не так уж плохо ввести четырехзначный год — это всего лишь еще два штриха.
Наконец, я тестирую с помощью TryStrToDate(). Если есть только один разделитель или если это недопустимая дата, она будет обнаружена здесь.
Если вы хотите пофантазировать, вы могли бы проверить, был ли год в пределах последних 10 лет или что-то в этом роде. Просто добавьте:
Result := Result and (Now - TestDate < 3650);
Дэйв