【C++】UTC时间戳和datetime

(1)UTC时间与UTC+8

[!NOTE]

  • UTC(协调世界时)UTC+8(东八区时间) 是两种不同的时间标准,主要区别在于 时区偏移

    UTC 时间 = GMT(格林尼治标准时间)(但 GMT 可能受地球自转影响,UTC 更精确)。UTC 是全球标准时间,基于原子钟计算,不受夏令时影响。

    UTC+8 表示比 UTC 快 8 小时,东八区,适用于中国、新加坡、马来西亚、菲律宾等地区。北京时间(CST, China Standard Time)。中国全境统一使用 UTC+8。

    如果 UTC 时间是 2025-06-12 00:00:00,那么:

    UTC+8(北京时间)** 是 2025-06-12 08:00:00

    UTC-5(纽约时间) 是 2025-06-11 19:00:00(前一天)。

[!CAUTION]

  • unix时间戳是UTC时间,单位为s

  • 代码中uint64_t/time_t一律使用UTC时间戳(标准时间)

  • 库表中的datetime类型一律存当地时间(北京时间),存:FROM_UNIXTIME(uint64_t)获取当地时间,取:UNIX_TIMESTAMP()得到UTC时间戳

  • MySQL会根据服务器所在时区选择UTC+8(服务器在中国),UNIX_TIMESTAMP(datetime)会认为datetime是北京时间,先减去8小时得到UTC时间再计算时间戳。

    比如:UNIX_TIMESTAMP('2025-06-12 10:42:10'),它会认为 '2025-06-12 10:42:10' 是北京时间,并转换成 UTC 时间(2025-06-12 02:42:10),再计算2025-06-12 02:42:10的时间戳 → 1749696130

  • 从UTC unix时间戳得到当地的时间

    比如:从1749696130获得2025-06-12 10:42:10,而不是2025-06-12 02:42:10

    //timep:指向 time_t 类型的指针,表示从1970年1月1日00:00:00 UTC开始的秒数。
    //返回一个指向 struct tm 的指针,该结构体包含转换后的本地时间信息。如果转换失败,返回 NULL。
    struct tm* std::localtime(const time_t* timep);
    
    //localtime_r 是 localtime 的线程安全版本,localtime 是较早版本的函数
    struct tm* std::localtime_r(const time_t* timep, struct tm* result);
    
  • 从当地时间得到UTC unix时间戳

    比如:从2025-06-12 10:42:10获得1749696130

    //timeptr:指向 struct tm 的指针,该结构体包含要转换的本地时间信息。
    //返回一个 time_t 类型的时间戳,表示从1970年1月1日00:00:00 UTC开始的秒数。如果输入的 struct tm 无效,返回 -1。
    time_t std::mktime(struct tm* timeptr);
    
    time_t utc_timestamp = std::mktime(&local_tm); // 将本地时间转换为UTC时间戳
    

(2)由unix时间戳(uint64_t或time_t)获取datetime

[!NOTE]

datetime在代码中以std::tm表示

#include <ctime>
std::tm

struct tm
{
int tm_sec;			/* Seconds.	[0-60] (1 leap second) */
int tm_min;			/* Minutes.	[0-59] */
int tm_hour;			/* Hours.	[0-23] */
int tm_mday;			/* Day.		[1-31] */
int tm_mon;			/* Month.	[0-11] */
int tm_year;			/* Year	- 1900.  */
int tm_wday;			/* Day of week.	[0-6] */
int tm_yday;			/* Days in year.[0-365]	*/
int tm_isdst;			/* DST.		[-1/0/1]*/

# ifdef	__USE_MISC
long int tm_gmtoff;		/* Seconds east of UTC.  */
const char *tm_zone;		/* Timezone abbreviation.  */
# else
long int __tm_gmtoff;		/* Seconds east of UTC.  */
const char *__tm_zone;	/* Timezone abbreviation.  */
# endif
};
//年份
tm.tm_year+1900
//月份
tm.tm_year+1
//日
tm.tm_mday
//星期
tm.tm_wday+1
//uint64_t--->std::tm
#include <ctime>
#include <cstdint>
int uint64_to_tm(uint64_t timestamp, struct tm* tm_ptr) //timestamp为秒时间戳
{
    // 将时间戳转换为 time_t 类型
    time_t time = static_cast<time_t>(timestamp);
    // 转换为本地时间
    localtime_r(&sec_timestamp, tm_ptr);
    return 0;
}

int uint64_to_tm(uint64_t timestamp, struct tm* tm_ptr) //timestamp为秒时间戳
{
    // 手动给UTC时间戳+8小时调整时区到东八区(北京时间),得到本地时间戳
    int64_t milli = timestamp + (int64_t)8 * 60 * 60 * 1000; 
    auto mTime = std::chrono::milliseconds(milli);
    auto tp = std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds>(mTime);
    auto tt = std::chrono::system_clock::to_time_t(tp);//chrono::time_point转time_t
    *tm_ptr = std::gmtime(&tt);
}

//C++20标准
#include <chrono>
#include <format>

void uint64_to_tm(uint64_t timestamp, struct tm* tm_ptr) 
{
    auto tp = std::chrono::system_clock::from_time_t(timestamp);
    zoned_time zt{"Asia/Shanghai", tp}; // 指定时区
    *tm_ptr = std::chrono::get_tm(zt);
}

(3)datetime获取unix时间戳(uint64_t或time_t)

//std::tm--->uint64_t
#include <ctime>
#include <chrono>
#include <cstdint>
uint64_t tm_to_uint64(const std::tm& tm_time) //返回秒
{
    // 将 std::tm 转换为 time_t
    std::time_t time_t_value = mktime(const_cast<std::tm*>(&tm_time));
    // 转换为 uint64_t
    return static_cast<uint64_t>(time_t_value);
}

// 将 std::chrono::system_clock::time_point 转换为 uint64_t 时间戳(秒为单位)
uint64_t chrono_to_uint64(const std::chrono::system_clock::time_point& time_point) 
{
    // 转换为秒
    auto duration = time_point.time_since_epoch();
    auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration);
    return static_cast<uint64_t>(seconds.count());
}

(4)由UTC unix毫秒时间戳获取YYYY-MM-DD HH:MM:SS.mmm字符串

// utc unix毫秒时间戳(13位)获取YYYY-MM-DD HH:MM:SS.mmm字符串
std::string timestampToString(int64_t timestamp_ms) 
{
  // Convert milliseconds to seconds and remainder milliseconds
  std::time_t seconds = timestamp_ms / 1000;
  int milliseconds = timestamp_ms % 1000;

  //使用std::localtime函数将seconds(以秒为单位的时间)转换为本地时间,结果存储在tm结构体中
  std::tm *tm = std::localtime(&seconds);

  //存储格式化后的时间字符串(不包括毫秒部分)。大小为24,足以存储"YYYY-MM-DD HH:MM:SS"格式的字符串
  //将tm结构中的时间信息格式化为"YYYY-MM-DD HH:MM:SS"格式的字符串,并存储在buffer中
  char buffer[24];
  std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm);

  //将buffer中的字符串和毫秒部分拼接起来
  //std::setfill('0')设置填充字符为'0',std::setw(3)设置字段宽度为3,确保毫秒部分总是三位数字,不足时前面补零。
  std::ostringstream oss;
  oss << buffer << '.' << std::setfill('0') << std::setw(3) << milliseconds;

  return oss.str();
}

(5)HH:MM:SS时间字符串转成ms

//将形如1:30:21格式时间段转换为毫秒数
static uint64_t timeStringToMillis(const std::string &timeString)
{
    int hours, minutes, seconds;
    sscanf(timeString.c_str(), "%d:%d:%d", &hours, &minutes, &seconds);
    return (hours * 3600 + minutes * 60 + seconds) * 1000;
}

(6)YYYY-MM-DD HH:MM:SS.mmm字符串获取UTC unix毫秒时间戳(13位)

//YYYY-MM-DD HH:MM:SS.mmm字符串获取utc unix毫秒时间戳(13位)
int64_t stringToTimestamp(const std::string &datetime_str) 
{
  std::tm tm = {};
  int milliseconds = 0;

  // Manually parse the string "YYYY-MM-DD HH:MM:SS.mmm"
  if (sscanf(datetime_str.c_str(), "%4d-%2d-%2d %2d:%2d:%2d.%3d", &tm.tm_year,
             &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec,
             &milliseconds) != 7) 
  {
    throw std::runtime_error("Failed to parse date-time string");
  }

  // Adjust fields for tm structure
  tm.tm_year -= 1900; // tm_year is years since 1900
  tm.tm_mon -= 1;     // tm_mon is 0-based (0 = January)

  // Convert to time_t (seconds since epoch) and add milliseconds
  std::time_t seconds = std::mktime(&tm);
  if (seconds == -1) 
  {
    throw std::runtime_error("Failed to convert to time_t");
  }

  return static_cast<int64_t>(seconds) * 1000 + milliseconds;
}

(6)将yyyy-MM-dd hh:mm:ss字符串转成yyyyMMddhhmmss字符串

std::string convertDateFormat(const std::string& date_str) 
{
    if (date_str.empty()) {
        throw std::invalid_argument("Input date string is empty");
    }

    // 检查输入长度是否符合 "YYYY-MM-DD HH:MM:SS"
    if (date_str.size() != 19) {
        throw std::runtime_error("Invalid date string format: " + date_str);
    }

    // 提取并验证格式
    std::string year = date_str.substr(0, 4);
    std::string month = date_str.substr(5, 2);
    std::string day = date_str.substr(8, 2);
    std::string hour = date_str.substr(11, 2);
    std::string minute = date_str.substr(14, 2);
    std::string second = date_str.substr(17, 2);

    // 确保字符的位置符合标准格式
    if (date_str[4] != '-' || date_str[7] != '-' || date_str[10] != ' ' || 
        date_str[13] != ':' || date_str[16] != ':') {
        throw std::runtime_error("Invalid date string format: " + date_str);
    }

    // 合并为目标格式
    std::string result = year + month + day + hour + minute + second;
    return result;
}

(8)获取当前UNIX时间戳

uint64_t nowMS() 
{
    std::chrono::time_point<std::chrono::system_clock> now =
        std::chrono::system_clock::now();
    auto duration = now.time_since_epoch();
    auto millis =
        std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
    return millis;
}

(9)根据时间戳获取当日零点时间戳

//timestamp(单位ms)
//返回值(单位ms)
time_t midnight(uint64_t timestamp) 
{
    time_t tmt = timestamp / 1000;
    tm* pTM = localtime(&tmt);
    pTM->tm_hour = 0;
    pTM->tm_min = 0;
    pTM->tm_sec = 0;
    auto midnight_timet = mktime(pTM) * 1000;
    return midnight_timet;
}

uint64_t midnite = midnight(ms_now);    // 当天0点时间戳

(10)库表中时间类型的选择

  • 日期时间类型:DATETIME(4) 表示时间戳的精度为4位小数(毫秒级)。如果你需要更高的精度(例如微秒级),可以使用 DATETIME(6)
  • 时间戳类型:存储s/ms/us,使用bigint

(11)c++代码中时间类型

[!NOTE]

uint64_t和time_t可直接强转

std::time_t time_t_value;
uint64_t uint64_value static_cast<uint64_t>(time_t_value);
  • 日期时间类型:std::tmstd::chrono::system_clock::time_point

  • 时间戳类型:uint64_ttime_t,这两者数值一致。

    秒时间戳:10位整数。

    毫秒时间戳:13位整数。

(12)MySQL语句中时间戳处理

  • time_t(time_t是时间戳epoch到现在的seconds)uint_64可表示秒,毫秒,微秒时间戳,精度够

  • 字段类型为时间戳:存,time_tuint_64格式变量在execute()中执行插入到字段时,都要先转double(秒)或long long(微秒,微秒用long long约定俗成)。取,用getDouble()getLLong()直接取库表里的字面值。

  • 比较时间先后,一律使用时间戳进行比较。datetime也先转成时间戳再比较!

获取日期当天零点

-- 获取指定datetime的当天0点
SELECT DATE_FORMAT(`datetime字段`, '%Y-%m-%d 00:00:00') AS midnight;

-- 获取现在时间的当天0点
SELECT DATE_FORMAT(NOW(), '%Y-%m-%d 00:00:00') AS midnight;

unix时间戳(int64_t或time_t)存取

  • 库表字段数据类型假设为bigint,11位,但存入库中不直接存uint64_t,类型time_tuint64_t需要转成double或long long才能被libzdb库的函数execute使用

    //存入
    uint64_t tm;
    auto tmTmp = static_cast<long long>(tm);
    conn.execute("UPDATE e_warning_Info SET is_sent_user=1 WHERE tm=?", tmTmp);
    或
    auto tmTmp = static_cast<double>(tm);
    conn.execute("UPDATE e_warning_Info SET is_sent_user=1 WHERE tm=?", tmTmp);
    
  • getDouble()getLLong()读取库表中unix时间戳字面值

    getDouble()会不会是使用getTimestamp()?不要用getTimestamp()!!!!!

    //获取表内字面值,强转long long int。(无法获取字面值)
    auto rslt = conn.executeQuery("...");
    uint64_t tm = rslt.getLLong("tm");//一般用来取ms或us
    cout<<"tm="<<tm<<endl;//和数据库中所存的值相等
    或
    uint64_t tm = rslt.getDouble("tm");//用来取秒时间戳
    cout<<"tm="<<tm<<endl;//和数据库中所存的值不等
    cout<<"tm="<<(double)(tm)<<endl;//和数据库中所存字面值相等
    

unix时间戳(int64_t或time_t)转成datetime存取

//MySQL会根据服务器所在时区选择UTC+8(服务器在中国)
UNIX_TIMESTAMP(datetime)//datetime转unix时间戳(单位s,拿到datetime先减去8小时得到UTC时间再计算时间戳)
FROM_UNIXTIME(uint64_t)//unix时间戳(s)转datetime(拿到uint64_t转成datetime后加上8小时)

库表字段数据类型设为datetime(4),秒后面4位小数。

调用函数FROM_UNIXTIME()将unix时间戳(单位s)转成datetime插入

datetime类型字段用conn.getDateTime("field")从查询结果中获取,获取结果是struct tm类型

//int64_t或time_t--->datetime后存入
uint64_t tmStmp = dfVec[0]->sampTime();//ms

conn21.execute(
"INSERT INTO "
"e_mining_signaldata_frm_info "
"(dev_id,samp_time,class_id,samp_intvl,valNum,vecSize,outLen,frm_"
"loc,fx,fy,fz,dis)"
"values(?,FROM_UNIXTIME(?),?,?,?,?,?,?,?,?,?,?) "//ms转成datetime
" ON DUPLICATE KEY UPDATE samp_intvl= values(samp_intvl),valNum= "
"values(valNum),vecSize= values(vecSize),outLen= values(outLen)",
(uint8_t)devId,  // dev_id
(double)tmStmp / 1000.0, // tmStmp是uint64_t毫秒时间戳,转秒,因此要再转double
classId2,        // class_id
deltT,           // curt_ch
valNum,          // channels
vecSize,
pcoParm.outLen, // ch_pnts
(relLocFromTime(tmStmp) / std::to_string(classId2) / std::to_string(tmStmp)).c_str(), // frm_loc
pcoLocParm.fx,
pcoLocParm.fy,
pcoLocParm.fz,
pcoLocParm.dis_y);

//datetime--->int64_t或time_t取出
auto rslt = conn.executeQuery("SELECT UNIX_TIMESTAMP(field) from table");
uint64_t tm = rslt.getLLong("field");

//直接读取datetime类型字段,获取struct tm类型(若需要uint64_t类型可再转)
std::tm = conn.getDateTime("field");

1. time_t

  • 定义time_t 是 C 和 C++ 标准库中用于表示时间的类型,通常用于表示从 1970 年 1 月 1 日 00:00:00 UTC 到当前时间的秒数(即 Unix 时间戳)。
  • 类型time_t 的具体实现依赖于平台和编译器,但通常是 longlong long 类型。在大多数现代系统中,time_t 是一个 64 位有符号整数。
  • 用途:主要用于时间相关的操作,如获取当前时间、时间间隔计算等。
  • 范围:由于 time_t 是有符号整数,其范围通常是 -2^632^63-1(在 64 位系统中)。

2. uint64_t

  • 定义uint64_t 是 C++ 标准库中定义的无符号 64 位整数类型,属于 <cstdint> 头文件中的固定宽度整数类型。
  • 类型uint64_t 是一个无符号 64 位整数。
  • 用途:用于需要精确表示 64 位无符号整数的场景,如大整数计算、文件大小、内存地址等。
  • 范围02^64-1

对比

特性 time_t uint64_t
类型 通常是 64 位有符号整数 64 位无符号整数
范围 -2^632^63-1 02^64-1
用途 时间相关的操作 大整数计算、文件大小等
标准库 <ctime><chrono> <cstdint>
posted @ 2025-06-12 15:48  仰望星河Leon  阅读(108)  评论(0)    收藏  举报