Войти

Показать полную графическую версию : TDateTime - Операции с временем [вычитание, сложение]


Drongo
19-07-2010, 14:55
Приветствую всех. :)

Друзья, понадобилась ваша помощь, самостоятельно решить не могу, вернее, только частично. Пишу парсер логов AVZ. В программу добавил функцию поиска файлов, созданых в определённый промежуток времени - 30 дней с момента создания лога AVZ. В логе AVZ эта строка означает дату сканирования
LogDate="7/17/2010 10:33:29 PM"Переменная DataScan в ней хранится уже извлечённая дата
DataScan = "7/17/2010 10:33:29 PM"В таком же формате времени записаны даты создания файлов.
Переменная AnsiString str хранит передаваемую дату создания файла.

Задача: Как правильно вычесть одну дату из другой?DataScan - strЕсли в логах AVZ формат даты может быть разным:

7/17/2010 10:33:29 PM - Месяц/День/Год - M/D/YYYY
20.03.2010 19:14:48 - День.Месяц.Год - DD.MM.YYYY
19.Jul.2010 19:14:48 - День.Месяц.Год
20.03.2010 19:14:48 - День-Месяц-Год - DD-MM-YYYY
В общем любой формат времени из возможных с любым разделителем. Как из дат различных форматов правильно произвести вычитание?
[hr]
С последним форматом проблем нет, также можно заменить разделитель /, - на точку. Друг помог с форматом 19.Jul.2010 19:14:48

Функция SearchCreateToNMounth принимает два аргумента: 1-й дату, 2-й полное имя файла.



// Поиск файлов созданых в течении 1, 2, 3-х месяцев-------------------------
void __fastcall TForm1::SearchCreateToNMounth(String str, String nf)
{
// Вычисляем диапазон создания файла
TDateTime tmp = StrToDateTime(StrToDateTime(MyDate(DateScan)) - StrToDateTime(MyDate(str)));

if((int)tmp <= 3){
Edit3->Text = String(nf) + " = " + DateScan;
}
}
// BadDate - это дата, которую нужно проверить на "правильность" записи. Допустим, "3-Окт-17 23:22:21"
AnsiString __fastcall TForm1::MyDate(String BadDate)
{
String MonthRUS[13]={"", "Янв", "Фвр", "Мрт", "Апр", "Май", "Июн", "Июл", "Агс", "Снт", "Окт", "Нбр", "Дкб"};
String MonthENG[13]={"", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

for(int i = 1; i <= 12; i++){
BadDate = StringReplace(BadDate, MonthENG[i], i, TReplaceFlags()<<rfReplaceAll); //Здесь мы заменяем название месяца на его порядковый номер
BadDate = StringReplace(BadDate, MonthRUS[i], i, TReplaceFlags()<<rfReplaceAll); //Здесь мы заменяем название месяца на его порядковый номер
}
ShortDateFormat = "dd.mm.yyyy"; //Устанавливаем вид "правильной" даты
for(int i = 1; i < BadDate.Length(); i++){ //Вот здесь мы начинаем проходить циклом по всей записи
if(!(BadDate[i] >= '0' && BadDate[i] <= '9')){ // И ищем тот символ, который не является цифрой
DateSeparator = BadDate[i];
break;
} //Как только находим его - делаем его разделителем и прерываем цикл (обязательно, иначе конечным символом станет ":", потому что это будет последняя "не цифра")
}
BadDate = StrToDateTime(BadDate); //Теперь приводим к нужному формату записи
return BadDate; //И возвращаем
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString Time = "6/17/2010 10:07:11 PM"; //Edit1->Text; 5/17/2010 10:07:11 PM
AnsiString NameFile = "C:\\WinXP\\MyFile.exe";
SearchCreateToNMounth(Time, NameFile);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
DateScan = Edit2->Text; //Edit2->Text; 4/17/2010 10:07:11 PM
}
//---------------------------------------------------------------------------

Ошибку выдаёт такую

http://i077.radikal.ru/1007/71/0197fe3c702c.png

Про операции проводимые с временем читал здесь (http://cppbuilder.ru/articles/0164.php#idaczmxd) и здесь (http://cppbuilder.ru/articles/0174.php)

Что я не так делаю? Если не трудно, подскажите как можно это сделать правильно?

Спасибо.

pva
20-07-2010, 20:35
Можно сделать угадывалку (по типу угадывалки кодировки в Total Commander) примерно так:

#include <vector>
using namespace std;

class GuessDateFormat
{
public:
enum state_t {
state_ready, // угадал
state_not_enough, // есть неоднозначности, не хватает примеров
state_umbiguity // противоречивые примеры
};

GuessDateFormat();
void add_sample(const char* date_str);
state_t state() const;
bool convert(const char* from, time_t& to);

private:
enum {
like_nothing = 0x0000, // битовые поля "похоже на..."
like_year = 0x0001,
like_mon = 0x0002,
like_mday = 0x0004,
like_wday = 0x0008,
like_hour = 0x0010,
like_min = 0x0020,
like_sec = 0x0040,
like_pm = 0x0080, // pm/am
like_amon = 0x0100, // месяц прописью
like_date = like_year|like_mon|like_mday|like_amon,
like_time = like_hour|like_min|like_sec,
like_alpha = like_amon|like_pm,
like_everything = 0x01ff,

got_pm = 0x01,
got_year2000 = 0x02,
got_amon = 0x04
};

vector<short> _like_what; // каждый элемент показывает, на что похоже содержимое позиции
short _its_clear, _expecting; // какие элементы точно выяснены

void _suspecting_one(int);
void _suspecting_pair(int);
};

bool GuessDateFormat::convert(const char* from, time_t& to)
{
typedef struct tm tm_t;
tm_t tm1 = {};

vector<short>::iterator first=_like_what.begin(), last=_like_what.end();

while ( *from && first!=last)
{
// найти первый подходящий символ
while (!isalnum(*from) && *++from) {}

if (*from)
{
switch ( *first)
{
case like_year :
tm1.tm_year = atoi(from);
break;

case like_mon :
tm1.tm_mday = atoi(from)-1;
break;

case like_mday :
tm1.tm_mday = atoi(from);
break;

case like_hour :
tm1.tm_hour = atoi(from);
break;

case like_min :
tm1.tm_min = atoi(from);
break;

case like_sec :
tm1.tm_sec = atoi(from);
break;

case like_pm :
// думаем что PM после времени пишется.
if (*from=='P') tm1.tm_hour += 12;
break;

case like_amon :
// выбрать месяц из справочника
break;
}
}

while (isalnum(*from) && *++from) {}
}

to = mktime(&tm1);
return true;
}

GuessDateFormat::state_t GuessDateFormat::state() const
{
for (vector<short>::iterator first=_like_what.begin(),
last=_like_what.end(); first!=last; ++first)
{
short n = *first;
if (!n) return state_umbiguity;
while ( !(n & 1)) n>>=1;
if (n & (~1)) return state_not_enough;
}

return state_ready;
}

void GuessDateFormat::_suspecting_one(int bitset)
{
_like_what.push_back(bitset & _expecting);
_expecting = like_everything;
}

void GuessDateFormat::_suspecting_pair(int bitset)
{
if (!_like_what.empty()) *(_like_what.end()-1) &= bitset;
_expecting = bitset;
}

bool GuessDateFormat::add_sample(const char* date_str)
{
// отмели простейший случай
if (!date_str || !*date_str) return false;

_expecting = like_everything;
_its_clear = 0;

do
{
short suspecting = _expecting;

// пропустили пробелы
while (*date_str && isspace(*date_str)) ++date_str;
if ( !*date_str) break;

if (isalpha(*date_str))
{
suspecting &= like_alpha;

if (date_str[1]=='M' && (date_str[0]=='A' || date_str[0]=='P'))
{
suspecting &= like_pm;
_its_clear |= got_pm;
}
else {
suspecting &= ~like_pm;
_its_clear |= got_amon;
}

_suspecting_one(suspecting);
}
else if (isdigit(*date_str))
{
suspecting &= ~like_alpha;

// проверяем границы
int value = atoi(date_str);
do {++date_str;} while (*date_str && isdigit(*date_str));

if (12 < value)
{
suspecting &= ~like_mon;
if (_its_clear & got_am) suspecting &= ~like_hour;

if (23 < value)
{
suspecting &= ~like_hour;

if (31 < value)
{
suspecting &= ~(like_mon|like_mday);

if (59 < value)
{
suspecting &= like_year;
_its_clear |= got_year2000;
}
}
}
}

if ( (_its_clear & got_year2000) && value < 60)
{
suspecting &= ~like_year;
}

if (_its_clear & got_amon)
{
suspecting &= ~like_mon;
}

_suspecting_one(suspecting);
}
else
{
switch (*date_str)
{
case '/' :
case '.' :
case '-' :
_suspecting_pair(like_date);
break;

case ':' :
_suspecting_pair(like_time);
break;
}
}
}
while (*date_str);
}
//----------------------------------------

как пользоваться:
1) создали
GuessDateFormat guess_format;
2) скидали в него все даты, какие есть
for(...) guess_format.add_sample(date[n].c_str());
3) проверили, угадался ли формат
if (guess_format.state()==GuessDateFormat::state_ready)
4) переводим даты
time_t t1;
for(...) guess_format.convert(date[n].c_str(), t1);

чего в коде нет:
1) таблицы соответсвия месяц-номер месяца (на всех языках, со всеми сокращениями)
2) не может отличить минуты от секунд. Чтобы это обойти, надо внутри добавить код: пробежаться по вектору с подозрениями и если встретится элемент времени, то последовательно обозначить час, минута, секунда.

Drongo
20-07-2010, 22:06
pva, Код конечно шикарный, осталось в нём разобраться. :gigi: Можешь его внедрить в простой пример? :)

pva
21-07-2010, 22:28
вот, правда сделано на билдере 4 (у билдера 6 есть проблема с линковкой имён с подчёркиваниями)




© OSzone.net 2001-2012