Я работаю над пользовательским приложением клиент-сервер (для запуска в 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)?
Я сделал ошибку в его поколении? Я не понимаю, где, потому что это точно такой же код, как на стороне клиента …
В вашем коде есть пара ошибок, некоторые по вашей вине, некоторые нет. Самая большая проблема здесь в том, что 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;
}
Отметка времени является абсолютной величиной. Это не зависит от часовых поясов или летнего времени. Он представляет количество секунд с определенного момента в прошлом. Значение, возвращаемое 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
в зависимости от родного языка читателя.