diff --git a/Editor/AGS.Editor/Resources/agsdefns.sh b/Editor/AGS.Editor/Resources/agsdefns.sh index 457e8d46c7..d5d57df3e6 100644 --- a/Editor/AGS.Editor/Resources/agsdefns.sh +++ b/Editor/AGS.Editor/Resources/agsdefns.sh @@ -2266,6 +2266,12 @@ builtin struct Maths { builtin managed struct DateTime { /// Gets the current date and time on the player's system. readonly import static attribute DateTime* Now; // $AUTOCOMPLETESTATICONLY$ + /// Creates DateTime object from the provided calendar and, optionally, time components + import static DateTime* CreateFromDate(int year, int month, int day, int hour = 0, int minute = 0, int second = 0); // $AUTOCOMPLETESTATICONLY$ + /// Creates DateTime object from the provided time components + import static DateTime* CreateFromTime(int hour, int minute, int second); // $AUTOCOMPLETESTATICONLY$ + /// Creates DateTime object from the provided raw time value (in seconds) + import static DateTime* CreateFromRawTime(int rawTime); // $AUTOCOMPLETESTATICONLY$ /// Gets the Year component of the date. readonly import attribute int Year; /// Gets the Month (1-12) component of the date. diff --git a/Engine/ac/datetime.cpp b/Engine/ac/datetime.cpp index c3e5c69e79..bb101a590b 100644 --- a/Engine/ac/datetime.cpp +++ b/Engine/ac/datetime.cpp @@ -11,9 +11,9 @@ // https://opensource.org/license/artistic-2-0/ // //============================================================================= -#include #include "ac/datetime.h" #include "ac/dynobj/dynobj_manager.h" +#include "debug/debug_log.h" #include "platform/base/agsplatformdriver.h" @@ -23,6 +23,33 @@ ScriptDateTime* DateTime_Now() { return sdt; } +ScriptDateTime* DateTime_CreateFromDate(int year, int month, int day, int hour, int minute, int second) { + if (month < 1 || month > 12 || day < 1 || day > 31 || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) + { + debug_script_warn("DateTime.CreateFromDate: out of range date or time requested, result may be incorrect: %dy,%dm,%dd,%dh:%dm:%ds", + year, month, day, hour, minute, second); + } + ScriptDateTime *sdt = new ScriptDateTime(year, month, day, hour, minute, second); + ccRegisterManagedObject(sdt, sdt); + return sdt; +} + +ScriptDateTime* DateTime_CreateFromTime(int hour, int minute, int second) { + if (hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) + { + debug_script_warn("DateTime.CreateFromDate: out of range time requested, result may be incorrect: %dh:%dm:%ds", + hour, minute, second); + } + ScriptDateTime *sdt = new ScriptDateTime(0, 0, 0, hour, minute, second); + ccRegisterManagedObject(sdt, sdt); + return sdt; +} + +ScriptDateTime* DateTime_CreateFromRawTime(int raw_time) { + ScriptDateTime *sdt = new ScriptDateTime(raw_time); + ccRegisterManagedObject(sdt, sdt); + return sdt; +} int DateTime_GetYear(ScriptDateTime *sdt) { return sdt->Year(); @@ -68,6 +95,21 @@ RuntimeScriptValue Sc_DateTime_Now(const RuntimeScriptValue *params, int32_t par API_SCALL_OBJAUTO(ScriptDateTime, DateTime_Now); } +RuntimeScriptValue Sc_DateTime_CreateFromRawTime(const RuntimeScriptValue *params, int32_t param_count) +{ + API_SCALL_OBJAUTO_PINT(ScriptDateTime, DateTime_CreateFromRawTime); +} + +RuntimeScriptValue Sc_DateTime_CreateFromDate(const RuntimeScriptValue *params, int32_t param_count) +{ + API_SCALL_OBJAUTO_PINT6(ScriptDateTime, DateTime_CreateFromDate); +} + +RuntimeScriptValue Sc_DateTime_CreateFromTime(const RuntimeScriptValue *params, int32_t param_count) +{ + API_SCALL_OBJAUTO_PINT3(ScriptDateTime, DateTime_CreateFromTime); +} + // int (ScriptDateTime *sdt) RuntimeScriptValue Sc_DateTime_GetYear(void *self, const RuntimeScriptValue *params, int32_t param_count) { @@ -114,6 +156,9 @@ void RegisterDateTimeAPI() { ScFnRegister datetime_api[] = { { "DateTime::get_Now", API_FN_PAIR(DateTime_Now) }, + { "DateTime::CreateFromDate", API_FN_PAIR(DateTime_CreateFromDate) }, + { "DateTime::CreateFromTime", API_FN_PAIR(DateTime_CreateFromTime) }, + { "DateTime::CreateFromRawTime", API_FN_PAIR(DateTime_CreateFromRawTime) }, { "DateTime::get_DayOfMonth", API_FN_PAIR(DateTime_GetDayOfMonth) }, { "DateTime::get_Hour", API_FN_PAIR(DateTime_GetHour) }, diff --git a/Engine/ac/dynobj/scriptdatetime.cpp b/Engine/ac/dynobj/scriptdatetime.cpp index 3f0c30026d..8c4826e505 100644 --- a/Engine/ac/dynobj/scriptdatetime.cpp +++ b/Engine/ac/dynobj/scriptdatetime.cpp @@ -40,6 +40,28 @@ ScriptDateTime::ScriptDateTime(const ClockTimePoint &time) SetTime(time); } +ScriptDateTime::ScriptDateTime(int raw_time) +{ + SetTime(ClockTimePoint(std::chrono::seconds(raw_time))); +} + +ScriptDateTime::ScriptDateTime(int year, int month, int day, int hour, int minute, int second) +{ + // NOTE: we do not init our calendar fields here directly, and instead + // go through SetTime, in case the combination of input values does not + // represent a true date, in which case it may be auto corrected when + // constructing a time point. + std::tm tm = { /* .tm_sec = */ second, + /* .tm_min = */ minute, + /* .tm_hour = */ hour, + /* .tm_mday = */ day, + /* .tm_mon = */ month - 1, + /* .tm_year = */ year - 1900, + }; + tm.tm_isdst = -1; // use DST value from local time zone + SetTime(std::chrono::system_clock::from_time_t(std::mktime(&tm))); +} + void ScriptDateTime::SetTime(const ClockTimePoint &time) { // NOTE: subject to year 2038 problem due to shoving seconds since epoch into an int32 @@ -48,6 +70,9 @@ void ScriptDateTime::SetTime(const ClockTimePoint &time) std::time_t ttime = SystemClock::to_time_t(time); std::tm *newtime = std::localtime(&ttime); + if (newtime == nullptr) + return; + _hour = newtime->tm_hour; _minute = newtime->tm_min; _second = newtime->tm_sec; diff --git a/Engine/ac/dynobj/scriptdatetime.h b/Engine/ac/dynobj/scriptdatetime.h index 67b5094d7d..1731be7609 100644 --- a/Engine/ac/dynobj/scriptdatetime.h +++ b/Engine/ac/dynobj/scriptdatetime.h @@ -33,6 +33,10 @@ struct ScriptDateTime final : AGSCCDynamicObject ScriptDateTime(const time_t &time); // Constructs DateTime initialized using chrono::time_point ScriptDateTime(const ClockTimePoint &time); + // Constructs DateTime initialized with raw time (unix time) + ScriptDateTime(int raw_time); + // Constructs DateTime initialized with all the date/time components + ScriptDateTime(int year, int month, int day, int hour, int minute, int second); int Dispose(void *address, bool force) override; const char *GetType() override; diff --git a/Engine/script/script_api.h b/Engine/script/script_api.h index b7194469cf..60c645f4f6 100644 --- a/Engine/script/script_api.h +++ b/Engine/script/script_api.h @@ -352,6 +352,11 @@ inline const char *ScriptVSprintf(char *buffer, size_t buf_length, const char *f RET_CLASS* ret_obj = FUNCTION(params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue, params[4].IValue); \ return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj) +#define API_SCALL_OBJAUTO_PINT6(RET_CLASS, FUNCTION) \ + ASSERT_PARAM_COUNT(FUNCTION, 6); \ + RET_CLASS* ret_obj = FUNCTION(params[0].IValue, params[1].IValue, params[2].IValue, params[3].IValue, params[4].IValue, params[5].IValue); \ + return RuntimeScriptValue().SetScriptObject(ret_obj, ret_obj) + #define API_SCALL_OBJAUTO_PINT2_PBOOL(RET_CLASS, FUNCTION) \ ASSERT_PARAM_COUNT(FUNCTION, 3); \ RET_CLASS* ret_obj = FUNCTION(params[0].IValue, params[1].IValue, params[2].GetAsBool()); \