获取毫秒级的高精度时间戳是一个很常见的需求,尤其在向日志文件中输出信息时通常需要附带格式化的时间戳, 下面在不同的语言中尝试生成形如 [2024-07-30 00:52:47.379] 的高精度时间戳。(虽然在大部分语境下,时间戳是一个非负整数,但是为了方便使用,这里统一为含毫秒的固定格式的时间字符串)
C++ 对于C++,标准库 chrono 可以获取高精度的时间, 然后通过 localtime 函数进行格式化,由于它不支持毫秒部分的格式化,我们还需要对毫秒进行额外处理。
下面是一个生成时间戳的示例函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static std::string time_stamp () { auto now = std::chrono::system_clock::now (); auto now_time_t = std::chrono::system_clock::to_time_t (now); auto now_ms = std::chrono::duration_cast <std::chrono::milliseconds>( now.time_since_epoch ()) % 1000 ; char buffer[32 ]{}; std::strftime (static_cast <char *>(buffer), sizeof (buffer), "[%Y-%m-%d %H:%M:%S" , localtime (&now_time_t )); std::snprintf (static_cast <char *>(buffer) + 20 , 7 , ".%03d]" , static_cast <int >(now_ms.count ())); return std::string{static_cast <char *>(buffer)}; }
这里使用的是 std::strftime 函数,它把格式化的时间写入到指定的字符数组中,除此之外,还有一个类似的函数 std::put_time 把格式化的时间输出到流。
我们使用的 localtime 函数无法保证线程安全,在 Windows 和 Linux 平台上分别为其提供了线程安全的版本:localtime_s和 localtime_r,都需要传入两个指针参数,需要特别注意,这两个平台提供的参数顺序是 TM 相反的!
我们可以通过条件编译来支持不同的平台,在其它情况下则通过加锁来保证线程安全。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 static std::string time_stamp () { auto now = std::chrono::system_clock::now (); auto now_time_t = std::chrono::system_clock::to_time_t (now); auto now_ms = std::chrono::duration_cast <std::chrono::milliseconds>( now.time_since_epoch ()) % 1000 ; char buffer[32 ]{}; struct tm timeinfo {}; #if defined(_MSC_VER) localtime_s (&timeinfo, &now_time_t ); #elif defined(__unix__) localtime_r (&now_time_t , &timeinfo); #else static std::mutex mtx; { std::lock_guard<std::mutex> lock (mtx) ; timeinfo = *localtime (&now_time_t ); } #endif std::strftime (static_cast <char *>(buffer), sizeof (buffer), "[%Y-%m-%d %H:%M:%S" , &timeinfo); std::snprintf (static_cast <char *>(buffer) + 20 , 6 , ".%03d]" , static_cast <int >(now_ms.count ())); return std::string{static_cast <char *>(buffer)}; }
这里没有考虑 C++20 或者更新的语法标准,期待后续可以有更简洁的写法吧。
Fortran 对于 Fortran 这个老古董语言,要获取时间戳就比 C++ 还困难了,我们只能手动地实现格式化的部分,下面是一个示例子程序,使用 mingw-w64 gfortran 可以顺利编译运行,其它平台和编译器并没有测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 subroutine get_timestamp(timestamp) character (len=30 ) :: timestamp character (len=8 ) :: time_str character (len=10 ) :: date_str character (len=4 ) :: year character (len=2 ) :: month, day, hour, minute, second integer :: millis call date_and_time (date=date_str, time=time_str) call system_clock (count =millis) year = date_str(1 :4 ) month = date_str(5 :6 ) day = date_str(7 :8 ) hour = time_str(1 :2 ) minute = time_str(3 :4 ) second = time_str(5 :6 ) write (timestamp, '("[",A,"-",A,"-",A," ",A,":",A,":",A,".",I3.3,"] ")' ) & year, month, day, hour, minute, second, mod (millis, 1000 ) end subroutine get_timestamp
Python 在 Python 中获取指定格式的高精度时间戳是非常简单的,需要先导入 datetime 模块中的 datetime 类,然后一行代码就可以搞定
1 2 3 from datetime import datetimetimestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f" )[:-3 ]
这里 [:-3] 是为了只截取毫秒部分的前三个数字。
MATLAB 在 MATLAB 中获取指定格式的高精度时间戳同样非常简单,基于 datestr 函数即可实现
1 timestamp = datestr(now, 'yyyy-mm-dd HH:MM:SS.FFF' );
注意 datastr 函数在较新的版本中不再建议使用,MATLAB 建议使用 datetime 函数替代
1 timestamp = datetime("now" ,"Format" ,"yyyy-MM-dd HH:mm:ss.SSS" );
Julia 在 Julia 中获取指定格式的高精度时间戳非常简单,只需要使用内置函数 now(),然后通过调用内置模块 Dates 的 Dates.format() 函数进行格式化即可
1 2 3 using Datestimestamp = Dates.format(now(), "yyyy-mm-dd HH:MM:SS.sss" )