Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лабы по МП / API / Methods.doc
Скачиваний:
42
Добавлен:
11.05.2015
Размер:
233.47 Кб
Скачать

1.15.2 Локальное время

Теперь рассмотрим вопросы, связанные с локальным временем. Это время зависит от часового пояса. К тому же, в нашей страной многих других странах имеется различие между летним и зимним локальным временем. В API Windowsесть тип структурыTTimeZoneInformation( _ТIМЕ_ZONE_INFORMATION), содержащей данные 6 смещении локального времени и датах перехода на летнее и зимнее время:

Typedef struct _ТIМЕ_ZONE_INFORMATION {

LONG Bias;

WCHAR StandartName[ 32 ];

SYSTEMTIME StandartDate;

LONG StandartBias;

WCHAR DaylightName[ 32 ];

SYSTEMTIME DaylightDate;

LONG DaylightBias;

} ТIМЕ_ZONE_INFORMATION , *P ТIМЕ_ZONE_INFORMATION,

*L ТIМЕ_ZONE_INFORMATION;

Информация, содержащаяся в этой структуре, определяет соотношение между стандартом согласованного универсального времени (CoordinatedUniversalTime—UTCили время.по Гринвичу) и локальным временем. Основное полеBiasопределяет соответствие между этими временами. В поле содержится смещение локального времени в минутах по отношению кUTC. Например, для МосквыBias= -180. Т.е. московское время опережаетUTCна 3 часа. В общем случае:

UTC= локальное время + смещение.

Поле StandartName— строка с нулевым завершающим символом, дающая название стандартного (зимнего) времени, принятого в системе. Например, для Москвы это может быть "Московское время (зима)". Данное поле не используется операционной системой и не является обязательным. В качествеStandartNameможет быть задана пустая строка. Но если полеStandartNameзадано при Вызове функцииSetTimeZoneInformation, то оно будет прочитано без изменений при последующем вызове функцииGetTimeZoneInformation.

Поле StandartDateявляется структурой описанного ранее типаTSystemTime, содержащей дату и время перехода с летнего на стандартное время. Если такая дата не указана, то полеwMonthструктурыStandartDateдолжно быть равно 0. Если полеStandartDateзаполнено, то должно быть заполнено и описанное далее полеDaylightDate.

Структура StandartDateможет содержать информацию в двух форматах. Во-первых, это может быть точная дата перехода:wYear— год,wMonth— месяц,wDay— день,wHour— час,wMinute— минута,wSecond— секунда,wMillisecond— Миллисекунда. Однако обычно переход не привязан к определенной дате, а привязан к определенному дню недели определенного месяца. Например, к последнему воскресенью какого-то месяца. СтруктураStandartDateможет заполняться в таком формате. В этом случае ее полеwYearравно 0, полеwMonthуказывает месяц, полеwDayOfWeekуказывает день недели, а полеwDayопределяет, в который по счету день месяца происходит переход времени. ПолеwDay.может принимать значения от 1 (первый день) до 5 (последний день). Например, если переход на зимнее время осуществляется в 3 часа ночи последнего воскресенья октября, тоwYear==0, wMonth = 10,wDayOfWeek= 0 (на Западе воскресенье считается первым днем недели, а не последним как у насwDay= 5wHour==3.

Если задано значение поля StandartDate, то полеStandartBiasможет содержать дополнительное смещение, складываемое сBiasв период зимнего времени. Обычно это значение равно 0.

Поле DaylightName- строка с нулевым завершающим символрм, дающая название летнего времени, принятого в системе. Например, для Москвы это может быть "Московское время (лето)". Данное полене используется операционной системой и не является обязательным. В качествеDaylightNameможет быть Задана пустая строка. Но если полеDaylightNameзадано при вызове функцииSetTimeZoneInformation, то оно будет прочитано без изменений при последующем вызове функцииGetTimeZoneInformation.

Поле DaylightDateопределяет структуру типаTSystemTime, содержащую дату и время перехода с зимнего на летнее время. Если такая дата не указана, то полеwMonthструктурыDaylightDateравно 0. Если полеDaylightDateзаполнено, то должно быть заполнено и полеStandartDate. СтруктураStandartDateможет содержать информацию в двух форматах, описанных выше для поляStandartDate. Например, если переход на летнее время осуществляется в 2 часа ночи последнего воскресенья марта, тоwYear=0,wMonth=3,wDayOfWeek= 0,wDay=5,wHour=2.

Если задано значение поля DaylightDate, то полеDaylightBiasсодержит дополнительное смещение, складываемое сBiasв период летнего времени. Обычно это значение равно -60 — сдвиг на 1 час.

Работу со структурами типа TTimeZoneInformationобеспечивают функцииGetTimeZoneInformationиSetTimeZoneInformation:

DWORD GetTimeZoneInformation(

OUT LPTIME_ZONE_INFOMATION lpTimeZoneInformation);

BOOL SetTimeZoneInformation(

CONST TIME_ZONE_INFORMATION * lpTimeZoneInformation);

Функция GetTimeZoneInformationдает доступ к информации о поясном времени, на которое настроена система. Эту информацию функция извлекает из настроекWindowsи заносит в структуру, указанную параметромlpTimeZoneInformation. ФункцияSetTimeZoneInformationзаносит аналогичную информацию в систему.

При успешном вызове GetTimeZoneInformationфункция возвращает одно из следующих значений:

TIME_ZONE_ID_UNKNOWN

Операционная система не может определить временной пояс. Это может быть, если перед этим при вызове функции SetTimeZoneInformationвlpTimeZoneInformationбыло задано только поле смещенияBias.

TIME_ZONE_ID_STANDART

Операционная система работает в соответствии со значением поля StandartDateзаписиlpTimeZoneInformation, т.е. по стандартному (зимнему) времени.

TIME_ZONE_ID_DAYLIGHT

Операционная система работает в сoответствии со значением поляDaylightDateзаписиlpTimeZoneInformation, т.е. по летнему времени.

Если вызов GetTimeZoneInformationпривел к ошибке, возвращается значение 0xFFFFFFFF. ФункцияSetTimeZoneInformationвозвращает в случае ошибкиfalse.

Рассмотрим примеры применения функций локального времени. Пусть в переменной Т типа TDateTimeхранятся дата и время некоторого события по московскому времени. И вы хотите в переменнуюTLocтипаTDateTimeзанести локальные дату и время вашего часового пояса, соответствующие этому событию. Такой пересчет можно сделать следующим кодом:

TTimeZoneInformation lpTimeZoneInformation;

System::TDateTime T, TLoc=0;

if (GetTimeZoneInformation(&lpTimeZoneInformation)==

TIME_ZONE_ID_STANDART)

TLoc = T+ (-180. lpTimeZoneInformation.Bias –

lpTimeZoneInformation.StandartBias)/(60*24);

else

TLoc = T+ (-180. lpTimeZoneInformation.Bias –

lpTimeZoneInformation.DaylightBias)/(60*24);

ShowMessage(DateTimeToStr(T) + “/n” + DateTimeToStr(TLoc));

Оператор if вызывает функцию GetTimeZoneInformation, которая заполняет структуру lpTimeZoneInformation типа TTimeZoneInformation информацией о локальном времени. Анализируется значение, возвращенное функцией, и определяется, функционирует ли в данный момент зимнее или летнее время. В зависимости от этого определяется сдвиг локального времени. Для определения локального времени, соответствующего значению Т, формируется суммарный сдвиг, который делится на (60 * 24) — т.е. на число минут в сутках. Для формирования сдвига сначала вычитается 180. Так как смещение московского времени -180, то тем самым время Т приводится к согласованному универсальному времени. Затем к этому времени добавляется локальный сдвиг, величина которого зависит от того, функционирует ли в данный момент зимнее или летнее время. Последний оператор отображает сообщение, содержащее исходное московское и вычисленное локальное время.

Приведенный код предполагал, что на компьютере задана полная информация о переходе на летнее и зимнее время, т.е. в структуре типа TTimeZoneInformationзаданы значения полейStandartDateиDaylightDate. В этом случае, если функцияGetTimeZoneInformationне вернулаTIME_ZONE_ID_STANDART, значит время летнее. Но в некоторых случаях информация о зимнем и летнем времени может быть не полная. Так что в общем случае вместо содержащегося в приведенном коде оператораifлучше использоватьswitch:

Switch (GetTimeZoneInformation(&lpTimeZoneInformation))

{

case TIME_ZONE_ID_STANDART:

TLoc = T + (-180. lpTimeZoneInformation.Bias –

lpTimeZoneInformation.StandartBias)/(60*24);

ShowMessage(DateTimeToStr(T) + “\n” + DateTimeToStr(TLoc));

break;

case TIME_ZONE_ID_DAYLIGHT:

TLoc = T + (-180. lpTimeZoneInformation.Bias –

lpTimeZoneInformation.DaylightBias)/(60*24);

ShowMessage(DateTimeToStr(T) + “\n” + DateTimeToStr(TLoc));

break;

case TIME_ZONE_ID_UNKNOWN:

TLoc = T + (-180. lpTimeZoneInformation.Bias)/(60*24);

ShowMessage(DateTimeToStr(T) + “\n” + DateTimeToStr(TLoc));

break;

default:

ShowMessage(“Error”);

Exit(0);

}

Теперь рассмотрим решение обратной задачи — перевода текущего локального времени в московское. Это можно сделать следующим кодом:

TTimeZoneInformation lpTimeZoneInformation;

System::TDateTime T=0, TMos=0;

if (GetTimeZoneInformation(&lpTimeZoneInformation)==

TIME_ZONE_ID_STANDART)

TMos = T+ (lpTimeZoneInformation.Bias +

lpTimeZoneInformation.StandartBias +180.)/(60*24);

else

TLoc = T+ (lpTimeZoneInformation.Bias +

lpTimeZoneInformation.DaylightBias+180.)/(60*24);

ShowMessage(DateTimeToStr(T) + “\n” + DateTimeToStr(TMos));

Код аналогичен приведенному ранее. Текущее время определяется функцией Now. Далее прибавлением к полученному значению соответствующего локального смещения время приводится к стандартному, а прибавлением 180 стандартное время приводится к московскому.

В данном примере вместо функции Nowможно было бы воспользоваться описанной ранее функциейGetLocalTime. Но тогда бы код заметно усложнился:

TSystemTime lpSystemTime;

GetLocalTIme(&lpSystemTime);

T = SystemTimeToDataTime(lpSystemTime);

Для дат и времени, представленный в формате FILETIME(см. разд. 1.15.1), можно для взаимных преобразований локального времени и времениUTCиспользовать функцииLocalFileTimeToFileTimeиFileTimeToLocalFileTime:

BOOL LocalFileTimeToFileTime(CONST FILETIME *lpLocalTimeFile, LPFILETIME

lpFileTime);

BOOL FileTimeToLocalFileTime(CONST FILETIME *lpTimeFile, LPFILETIME

lpLocalFileTime);

Первая из них преобразует локальное время, записанное в структуре lpLocalTimeFile, во времяUTCв структуреlpFileTime, а вторая осуществляет обратное преобразование. В обеих функциях параметрыlpLocalTimeFileиlpFileTimeдолжны указывать на разные структуры.

Если исходные дата и время имеются в формате TDateTime, то это значение можно сначала преобразовать в форматSYSTEMTIMEфункциейDateTimeToSystemTime, затем полученный результат преобразовать функциейSystemTimeToFileTimeв форматFILETIME(функцииDateTimeToSystemTimeиSystemTimeToFileTimeописаны в разд. 1.15.1), после чего к результату можно применить функцииLocalFileTimeToFileTimeиFileTimeToLocalTimeFile. Цепочка получается довольно длинная. Так что ее имеет смысл использовать, только если результатом должно быть времяUTCв форматеFILETIME. Например, если в переменной Т типаTDateTimeзаписано локальное время, и надо получить соответствующее ему времяUTCв форматеFILETIME(подобный пример вы можете увидеть в разд. 3.6), это можно сделать следующим кодом:

FILETIME Local, UTC:

SYSTEMTIME st;

LARGE_INTEGER li;

DateTimeToSystemTime(T, st);

SystemTimeToFileTime(&st, &local);

LocalFileTimeToFileTime(&local, &UTC);

1.15.3 Измерение отрезков времени

В процессе разработки приложения иногда требуется зафиксировать интервалы времени, чтобы узнать, "например, сколько времени занимают вычисления в каком-то фрагменте вашей программы. Это можно сделать различными способами. Простейший вариант — зафиксировать функцией Timeначало и конец выполнения измеряемого фрагмента кода:

TDateTime T1, T2;

T1 = Time();

……//Фрагмент, время которого измеряется

T2 = Time();

AnsiString S;

DateTimeToString(S, “hh:nn:ss:zzz”, T2-T1);

Label1 ->Caption=S;

В переменные Т1 и Т2 записываются начальный и конечный моменты времени, а затем их разность заносится функцией DateTimeToStringв строкуS. Применена именно эта функция, а не более простаяDateTimeToStr, чтобы получить результат с точностью до миллисекунд. Если достаточно значение с точностью до секунды, то можно, конечно, воспользоваться функциейDateTimeToStrи отобразить результат оператором:

Label1 ->Caption = TimeToStr(T2-T1);

Для измерения отрезков времени с точностью до миллисекунд можно использовать функцию GetTickCount:

DWORDGetTickCount(VOID); .

Она возвращает число миллисекунд, прошедших с момента старта Windows. Ниже приведен рассмотренный выше пример, реализованный с помощью этой функции:

DWORD T0, T;

T0 = ……//Фрагмент, время которого измеряется

();

……//Фрагмент, время которого измеряется

T = GetTickCount();

Label1 ->Caption = T –T0;;

В этом примере в метку Label1 заносится число миллисекунд, потраченных на выполнение фрагмента кода.

Производить отсчеты времени с высокой точностью можно с помощью функций QueryPerformanceCounterиQueryPerformanceFrequency:

BOOL QueryPerformanceCounter(

OUT LARGE_INTEGER *lpPerformanceCount);

BOOL QueryPerformanceFrequency(OUT LARGE_INTEGER *lpFequency);

Они работают, со счетчиком повышенной точности, который, правда, имеется не на каждом компьютере. Если такого счетчика нет, функции возвращают 0.

Функция QueryPerformanceCounterфиксирует в переменной типаLARGE_INTEGER, на которую указывает параметрlpPerformanceCount, текущее значение счетчика. А функцияQueryPerformanceFrequencyзаносит в переменную, на которую указывает параметрlpFequency, частоту счётчика в герцах, т.е. число увеличений счетчика в секунду. Таким образом, реализация рассмотренного выше пример с помощью этих функций может иметь вид:

LARGE_INTEGER f, Coun0, Count1, dCount;

If(!QueryPerformanceFrequency(&f))

{

Label1 ->Caption= “Изменение невозможно”;

return;

}

QueryPerformanceCounter(&Count0);

……//Фрагмент, время которого измеряется

QueryPerformanceCounter(&Count1);

dCount.QuadPart = Count1.QuadPart – Count0.QuadPart;

Label1 ->Caption = dCount.QuadPart * 1000 / f.QuadPart;

В приведенном примере результат измерения отрезка времени выводится в метку Label1 в миллисекундах. Если в последнем операторе заменить множитель 1000 множителем 1000000, то результат будет выводиться в микросекундах. Так что можно измерять очень короткие интервалы времени, недоступные рассмотренным ранее функциям.

Следует отметить, что все рассмотренные методы позволяют достоверно измерять не очень маленькие, и не очень большие интервалы времени. Для больших интервалов, превышающих квант времени, выделяемый на процесс, начинают вмешиваться системные процессы. Если эти процессы связаны с фрагментом, длительность которого измеряется, то все нормально. Но в измеряемый интервал вклиниваются и процессы, связанные с переключением системы на другие задачи. В этих случаях более достоверно учесть время, потраченное навычисления именно в данном фрагменте кода, можно функциями GetProcessTimesиGetThreadTimes. Эти функции, подробно рассмотренные в разд. 3.7 и 3.8, дают затраты времени данного процесса или данного потока. Не буду повторять описание функций, а сразу приведу вариант их использования все в том же примере:

__int64 FileTimeToint64(FILETIME F)

{

Return Int64ShllMod32(F.dwHighDateTime, 32) | F.dwLowDateTime;

}

………….

FILETIME lpCreationTime, lpExitTime, lpKernelTime0, lpUserTime0, lpKernelTime1, lpUserTime1;

GetThreadTimes(GetCurrentThread(), &lpCreationTime, &lpExitTime, &lpKernelTime0, &lpUserTime0);

……//Фрагмент, время которого измеряется

GetThreadTimes(GetCurrentThread(), &lpCreationTime, &lpExitTime, &lpKernelTime1, &lpUserTime1);

Label1 ->Caption = (FileTimeToint64(lpUserTime1) - FileTimeToint64(lpUserTime0))/10000;

В этом коде введена описанная в разд. 1.15.1 функция FileTimeToint64, которая преобразует данные типаFILETIMEв целое 64-разрядное число. ФункцияGetThreadTimesвызывается до начала выполнения фрагмента, и после него. В переменныеlpUserTime0 иlpUserTime1 эта функция заяосит время, затраченное потоком в соответствующие моменты на выполнение кода. Разность этих времен, выраженная в миллисекундах, передается в меткуLabel1. Передаваемый результат можно было бы делить не на 10000, а на 10. Тогда результат отображался бы в микросекундах.

Если требуется знать полное время с учетом системных затрат, то последний оператор надо заменить следующим:

Label1 ->Caption = (FileTimeToint64(lpKernelTime1) –

FileTimeToint64(lpKernelTime0))+

FileTimeToint64(lpUserTime1) –

FileTimeToint64(lpUserTime0)/10000;

При любых измерениях интервалов времени, затрачиваемых на выполнение фрагмента кода, полезно задать самые высокие приоритеты процессу и потоку, в котором данный фрагмент выполняется. Это минимизирует добавку к измеряемому интервалу времени затрат на системные процессы. Так что пе-ред определением времени Начала выполнения фрагмента Полезно вставить операторы:

// запоминание приоритетов

DWORD PriorProc = GetPriorityClass(GetCurrentProcess());

DWORD PriorThread = GetThreadPriority(GetCurrentThread());

// установка высоких

SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);

SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);

Первые два оператора запоминают текущие значения приоритетов» в следующие операторы устанавливают максимальные значения приоритетов. Функции, использованные в этом коде, рассмотрены в разд. 3,7 И 3.8. ,

После окончания фрагментами определения времениокончания надо восстановить прежние значения приоритетов операторами:

// восстановление приоритетов

SetPriorityClass(GetCurrentProcess(), PriorProc);

SetThreadPriority(GetCurrentThread(), PriorThread);

Соседние файлы в папке API