|
6 | 6 | #include <list> |
7 | 7 | #include <mutex> // IWYU pragma: keep |
8 | 8 | #include <sstream> |
| 9 | +#include <cmath> |
9 | 10 |
|
10 | 11 | #include <chrono> |
11 | 12 | #include <ctime> |
|
29 | 30 |
|
30 | 31 | #else // _WIN32 |
31 | 32 |
|
| 33 | +#if ((defined(__APPLE__) && defined(__MACH__)) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)) |
| 34 | + #define ST_MTIME_NSEC st_mtimespec.tv_nsec |
| 35 | +#elif defined(__unix__) |
| 36 | + #define ST_MTIME_NSEC st_mtim.tv_nsec |
| 37 | +#endif |
| 38 | + |
32 | 39 | #include <cerrno> |
33 | 40 | #include <cstring> |
34 | 41 | #include <dirent.h> |
@@ -248,6 +255,94 @@ namespace Filesystem |
248 | 255 | return month_names.at(file_timeinfo.tm_mon) + date.str(); |
249 | 256 | } |
250 | 257 |
|
| 258 | + std::string FileStatus::generalizedTimeString() const |
| 259 | + { |
| 260 | + if (!is_ok_) |
| 261 | + return "19700101000000"; |
| 262 | + |
| 263 | + // The generalized time format is: YYYYMMDDHHMMSS |
| 264 | + // Example: 19970716210700 |
| 265 | + |
| 266 | + static constexpr auto tm_year_base_year = 1900; |
| 267 | + std::tm file_timeinfo{}; |
| 268 | + int time_ms = 0; |
| 269 | + |
| 270 | +#if defined(__unix__) |
| 271 | + int time_carryover_seconds = 0; |
| 272 | + if(file_status_.ST_MTIME_NSEC != 0) |
| 273 | + { |
| 274 | + time_ms = static_cast<time_t>(std::round(static_cast<double>(file_status_.ST_MTIME_NSEC) / 1000000)); |
| 275 | + time_carryover_seconds = time_ms / 1000; |
| 276 | + time_ms = time_ms % 1000; |
| 277 | + } |
| 278 | + auto time_sec = file_status_.st_mtime + time_carryover_seconds; |
| 279 | + gmtime_r (&time_sec, &file_timeinfo); |
| 280 | + |
| 281 | +#elif defined (_WIN32) |
| 282 | + // Query write time from WIN32 API, as the POSIX API does not reveal milliseconds on Windows |
| 283 | + |
| 284 | + const std::wstring w_path = StrConvert::Utf8ToWide(path_); |
| 285 | + HANDLE file_handle = CreateFileW(w_path.c_str() |
| 286 | + , GENERIC_READ |
| 287 | + , FILE_SHARE_READ |
| 288 | + , NULL |
| 289 | + , OPEN_EXISTING |
| 290 | + , 0 |
| 291 | + , NULL); |
| 292 | + |
| 293 | + if(file_handle == INVALID_HANDLE_VALUE) |
| 294 | + { |
| 295 | + return "19700101000000"; |
| 296 | + } |
| 297 | + |
| 298 | + FILETIME filetime_create{}; // Unused |
| 299 | + FILETIME filetime_access{}; // Unused |
| 300 | + FILETIME filetime_write{}; |
| 301 | + |
| 302 | + // Retrieve the file times for the file. |
| 303 | + if (!GetFileTime(file_handle, &filetime_create, &filetime_access, &filetime_write)) |
| 304 | + return "19700101000000"; |
| 305 | + |
| 306 | + CloseHandle(file_handle); |
| 307 | + |
| 308 | + // Convert the last-write-time from a filetime to systemtime (in UTC). |
| 309 | + SYSTEMTIME file_write_time_utc{}; |
| 310 | + FileTimeToSystemTime(&filetime_write, &file_write_time_utc); |
| 311 | + |
| 312 | + // Fill the time info struct just as the POSIX api would have, but also set the milliseconds |
| 313 | + file_timeinfo.tm_year = file_write_time_utc.wYear - tm_year_base_year; |
| 314 | + file_timeinfo.tm_mon = file_write_time_utc.wMonth - 1; |
| 315 | + file_timeinfo.tm_mday = file_write_time_utc.wDay; |
| 316 | + file_timeinfo.tm_hour = file_write_time_utc.wHour; |
| 317 | + file_timeinfo.tm_min = file_write_time_utc.wMinute; |
| 318 | + file_timeinfo.tm_sec = file_write_time_utc.wSecond; |
| 319 | + time_ms = file_write_time_utc.wMilliseconds; |
| 320 | + |
| 321 | +#else |
| 322 | + static std::mutex mtx; |
| 323 | + { |
| 324 | + std::lock_guard<std::mutex> lock(mtx); |
| 325 | + file_timeinfo = *std::gmtime (&file_status_.st_mtime); |
| 326 | + } |
| 327 | +#endif |
| 328 | + |
| 329 | + std::stringstream date; |
| 330 | + date << std::setw( 4 ) << ( file_timeinfo.tm_year + tm_year_base_year ) |
| 331 | + << std::setw( 2 ) << std::setfill( '0' ) << ( file_timeinfo.tm_mon + 1 ) |
| 332 | + << std::setw( 2 ) << std::setfill( '0' ) << file_timeinfo.tm_mday |
| 333 | + << std::setw( 2 ) << std::setfill( '0' ) << file_timeinfo.tm_hour |
| 334 | + << std::setw( 2 ) << std::setfill( '0' ) << file_timeinfo.tm_min |
| 335 | + << std::setw( 2 ) << std::setfill( '0' ) << file_timeinfo.tm_sec; |
| 336 | + |
| 337 | + // Append milliseconds only if available |
| 338 | + if (time_ms > 0) |
| 339 | + { |
| 340 | + date << "." << std::setw( 3 ) << std::setfill( '0' ) << time_ms; |
| 341 | + } |
| 342 | + |
| 343 | + return date.str(); |
| 344 | + } |
| 345 | + |
251 | 346 | bool FileStatus::canOpenDir() const |
252 | 347 | { |
253 | 348 | if (!is_ok_) |
|
0 commit comments