Сравнение меток времени UTC

Я работаю над пользовательским приложением клиент-сервер (для запуска в Linux), и один из отправляемых фреймов включает метку времени (то есть время отправки фрейма).

Чтобы сделать мое приложение надежным, я использовал gmtime производить время на клиента. Я нахожусь в Бельгии, поэтому сейчас час клиентской виртуальной машины на 2 часа позже времени UTC (из-за летнего часа).

На стороне сервера я сначала конвертирую полученную строку в time_t тип. Я делаю это, чтобы использовать difftime Функция, чтобы увидеть, если отметка времени не слишком старая.
Затем я снова генерирую метку времени (в формате UTC) с помощью gmtimeи я превращаю его в time_t,

Я сравниваю тогда два time_t чтобы увидеть разницу во времени.

У меня проблема с преобразованием времени на стороне сервера. Я использую тот же код, что и в клиенте, но вывод gmtime это отличается…


Сторона клиента : функция для генерации метки времени и ее экспорта в строку (time_str):

    std::string getTime()
{
time_t rawtime;
struct tm * timeinfo;
char buffer[80];

time (&rawtime);              // Get time of the system
timeinfo = gmtime(&rawtime);  // Convert it to UTC time

strftime(buffer,80,"%d-%m-%Y %H:%M:%S",timeinfo);
std::string time_str(buffer); // Cast it into a string
cout<<"Time Stamp now (client) : "<<time_str<<endl;

return time_str;
}

И это производит это (в 9:33 по местному времени):

Отметка времени сейчас: 06-04-2016 07:33:30


Сторона сервера: функция для извлечения метки времени, создания метки времени newx и сравнения их:

bool checkTimeStamp(std::string TimsStamp_str, double delay)
{
cout<<"TimeStamp recieved: "<<TimsStamp_str<<endl;
/* Construct tm from string */
struct tm TimeStampRecu;
strptime(TimsStamp_str.c_str(), "%d-%m-%Y %I:%M:%S", &TimeStampRecu);
time_t t_old = mktime(&TimeStampRecu);

/* Generate New TimeStamp */
time_t rawtime;
struct tm * timeinfo;
time (&rawtime);  // Get time of the system
timeinfo = gmtime(&rawtime);  // convert it to UTC time_t
time_t t2 = mktime(timeinfo);  // Re-Cast it to timt_t struct
/* Convert it into string (for output) */
char buffer[80];
strftime(buffer,80,"%d-%m-%Y %H:%M:%S",timeinfo);
std::string time_str(buffer); // Cast it into a string
cout<<"Time Stamp now (server) : "<<time_str<<endl;

/* Comparison */
double diffSecs = difftime(t2, t_old);
cout<<diffSecs<<endl;
bool isTimeStampOK;
if (diffSecs < delay)
isTimeStampOK = true;
else
isTimeStampOK = false;

return isTimeStampOK;
}

И это производит (в 9:33 в Бельгии):

TimeStamp получено: 06-04-2016 07:33:30

Штамп времени (сервер): 06-04-2016 08:33:31


Почему серверное время (8 ч 33) не указано ни по местному времени (9 ч 33), ни по времени UTC (7 ч 33)?

Я сделал ошибку в его поколении? Я не понимаю, где, потому что это точно такой же код, как на стороне клиента …

0

Решение

В вашем коде есть пара ошибок, некоторые по вашей вине, некоторые нет. Самая большая проблема здесь в том, что C <time.h> API настолько плох, запутан, неполон и подвержен ошибкам, что подобные ошибки почти обязательны. Подробнее об этом позже.

Первая проблема — эта строка:

struct tm TimeStampRecu;

Создает неинициализированный tm а затем передает это в strptime, strptime не может заполнить все поля TimeStampRecu, Вы должны инициализировать с нуля TimeStampRecu как это:

struct tm TimeStampRecu{};

Следующая проблема:

strptime(TimsStamp_str.c_str(), "%d-%m-%Y %I:%M:%S", &TimeStampRecu);

12-часовое время, обозначенное как %I неоднозначно без спецификатора AM / PM. Я подозреваю, что это просто тип o, поскольку это единственное место, где вы его используете.

Следующая проблема:

gmtime  time_t -> tm  UTC to UTC
mktime  tm -> time_t  local to UTC

То есть, mtkime интерпретирует ввод tm по местному времени компьютера, на котором он работает. Вместо этого вам нужно:

timegm  tm -> time_t  UTC to UTC

к несчастью timegm не является стандартным C (или C ++). К счастью, в вашей системе это все равно существует.

С этими изменениями, я думаю, ваш код будет работать так, как ожидалось.


Если вы используете C ++ 11, здесь есть более безопасная библиотека даты / времени (бесплатно / с открытым исходным кодом):

https://github.com/HowardHinnant/date

Вот ваш код, переведенный для использования этой библиотеки более высокого уровня:

std::string getTime()
{
using namespace std::chrono;
auto rawtime = time_point_cast<seconds>(system_clock::now());
auto time_str = date::format("%d-%m-%Y %H:%M:%S", rawTime);
std::cout<<"Time Stamp now (client) : "<<time_str<< '\n';

return time_str;
}

bool checkTimeStamp(std::string TimsStamp_str, std::chrono::seconds delay)
{
using namespace std::chrono;
std::cout<<"TimeStamp recieved:       "<<TimsStamp_str<< '\n';
/* Construct tm from string */
std::istringstream in{TimsStamp_str};
time_point<system_clock, seconds> t_old;
date::parse(in, "%d-%m-%Y %H:%M:%S", t_old);

/* Generate New TimeStamp */
auto rawtime = time_point_cast<seconds>(system_clock::now());
auto time_str = date::format("%d-%m-%Y %H:%M:%S", rawTime);
in.str(time_str);
time_point<system_clock, seconds> t2;
date::parse(in, "%d-%m-%Y %H:%M:%S", t2);
cout<<"Time Stamp now (server) : "<<time_str<<endl;

/* Comparison */
auto diffSecs = t2 - t_old;
cout<<diffSecs.count()<<endl;
bool isTimeStampOK;
if (diffSecs < delay)
isTimeStampOK = true;
else
isTimeStampOK = false;

return isTimeStampOK;
}
4

Другие решения

Отметка времени является абсолютной величиной. Это не зависит от часовых поясов или летнего времени. Он представляет количество секунд с определенного момента в прошлом. Значение, возвращаемое time() то же самое, независимо от того, что часовой пояс сервера.

Ваш код не производит отметки времени, но даты отформатированы для потребления человеком.

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

Я бы написал ваш код так (используя только временные метки):

// The client side
time_t getTime()
{
return time(NULL);
}// The server side
bool checkTimeStamp(time_t clientTime, double delay)
{
time_t now = time(NULL);

return difftime(now, clientTime) < delay;
}

Обновить

Если вам нужно использовать строки для связи между клиентом и сервером, то все, что вам нужно сделать, это обновить строку формата, используемую для форматирования временных меток как даты (на клиенте), и проанализировать даты обратно к временным меткам (на сервере), включите часовой пояс, который вы использовали для форматирования даты.

Это значит добавить %Z в формате везде в вашем коде. использование "%d-%m-%Y %H:%M:%S %Z", Кстати, код, который вы разместили сейчас читает "%d-%m-%Y %I:%M:%S" в призыве к strptime() и это принесет вам еще одну проблему во второй половине дня.

замечание

Я всегда использую "%Y-%m-%d %H:%M:%S %Z" потому что это однозначно и не зависит от локали. 06-04-2016 можно интерпретировать как April 6 или же June 4в зависимости от родного языка читателя.

1

По вопросам рекламы [email protected]