Mercurial > foo_out_sdl
view foosdk/sdk/pfc/filetimetools.cpp @ 1:20d02a178406 default tip
*: check in everything else
yay
| author | Paper <paper@tflc.us> |
|---|---|
| date | Mon, 05 Jan 2026 02:15:46 -0500 |
| parents | |
| children |
line wrap: on
line source
#include "pfc-lite.h" #include "filetimetools.h" #include "timers.h" #include <math.h> namespace { class exception_time_error {}; } using namespace pfc; #ifndef _WIN32 namespace { typedef uint16_t WORD; typedef struct _SYSTEMTIME { WORD wYear; WORD wMonth; WORD wDayOfWeek; WORD wDay; WORD wHour; WORD wMinute; WORD wSecond; WORD wMilliseconds; } SYSTEMTIME, * PSYSTEMTIME, * LPSYSTEMTIME; } static void SystemTimeToNix(const SYSTEMTIME& st, struct tm& Time) { memset(&Time, 0, sizeof(Time)); Time.tm_sec = st.wSecond; Time.tm_min = st.wMinute; Time.tm_hour = st.wHour; Time.tm_mday = st.wDay; Time.tm_mon = st.wMonth - 1; Time.tm_year = st.wYear - 1900; } static t_filetimestamp ExportSystemTime(const SYSTEMTIME& st) { struct tm Time; SystemTimeToNix(st, Time); return pfc::fileTimeUtoW(mktime(&Time)); } static t_filetimestamp ExportSystemTimeLocal(const SYSTEMTIME& st) { struct tm Time, Local; SystemTimeToNix(st, Time); time_t t = mktime(&Time); localtime_r(&t, &Local); return pfc::fileTimeUtoW(mktime(&Local)); } static void SystemTimeFromNix(SYSTEMTIME& st, struct tm const& Time, t_filetimestamp origTS) { memset(&st, 0, sizeof(st)); st.wSecond = Time.tm_sec; st.wMinute = Time.tm_min; st.wHour = Time.tm_hour; st.wDay = Time.tm_mday; st.wDayOfWeek = Time.tm_wday; st.wMonth = Time.tm_mon + 1; st.wYear = Time.tm_year + 1900; st.wMilliseconds = (origTS % filetimestamp_1second_increment) / (filetimestamp_1second_increment/1000); } static bool MakeSystemTime(SYSTEMTIME& st, t_filetimestamp ts) { time_t t = (time_t)pfc::fileTimeWtoU(ts); struct tm Time; if (gmtime_r(&t, &Time) == NULL) return false; SystemTimeFromNix(st, Time, ts); return true; } static bool MakeSystemTimeLocal(SYSTEMTIME& st, t_filetimestamp ts) { time_t t = (time_t)pfc::fileTimeWtoU(ts); struct tm Time; if (localtime_r(&t, &Time) == NULL) return false; SystemTimeFromNix(st, Time, ts); return true; } #else static t_filetimestamp ExportSystemTime(const SYSTEMTIME& st) { t_filetimestamp base; if (!SystemTimeToFileTime(&st, (FILETIME*)&base)) throw exception_time_error(); return base; } static t_filetimestamp ExportSystemTimeLocal(const SYSTEMTIME& st) { #ifdef FOOBAR2000_DESKTOP_WINDOWS t_filetimestamp base, out; if (!SystemTimeToFileTime(&st, (FILETIME*)&base)) throw exception_time_error(); if (!LocalFileTimeToFileTime((const FILETIME*)&base, (FILETIME*)&out)) throw exception_time_error(); return out; #else SYSTEMTIME UTC; if (!TzSpecificLocalTimeToSystemTime(NULL, &st, &UTC)) throw exception_time_error(); return ExportSystemTime(UTC); #endif } static bool MakeSystemTime(SYSTEMTIME& st, t_filetimestamp ts) { if (ts == filetimestamp_invalid) return false; return !!FileTimeToSystemTime((const FILETIME*)&ts, &st); } static bool MakeSystemTimeLocal(SYSTEMTIME& st, t_filetimestamp ts) { if (ts == filetimestamp_invalid) return false; #ifdef FOOBAR2000_DESKTOP_WINDOWS FILETIME ft; if (FileTimeToLocalFileTime((FILETIME*)&ts, &ft)) { if (FileTimeToSystemTime(&ft, &st)) { return true; } } return false; #else SYSTEMTIME UTC; if (FileTimeToSystemTime((FILETIME*)&ts, &UTC)) { if (SystemTimeToTzSpecificLocalTime(NULL, &UTC, &st)) return true; } return false; #endif } #endif // _WIN32 static bool is_spacing(char c) { return c == ' ' || c == 10 || c == 13 || c == '\t'; } static unsigned ParseDateElem(const char* ptr, t_size len) { unsigned ret = 0; for (t_size walk = 0; walk < len; ++walk) { const char c = ptr[walk]; if (c < '0' || c > '9') throw exception_time_error(); ret = ret * 10 + (unsigned)(c - '0'); } return ret; } static bool st_sanity(SYSTEMTIME const& st) { return st.wYear >= 1601 && st.wMonth >= 1 && st.wMonth <= 12 && st.wDay >= 1 && st.wDay <= 31 && st.wHour < 24 && st.wMinute < 60 && st.wSecond < 60 && st.wMilliseconds < 1000; } static t_filetimestamp filetimestamp_from_string_internal(const char* date, bool local) { // Accepted format // YYYY-MM-DD HH:MM:SS try { SYSTEMTIME st = {}; st.wDay = 1; st.wMonth = 1; unsigned walk = 0; auto worker = [&](unsigned n) { auto ret = ParseDateElem(date + walk, n); walk += n; if (ret > UINT16_MAX) throw exception_time_error(); return (WORD)ret;; }; auto skip = [&](char c) { if (date[walk] == c) ++walk; }; auto skipSpacing = [&] { while (is_spacing(date[walk])) ++walk; }; skipSpacing(); st.wYear = worker(4); skip('-'); st.wMonth = worker(2); skip('-'); st.wDay = worker(2); skipSpacing(); st.wHour = worker(2); skip(':'); st.wMinute = worker(2); skip(':'); st.wSecond = worker(2); if (date[walk] == '.') { double v = pfc::string_to_float(date + walk); st.wMilliseconds = (WORD)floor(v * 1000.f); // don't ever round up, don't want to handle ms of 1000 } if (!st_sanity(st)) throw exception_time_error(); if (local) { return ExportSystemTimeLocal(st); } else { return ExportSystemTime(st); } } catch (exception_time_error const &) { return filetimestamp_invalid; } } namespace pfc { t_filetimestamp filetimestamp_from_string(const char* date) { return filetimestamp_from_string_internal(date, true); } t_filetimestamp filetimestamp_from_string_utc(const char* date) { return filetimestamp_from_string_internal(date, false); } static constexpr char g_invalidMsg[] = "<invalid timestamp>"; pfc::string_formatter format_filetimestamp(t_filetimestamp p_timestamp) { try { SYSTEMTIME st; if (MakeSystemTimeLocal(st, p_timestamp)) { pfc::string_formatter buffer; buffer << pfc::format_uint(st.wYear, 4) << "-" << pfc::format_uint(st.wMonth, 2) << "-" << pfc::format_uint(st.wDay, 2) << " " << pfc::format_uint(st.wHour, 2) << ":" << pfc::format_uint(st.wMinute, 2) << ":" << pfc::format_uint(st.wSecond, 2); return buffer; } } catch (...) {} return g_invalidMsg; } pfc::string_formatter format_filetimestamp_ms(t_filetimestamp p_timestamp) { try { SYSTEMTIME st; if (MakeSystemTimeLocal(st, p_timestamp)) { pfc::string_formatter buffer; buffer << pfc::format_uint(st.wYear, 4) << "-" << pfc::format_uint(st.wMonth, 2) << "-" << pfc::format_uint(st.wDay, 2) << " " << pfc::format_uint(st.wHour, 2) << ":" << pfc::format_uint(st.wMinute, 2) << ":" << pfc::format_uint(st.wSecond, 2) << "." << pfc::format_uint(st.wMilliseconds, 3); return buffer; } } catch (...) {} return g_invalidMsg; } pfc::string_formatter format_filetimestamp_utc(t_filetimestamp p_timestamp) { try { SYSTEMTIME st; if (MakeSystemTime(st, p_timestamp)) { pfc::string_formatter buffer; buffer << pfc::format_uint(st.wYear, 4) << "-" << pfc::format_uint(st.wMonth, 2) << "-" << pfc::format_uint(st.wDay, 2) << " " << pfc::format_uint(st.wHour, 2) << ":" << pfc::format_uint(st.wMinute, 2) << ":" << pfc::format_uint(st.wSecond, 2); return buffer; } } catch (...) {} return g_invalidMsg; } } // namespace foobar2000_io namespace { struct dateISO_t { unsigned Y, M, D; unsigned h, m, s; double sfrac; int tzdelta; }; dateISO_t read_ISO_8601(const char* dateISO) { dateISO_t ret = {}; // 2022-01-26T13:44:51.200000Z // 2010-02-19T14:54:23.031+08:00 // 2022-01-27T11:01:49+00:00 // 2022-01-27T11:01:49Z // 20220127T110149Z unsigned walk = 0; auto worker = [&](unsigned n) { auto ret = ParseDateElem(dateISO + walk, n); walk += n; return ret; }; auto skip = [&](char c) { if (dateISO[walk] == c) ++walk; }; auto expect = [&](char c) { if (dateISO[walk] != c) throw exception_time_error(); ++walk; }; ret.Y = worker(4); skip('-'); ret.M = worker(2); skip('-'); ret.D = worker(2); expect('T'); ret.h = worker(2); skip(':'); ret.m = worker(2); skip(':'); ret.s = worker(2); if (dateISO[walk] == '.') { unsigned base = walk; ++walk; while (pfc::char_is_numeric(dateISO[walk])) ++walk; ret.sfrac = pfc::string_to_float(dateISO + base, walk - base); } if (dateISO[walk] == '+' || dateISO[walk] == '-') { bool neg = dateISO[walk] == '-'; ++walk; unsigned tz_h = worker(2); if (tz_h >= 24) throw exception_time_error(); skip(':'); unsigned tz_m = worker(2); if (tz_m >= 60) throw exception_time_error(); tz_m += tz_h * 60; ret.tzdelta = neg ? (int)tz_m : -(int)tz_m; // reversed! it's a timezone offset, have to *add* it if timezone has a minus } return ret; } } t_filetimestamp pfc::filetimestamp_from_string_ISO_8601(const char* dateISO) { try { auto elems = read_ISO_8601(dateISO); SYSTEMTIME st = {}; st.wDay = 1; st.wMonth = 1; st.wYear = (WORD)elems.Y; st.wMonth = (WORD)elems.M; st.wDay = (WORD)elems.D; st.wHour = (WORD)elems.h; st.wMinute = (WORD)elems.m; st.wSecond = (WORD)elems.s; st.wMilliseconds = (WORD)floor(elems.sfrac * 1000.f); if (!st_sanity(st)) throw exception_time_error(); auto ret = ExportSystemTime(st); ret += filetimestamp_1second_increment * elems.tzdelta * 60; return ret; } catch (...) { return filetimestamp_invalid; } }
