From 5addaffa5665f353c874f45505914ab692535c24 Mon Sep 17 00:00:00 2001 From: Corey D Date: Tue, 4 Jul 2023 21:42:45 +1000 Subject: [PATCH] Add ParseTime (strptime) native (#1697) * Add GetTimeStamp (strptime) native * Fix description * Fix typos * Fix issues * Rewrite ParseTime * Fix docstrings * Fix ParseTime * Clarify docs --- core/logic/smn_core.cpp | 46 +++++++++++++++++++++++++++++++++++ plugins/include/sourcemod.inc | 18 ++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/core/logic/smn_core.cpp b/core/logic/smn_core.cpp index 490a6a00da..8e90d40310 100644 --- a/core/logic/smn_core.cpp +++ b/core/logic/smn_core.cpp @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include #include "common_logic.h" #include "Logger.h" @@ -273,6 +275,49 @@ static cell_t FormatTime(IPluginContext *pContext, const cell_t *params) return 1; } +static int ParseTime(IPluginContext *pContext, const cell_t *params) +{ + char *datetime; + char *format; + pContext->LocalToStringNULL(params[1], &datetime); + pContext->LocalToStringNULL(params[2], &format); + + if (format == NULL) + { + format = const_cast(bridge->GetCvarString(g_datetime_format)); + } + else if (!format[0]) + { + return pContext->ThrowNativeError("Time format string cannot be empty."); + } + if (!datetime || !datetime[0]) + { + return pContext->ThrowNativeError("Date/time string cannot be empty."); + } + + // https://stackoverflow.com/a/33542189 + std::tm t{}; + std::istringstream input(datetime); + + auto previousLocale = input.imbue(std::locale::classic()); + input >> std::get_time(&t, format); + bool failed = input.fail(); + input.imbue(previousLocale); + + if (failed) + { + return pContext->ThrowNativeError("Invalid date/time string or time format."); + } + +#if defined PLATFORM_WINDOWS + return _mkgmtime(&t); +#elif defined PLATFORM_LINUX || defined PLATFORM_APPLE + return timegm(&t); +#else + return pContext->ThrowNativeError("Platform has no implemented UTC conversion for std::tm to std::time_t"); +#endif +} + static cell_t GetPluginIterator(IPluginContext *pContext, const cell_t *params) { IPluginIterator *iter = scripts->GetPluginIterator(); @@ -1088,6 +1133,7 @@ REGISTER_NATIVES(coreNatives) {"ThrowError", ThrowError}, {"GetTime", GetTime}, {"FormatTime", FormatTime}, + {"ParseTime", ParseTime}, {"GetPluginIterator", GetPluginIterator}, {"MorePlugins", MorePlugins}, {"ReadPlugin", ReadPlugin}, diff --git a/plugins/include/sourcemod.inc b/plugins/include/sourcemod.inc index 876a6d6834..1927015680 100644 --- a/plugins/include/sourcemod.inc +++ b/plugins/include/sourcemod.inc @@ -412,6 +412,24 @@ native int GetTime(int bigStamp[2]={0,0}); */ native void FormatTime(char[] buffer, int maxlength, const char[] format, int stamp=-1); +/** + * Parses a string representing a date and/or time into a unix timestamp. + * The timezone is always interpreted as UTC/GMT. + * + * See this URL for valid parameters: + * https://en.cppreference.com/w/cpp/io/manip/get_time + * + * Note that available parameters depends on support from your operating system. + * In particular, ones highlighted in yellow on that page are not currently + * available on Windows and should be avoided for portable plugins. + * + * @param dateTime Date and/or time string. + * @param format Formatting rules (passing NULL_STRING will use the rules defined in sm_datetime_format). + * @return 32bit timestamp (number of seconds since unix epoch). + * @error Invalid date/time string or time format. + */ +native int ParseTime(const char[] dateTime, const char[] format); + /** * Loads a game config file. *