diff --git a/Android.mk b/Android.mk index 2b4255559..a60a0d363 100644 --- a/Android.mk +++ b/Android.mk @@ -60,6 +60,8 @@ LOCAL_SRC_FILES := \ $(wildcard $(LOCAL_PATH)/src/stdlib/*.c) \ $(wildcard $(LOCAL_PATH)/src/thread/*.c) \ $(wildcard $(LOCAL_PATH)/src/thread/pthread/*.c) \ + $(wildcard $(LOCAL_PATH)/src/time/*.c) \ + $(wildcard $(LOCAL_PATH)/src/time/unix/*.c) \ $(wildcard $(LOCAL_PATH)/src/timer/*.c) \ $(wildcard $(LOCAL_PATH)/src/timer/unix/*.c) \ $(wildcard $(LOCAL_PATH)/src/video/*.c) \ diff --git a/CMakeLists.txt b/CMakeLists.txt index 8017bc2b7..9b288c932 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -497,6 +497,7 @@ sdl_glob_sources( "${SDL3_SOURCE_DIR}/src/stdlib/*.c" "${SDL3_SOURCE_DIR}/src/storage/*.c" "${SDL3_SOURCE_DIR}/src/thread/*.c" + "${SDL3_SOURCE_DIR}/src/time/*.c" "${SDL3_SOURCE_DIR}/src/timer/*.c" "${SDL3_SOURCE_DIR}/src/video/*.c" "${SDL3_SOURCE_DIR}/src/video/yuv2rgb/*.c" @@ -1044,6 +1045,7 @@ if(SDL_LIBC) string.h strings.h sys/types.h + time.h wchar.h ) foreach(_HEADER IN LISTS headers_to_check) @@ -1105,6 +1107,9 @@ if(SDL_LIBC) check_symbol_exists(sigaction "signal.h" HAVE_SIGACTION) check_symbol_exists(setjmp "setjmp.h" HAVE_SETJMP) check_symbol_exists(nanosleep "time.h" HAVE_NANOSLEEP) + check_symbol_exists(gmtime_r "time.h" HAVE_GMTIME_R) + check_symbol_exists(localtime_r "time.h" HAVE_LOCALTIME_R) + check_symbol_exists(nl_langinfo "langinfo.h" HAVE_NL_LANGINFO) check_symbol_exists(sysconf "unistd.h" HAVE_SYSCONF) check_symbol_exists(sysctlbyname "sys/types.h;sys/sysctl.h" HAVE_SYSCTLBYNAME) check_symbol_exists(getauxval "sys/auxv.h" HAVE_GETAUXVAL) @@ -1329,6 +1334,10 @@ if(ANDROID) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/locale/android/*.c") set(HAVE_SDL_LOCALE TRUE) + set(SDL_TIME_UNIX 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c") + set(HAVE_SDL_TIME TRUE) + set(SDL_TIMER_UNIX 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/unix/*.c") set(HAVE_SDL_TIMERS TRUE) @@ -1484,6 +1493,10 @@ elseif(EMSCRIPTEN) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/locale/emscripten/*.c") set(HAVE_SDL_LOCALE TRUE) + set(SDL_TIME_UNIX 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c") + set(HAVE_SDL_TIME TRUE) + set(SDL_TIMER_UNIX 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/unix/*.c") set(HAVE_SDL_TIMERS TRUE) @@ -1783,6 +1796,10 @@ elseif(UNIX AND NOT APPLE AND NOT RISCOS AND NOT HAIKU) sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c") set(HAVE_SDL_FSOPS TRUE) + set(SDL_TIME_UNIX 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c") + set(HAVE_SDL_TIME TRUE) + set(SDL_TIMER_UNIX 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/unix/*.c") set(HAVE_SDL_TIMERS TRUE) @@ -2030,6 +2047,10 @@ elseif(WINDOWS) ) endif() + set(SDL_TIME_WINDOWS 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/windows/*.c") + set(HAVE_SDL_TIME TRUE) + set(SDL_TIMER_WINDOWS 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/windows/*.c") set(HAVE_SDL_TIMERS TRUE) @@ -2238,6 +2259,10 @@ elseif(APPLE) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/locale/macos/*.m") set(HAVE_SDL_LOCALE TRUE) + set(SDL_TIME_UNIX 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c") + set(HAVE_SDL_TIME TRUE) + set(SDL_TIMER_UNIX 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/unix/*.c") set(HAVE_SDL_TIMERS TRUE) @@ -2459,6 +2484,10 @@ elseif(HAIKU) sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c") set(HAVE_SDL_FSOPS TRUE) + set(SDL_TIME_UNIX 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c") + set(HAVE_SDL_TIME TRUE) + set(SDL_TIMER_HAIKU 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/haiku/*.c") set(HAVE_SDL_TIMERS TRUE) @@ -2499,6 +2528,10 @@ elseif(RISCOS) sdl_sources("${SDL3_SOURCE_DIR}/src/filesystem/posix/SDL_sysfsops.c") set(HAVE_SDL_FSOPS TRUE) + set(SDL_TIME_UNIX 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/unix/*.c") + set(HAVE_SDL_TIME TRUE) + set(SDL_TIMER_UNIX 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/unix/*.c") set(HAVE_SDL_TIMERS TRUE) @@ -2566,6 +2599,10 @@ elseif(VITA) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/locale/vita/*.c") set(HAVE_SDL_LOCALE TRUE) + set(SDL_TIME_VITA 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/vita/*.c") + set(HAVE_SDL_TIME TRUE) + set(SDL_TIMER_VITA 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/vita/*.c") set(HAVE_SDL_TIMERS TRUE) @@ -2699,6 +2736,10 @@ elseif(PSP) ) set(HAVE_SDL_THREADS TRUE) + set(SDL_TIME_PSP 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/psp/*.c") + set(HAVE_SDL_TIME TRUE) + set(SDL_TIMER_PSP 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/psp/*.c") set(HAVE_SDL_TIMERS TRUE) @@ -2761,6 +2802,10 @@ elseif(PS2) ) set(HAVE_SDL_THREADS TRUE) + set(SDL_TIME_PS2 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/ps2/*.c") + set(HAVE_SDL_TIME TRUE) + set(SDL_TIMER_PS2 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/ps2/*.c") set(HAVE_SDL_TIMERS TRUE) @@ -2820,6 +2865,10 @@ elseif(N3DS) ) set(HAVE_SDL_THREADS TRUE) + set(SDL_TIME_N3DS 1) + sdl_glob_sources("${SDL3_SOURCE_DIR}/src/time/n3ds/*.c") + set(HAVE_SDL_TIME TRUE) + set(SDL_TIMER_N3DS 1) sdl_glob_sources("${SDL3_SOURCE_DIR}/src/timer/n3ds/*.c") set(HAVE_SDL_TIMERS TRUE) diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj index 948e75578..53926d49a 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj +++ b/VisualC-GDK/SDL/SDL.vcxproj @@ -384,6 +384,7 @@ + @@ -795,6 +796,8 @@ + + diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters index bc62d7a0b..39aac2b53 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj.filters +++ b/VisualC-GDK/SDL/SDL.vcxproj.filters @@ -171,6 +171,12 @@ + + time + + + time\windows + diff --git a/VisualC-WinRT/SDL-UWP.vcxproj b/VisualC-WinRT/SDL-UWP.vcxproj index 4948abf9b..75ffad0c4 100644 --- a/VisualC-WinRT/SDL-UWP.vcxproj +++ b/VisualC-WinRT/SDL-UWP.vcxproj @@ -86,6 +86,7 @@ + @@ -521,6 +522,8 @@ + + diff --git a/VisualC-WinRT/SDL-UWP.vcxproj.filters b/VisualC-WinRT/SDL-UWP.vcxproj.filters index ca56e1757..144061e65 100644 --- a/VisualC-WinRT/SDL-UWP.vcxproj.filters +++ b/VisualC-WinRT/SDL-UWP.vcxproj.filters @@ -25,6 +25,12 @@ {0000bc587ef6c558d75ce2e620cb0000} + + {0000948771d0040a6a55997a7f1e0000} + + + {0000012051ca8361c8e1013aee1d0000} + @@ -792,6 +798,12 @@ Source Files + + time + + + time\windows + Source Files diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj index 24c397472..d50987c37 100644 --- a/VisualC/SDL/SDL.vcxproj +++ b/VisualC/SDL/SDL.vcxproj @@ -307,6 +307,7 @@ + @@ -653,6 +654,8 @@ + + diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters index 5d3e43001..766c3bc8d 100644 --- a/VisualC/SDL/SDL.vcxproj.filters +++ b/VisualC/SDL/SDL.vcxproj.filters @@ -190,6 +190,12 @@ {5115ba31-20f8-4eab-a8c5-6a572ab78ff7} + + {00003288226ff86b99eee5b443e90000} + + + {0000d7fda065b13b0ca4ab262c380000} + @@ -1183,6 +1189,12 @@ joystick\virtual + + time + + + time\windows + video diff --git a/VisualC/tests/testautomation/testautomation.vcxproj b/VisualC/tests/testautomation/testautomation.vcxproj index 7fc14cb70..84539df43 100644 --- a/VisualC/tests/testautomation/testautomation.vcxproj +++ b/VisualC/tests/testautomation/testautomation.vcxproj @@ -225,6 +225,7 @@ + diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj index 1f066dde0..d39f5b860 100644 --- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj +++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj @@ -510,6 +510,8 @@ 000080903BC03006F24E0000 /* SDL_filesystem.c in Sources */ = {isa = PBXBuildFile; fileRef = 00002B010DB1A70931C20000 /* SDL_filesystem.c */; }; 00000D60346481EEC8FB0000 /* SDL_filesystem.h in Headers */ = {isa = PBXBuildFile; fileRef = 0000BE1BF5193C6D0F4F0000 /* SDL_filesystem.h */; }; 0000481D255AF155B42C0000 /* SDL_sysfsops.c in Sources */ = {isa = PBXBuildFile; fileRef = 0000F4E6AA3EF99DA3C80000 /* SDL_sysfsops.c */; }; + 0000494CC93F3E624D3C0000 /* SDL_systime.c in Sources */ = {isa = PBXBuildFile; fileRef = 00003F472C51CE7DF6160000 /* SDL_systime.c */; }; + 000095FA1BDE436CF3AF0000 /* SDL_time.c in Sources */ = {isa = PBXBuildFile; fileRef = 0000641A9BAC11AB3FBE0000 /* SDL_time.c */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -1048,6 +1050,8 @@ 00002B010DB1A70931C20000 /* SDL_filesystem.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_filesystem.c; path = SDL_filesystem.c; sourceTree = ""; }; 0000BE1BF5193C6D0F4F0000 /* SDL_filesystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDL_filesystem.h; path = SDL_filesystem.h; sourceTree = ""; }; 0000F4E6AA3EF99DA3C80000 /* SDL_sysfsops.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_sysfsops.c; path = SDL_sysfsops.c; sourceTree = ""; }; + 00003F472C51CE7DF6160000 /* SDL_systime.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_systime.c; path = SDL_systime.c; sourceTree = ""; }; + 0000641A9BAC11AB3FBE0000 /* SDL_time.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = SDL_time.c; path = SDL_time.c; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1272,6 +1276,7 @@ F386F6E52884663E001840AA /* SDL_utils_c.h */, F386F6E62884663E001840AA /* SDL_utils.c */, A7D8A57123E2513D00DCD162 /* SDL.c */, + 0000F5E7419220E3A8AB0000 /* time */, ); name = "Library Source"; path = ../../src; @@ -2254,6 +2259,23 @@ path = posix; sourceTree = ""; }; + 0000F5E7419220E3A8AB0000 /* time */ = { + isa = PBXGroup; + children = ( + 000004752BA2F77DECDF0000 /* unix */, + 0000641A9BAC11AB3FBE0000 /* SDL_time.c */, + ); + path = time; + sourceTree = ""; + }; + 000004752BA2F77DECDF0000 /* unix */ = { + isa = PBXGroup; + children = ( + 00003F472C51CE7DF6160000 /* SDL_systime.c */, + ); + path = unix; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -2843,6 +2865,8 @@ 00002B20A48E055EB0350000 /* SDL_camera_coremedia.m in Sources */, 000080903BC03006F24E0000 /* SDL_filesystem.c in Sources */, 0000481D255AF155B42C0000 /* SDL_sysfsops.c in Sources */, + 0000494CC93F3E624D3C0000 /* SDL_systime.c in Sources */, + 000095FA1BDE436CF3AF0000 /* SDL_time.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/include/SDL3/SDL.h b/include/SDL3/SDL.h index 4a25af8c1..8dac3e9f9 100644 --- a/include/SDL3/SDL.h +++ b/include/SDL3/SDL.h @@ -75,6 +75,7 @@ #include #include #include +#include #include #include #include diff --git a/include/SDL3/SDL_filesystem.h b/include/SDL3/SDL_filesystem.h index f39a9e10c..239a6513d 100644 --- a/include/SDL3/SDL_filesystem.h +++ b/include/SDL3/SDL_filesystem.h @@ -245,19 +245,13 @@ typedef enum SDL_PathType SDL_PATHTYPE_OTHER /**< something completely different like a device node (not a symlink, those are always followed) */ } SDL_PathType; -/* SDL file times are 64-bit integers representing nanoseconds since the Unix epoch (Jan 1, 1970) - * - * They can be converted between to POSIX time_t values with SDL_NS_TO_SECONDS() and SDL_SECONDS_TO_NS(), and between Windows FILETIME values with SDL_FileTimeToWindows() and SDL_FileTimeFromWindows() - */ -typedef Sint64 SDL_FileTime; - typedef struct SDL_PathInfo { SDL_PathType type; /* the path type */ Uint64 size; /* the file size in bytes */ - SDL_FileTime create_time; /* the time when the path was created */ - SDL_FileTime modify_time; /* the last time the path was modified */ - SDL_FileTime access_time; /* the last time the path was read */ + SDL_Time create_time; /* the time when the path was created */ + SDL_Time modify_time; /* the last time the path was modified */ + SDL_Time access_time; /* the last time the path was read */ } SDL_PathInfo; /** @@ -323,30 +317,6 @@ extern DECLSPEC int SDLCALL SDL_RenamePath(const char *oldpath, const char *newp */ extern DECLSPEC int SDLCALL SDL_GetPathInfo(const char *path, SDL_PathInfo *info); -/* Converts an SDL file time into a Windows FILETIME (100-nanosecond intervals since January 1, 1601). - * - * This function fills in the two 32-bit values of the FILETIME structure. - * - * \param ftime the time to convert - * \param dwLowDateTime a pointer filled in with the low portion of the Windows FILETIME value - * \param dwHighDateTime a pointer filled in with the high portion of the Windows FILETIME value - * - * \since This function is available since SDL 3.0.0. - */ -extern DECLSPEC void SDLCALL SDL_FileTimeToWindows(SDL_FileTime ftime, Uint32 *dwLowDateTime, Uint32 *dwHighDateTime); - -/* Converts a Windows FILETIME (100-nanosecond intervals since January 1, 1601) to an SDL file time - * - * This function takes the two 32-bit values of the FILETIME structure as parameters. - * - * \param dwLowDateTime the low portion of the Windows FILETIME value - * \param dwHighDateTime the high portion of the Windows FILETIME value - * \returns the converted file time - * - * \since This function is available since SDL 3.0.0. - */ -extern DECLSPEC SDL_FileTime SDLCALL SDL_FileTimeFromWindows(Uint32 dwLowDateTime, Uint32 dwHighDateTime); - /* Ends C function definitions when using C++ */ #ifdef __cplusplus } diff --git a/include/SDL3/SDL_stdinc.h b/include/SDL3/SDL_stdinc.h index 281aa56d1..0a5cf33b4 100644 --- a/include/SDL3/SDL_stdinc.h +++ b/include/SDL3/SDL_stdinc.h @@ -186,6 +186,16 @@ typedef int64_t Sint64; #define SDL_MIN_UINT64 ((Uint64)(0x0000000000000000ull)) /* 0 */ typedef uint64_t Uint64; +/** + * SDL times are signed, 64-bit integers representing nanoseconds since the Unix epoch (Jan 1, 1970) + * + * They can be converted between POSIX time_t values with SDL_NS_TO_SECONDS() and SDL_SECONDS_TO_NS(), + * and between Windows FILETIME values with SDL_TimeToWindows() and SDL_TimeFromWindows(). + */ +#define SDL_MAX_TIME SDL_MAX_SINT64 +#define SDL_MIN_TIME SDL_MIN_SINT64 +typedef Sint64 SDL_Time; + /* @} *//* Basic data types */ /** diff --git a/include/SDL3/SDL_time.h b/include/SDL3/SDL_time.h new file mode 100644 index 000000000..f87118e44 --- /dev/null +++ b/include/SDL3/SDL_time.h @@ -0,0 +1,195 @@ +/* +Simple DirectMedia Layer +Copyright (C) 1997-2024 Sam Lantinga + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SDL_time_h_ +#define SDL_time_h_ + +/** + * \file SDL_time.h + * + * Header for the SDL realtime clock and date/time routines. + */ + +#include +#include + +#include +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/** + * A structure holding a calendar date and time broken down into its components. + */ +typedef struct SDL_DateTime +{ + int year; /**< Year */ + int month; /**< Month [01-12] */ + int day; /**< Day of the month [01-31] */ + int hour; /**< Hour [0-23] */ + int minute; /**< Minute [0-59] */ + int second; /**< Seconds [0-60] */ + int nanosecond; /**< Nanoseconds [0-999999999] */ + int day_of_week; /**< Day of the week [0-6] (0 being Sunday) */ + int utc_offset; /**< Seconds east of UTC */ +} SDL_DateTime; + +/** + * The preferred date format of the current system locale. + * + * \sa SDL_PROP_GLOBAL_SYSTEM_DATE_FORMAT_NUMBER + */ +typedef enum SDL_DATE_FORMAT +{ + SDL_DATE_FORMAT_YYYYMMDD = 0, /**< Year/Month/Day */ + SDL_DATE_FORMAT_DDMMYYYY = 1, /**< Day/Month/Year */ + SDL_DATE_FORMAT_MMDDYYYY = 2, /**< Month/Day/Year */ +} SDL_DATE_FORMAT; + +/** + * The preferred time format of the current system locale. + * + * \sa SDL_PROP_GLOBAL_SYSTEM_TIME_FORMAT_NUMBER + */ +typedef enum SDL_TIME_FORMAT +{ + SDL_TIME_FORMAT_24HR = 0, /**< 24 hour time */ + SDL_TIME_FORMAT_12HR = 1, /**< 12 hour time */ +} SDL_TIME_FORMAT; + +/** + * Global date/time properties + * + * - `SDL_PROP_GLOBAL_SYSTEM_DATE_FORMAT_NUMBER`: the SDL_DATE_FORMAT to use as the preferred date display format + * for the current system locale. + * - `SDL_PROP_GLOBAL_SYSTEM_TIME_FORMAT_NUMBER`: the SDL_TIME_FORMAT to use as the preferred time display format + * for the current system locale. + */ +#define SDL_PROP_GLOBAL_SYSTEM_DATE_FORMAT_NUMBER "SDL.time.date_format" +#define SDL_PROP_GLOBAL_SYSTEM_TIME_FORMAT_NUMBER "SDL.time.time_format" + +/** + * Gets the current value of the system realtime clock in nanoseconds since Jan 1, 1970 in + * Universal Coordinated Time (UTC). + * + * \param ticks the SDL_Time to hold the returned tick count + * \returns 0 on success or -1 on error; call SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0 + */ +extern DECLSPEC int SDLCALL SDL_GetCurrentTime(SDL_Time *ticks); + +/** + * Converts an SDL_Time in nanoseconds since the epoch to a calendar time in the SDL_DateTime format. + * + * \param ticks the SDL_Time to be converted + * \param dt the resulting SDL_DateTime + * \param localTime the resulting SDL_DateTime will be expressed in local time if true, otherwise + * it will be in Universal Coordinated Time (UTC) + * \returns 0 on success or -1 on error; call SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0 + */ +extern DECLSPEC int SDLCALL SDL_TimeToDateTime(SDL_Time ticks, SDL_DateTime *dt, SDL_bool localTime); + +/** + * Converts a calendar time to an SDL_Time in nanoseconds since the epoch. + * This function ignores the day_of_week member of the SDL_DateTime struct, so it may remain unset. + * + * \param dt the source SDL_DateTime + * \param ticks the resulting SDL_Time + * \returns 0 on success or -1 on error; call SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0 + */ +extern DECLSPEC int SDLCALL SDL_DateTimeToTime(const SDL_DateTime *dt, SDL_Time *ticks); + +/** + * Converts an SDL time into a Windows FILETIME (100-nanosecond intervals since January 1, 1601). + * + * This function fills in the two 32-bit values of the FILETIME structure. + * + * \param ticks the time to convert + * \param dwLowDateTime a pointer filled in with the low portion of the Windows FILETIME value + * \param dwHighDateTime a pointer filled in with the high portion of the Windows FILETIME value + * + * \since This function is available since SDL 3.0.0. + */ +extern DECLSPEC void SDLCALL SDL_TimeToWindows(SDL_Time ticks, Uint32 *dwLowDateTime, Uint32 *dwHighDateTime); + +/** + * Converts a Windows FILETIME (100-nanosecond intervals since January 1, 1601) to an SDL time + * + * This function takes the two 32-bit values of the FILETIME structure as parameters. + * + * \param dwLowDateTime the low portion of the Windows FILETIME value + * \param dwHighDateTime the high portion of the Windows FILETIME value + * \returns the converted SDL time + * + * \since This function is available since SDL 3.0.0. + */ +extern DECLSPEC SDL_Time SDLCALL SDL_TimeFromWindows(Uint32 dwLowDateTime, Uint32 dwHighDateTime); + +/** + * Get the number of days in a month for a given year. + * + * \param year the year + * \param month the month [1-12] + * \returns the number of days in the requested month, otherwise -1; call SDL_GetError() for more information. + * + * \since This function is available since SDL 3.0.0 + */ +extern DECLSPEC int SDLCALL SDL_GetDaysInMonth(int year, int month); + +/** + * Get the day of year for a calendar date. + * + * \param year the year component of the date + * \param month the month component of the date + * \param day the day component of the date + * \returns the day of year [0-365] if the date is valid, otherwise -1; call SDL_GetError() + * for more information. + * + * \since This function is available since SDL 3.0.0 + */ +extern DECLSPEC int SDLCALL SDL_GetDayOfYear(int year, int month, int day); + +/** + * Get the day of week for a calendar date. + * + * \param year the year component of the date + * \param month the month component of the date + * \param day the day component of the date + * \returns a value between 0 and 6 (0 being Sunday) if the date is valid, otherwise -1; call SDL_GetError() + * for more information. + * + * \since This function is available since SDL 3.0.0 + */ +extern DECLSPEC int SDLCALL SDL_GetDayOfWeek(int year, int month, int day); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include + +#endif /* SDL_time_h_ */ diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake index c71a0cafb..951771e49 100644 --- a/include/build_config/SDL_build_config.h.cmake +++ b/include/build_config/SDL_build_config.h.cmake @@ -192,6 +192,9 @@ #cmakedefine HAVE_SA_SIGACTION 1 #cmakedefine HAVE_SETJMP 1 #cmakedefine HAVE_NANOSLEEP 1 +#cmakedefine HAVE_GMTIME_R 1 +#cmakedefine HAVE_LOCALTIME_R 1 +#cmakedefine HAVE_NL_LANGINFO 1 #cmakedefine HAVE_SYSCONF 1 #cmakedefine HAVE_SYSCTLBYNAME 1 #cmakedefine HAVE_CLOCK_GETTIME 1 @@ -350,6 +353,14 @@ #cmakedefine SDL_THREAD_PS2 @SDL_THREAD_PS2@ #cmakedefine SDL_THREAD_N3DS @SDL_THREAD_N3DS@ +/* Enable various RTC systems */ +#cmakedefine SDL_TIME_UNIX @SDL_TIME_UNIX@ +#cmakedefine SDL_TIME_WINDOWS @SDL_TIME_WINDOWS@ +#cmakedefine SDL_TIME_VITA @SDL_TIME_VITA@ +#cmakedefine SDL_TIME_PSP @SDL_TIME_PSP@ +#cmakedefine SDL_TIME_PS2 @SDL_TIME_PS2@ +#cmakedefine SDL_TIME_N3DS @SDL_TIME_N3DS@ + /* Enable various timer systems */ #cmakedefine SDL_TIMER_HAIKU @SDL_TIMER_HAIKU@ #cmakedefine SDL_TIMER_DUMMY @SDL_TIMER_DUMMY@ diff --git a/include/build_config/SDL_build_config_android.h b/include/build_config/SDL_build_config_android.h index b784afd00..19df2278f 100644 --- a/include/build_config/SDL_build_config_android.h +++ b/include/build_config/SDL_build_config_android.h @@ -137,6 +137,8 @@ #define HAVE_SIGACTION 1 #define HAVE_SETJMP 1 #define HAVE_NANOSLEEP 1 +#define HAVE_GMTIME_R 1 +#define HAVE_LOCALTIME_R 1 #define HAVE_SYSCONF 1 #define HAVE_CLOCK_GETTIME 1 @@ -162,6 +164,9 @@ #define SDL_THREAD_PTHREAD 1 #define SDL_THREAD_PTHREAD_RECURSIVE_MUTEX 1 +/* Enable RTC system */ +#define SDL_TIME_UNIX 1 + /* Enable various timer systems */ #define SDL_TIMER_UNIX 1 @@ -193,4 +198,9 @@ #define SDL_CAMERA_DRIVER_ANDROID 1 #define SDL_CAMERA_DRIVER_DUMMY 1 +/* Enable nl_langinfo on version 26 and higher. */ +#if __ANDROID_API__ >= 26 +#define HAVE_NL_LANGINFO 1 +#endif + #endif /* SDL_build_config_android_h_ */ diff --git a/include/build_config/SDL_build_config_ios.h b/include/build_config/SDL_build_config_ios.h index 875f1942a..44c185cce 100644 --- a/include/build_config/SDL_build_config_ios.h +++ b/include/build_config/SDL_build_config_ios.h @@ -129,6 +129,9 @@ #define HAVE_SIGACTION 1 #define HAVE_SETJMP 1 #define HAVE_NANOSLEEP 1 +#define HAVE_GMTIME_R 1 +#define HAVE_LOCALTIME_R 1 +#define HAVE_NL_LANGINFO 1 #define HAVE_SYSCONF 1 #define HAVE_SYSCTLBYNAME 1 #define HAVE_O_CLOEXEC 1 @@ -161,6 +164,9 @@ #define SDL_THREAD_PTHREAD 1 #define SDL_THREAD_PTHREAD_RECURSIVE_MUTEX 1 +/* Enable various RTC system */ +#define SDL_TIME_UNIX 1 + /* Enable various timer systems */ #define SDL_TIMER_UNIX 1 diff --git a/include/build_config/SDL_build_config_macos.h b/include/build_config/SDL_build_config_macos.h index c853e58a9..1734beb82 100644 --- a/include/build_config/SDL_build_config_macos.h +++ b/include/build_config/SDL_build_config_macos.h @@ -133,6 +133,9 @@ #define HAVE_SIGACTION 1 #define HAVE_SETJMP 1 #define HAVE_NANOSLEEP 1 +#define HAVE_GMTIME_R 1 +#define HAVE_LOCALTIME_R 1 +#define HAVE_NL_LANGINFO 1 #define HAVE_SYSCONF 1 #define HAVE_SYSCTLBYNAME 1 diff --git a/include/build_config/SDL_build_config_windows.h b/include/build_config/SDL_build_config_windows.h index 69852a766..9f55afffd 100644 --- a/include/build_config/SDL_build_config_windows.h +++ b/include/build_config/SDL_build_config_windows.h @@ -263,6 +263,9 @@ typedef unsigned int uintptr_t; #define SDL_THREAD_GENERIC_RWLOCK_SUFFIX 1 #define SDL_THREAD_WINDOWS 1 +/* Enable RTC system */ +#define SDL_TIME_WINDOWS 1 + /* Enable various timer systems */ #define SDL_TIMER_WINDOWS 1 diff --git a/include/build_config/SDL_build_config_wingdk.h b/include/build_config/SDL_build_config_wingdk.h index f760cf477..3fdaf4c76 100644 --- a/include/build_config/SDL_build_config_wingdk.h +++ b/include/build_config/SDL_build_config_wingdk.h @@ -199,6 +199,9 @@ #define SDL_THREAD_GENERIC_RWLOCK_SUFFIX 1 #define SDL_THREAD_WINDOWS 1 +/* Enable various time systems */ +#define SDL_TIME_WINDOWS 1 + /* Enable various timer systems */ #define SDL_TIMER_WINDOWS 1 diff --git a/include/build_config/SDL_build_config_winrt.h b/include/build_config/SDL_build_config_winrt.h index 9a7702d87..c9baf5aee 100644 --- a/include/build_config/SDL_build_config_winrt.h +++ b/include/build_config/SDL_build_config_winrt.h @@ -189,6 +189,9 @@ #define SDL_THREAD_STDCPP 1 #endif +/* Enable RTC system */ +#define SDL_TIME_WINDOWS 1 + /* Enable various timer systems */ #define SDL_TIMER_WINDOWS 1 diff --git a/src/SDL.c b/src/SDL.c index 3653552a1..f010ad380 100644 --- a/src/SDL.c +++ b/src/SDL.c @@ -52,6 +52,7 @@ #define SDL_INIT_EVERYTHING ~0U /* Initialization/Cleanup routines */ +#include "time/SDL_time_c.h" #include "timer/SDL_timer_c.h" #ifdef SDL_VIDEO_DRIVER_WINDOWS extern int SDL_HelperWindowCreate(void); @@ -204,6 +205,7 @@ int SDL_InitSubSystem(Uint32 flags) } #endif + SDL_InitTime(); SDL_InitTicks(); /* Initialize the event subsystem */ @@ -536,6 +538,7 @@ void SDL_Quit(void) SDL_QuitSubSystem(SDL_INIT_EVERYTHING); SDL_QuitTicks(); + SDL_QuitTime(); #ifdef SDL_USE_LIBDBUS SDL_DBus_Quit(); diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index f8b773448..e651008fc 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -92,6 +92,7 @@ SDL3_0.0.0 { SDL_CreateWindowWithProperties; SDL_CursorVisible; SDL_DXGIGetOutputInfo; + SDL_DateTimeToTime; SDL_DelEventWatch; SDL_DelHintCallback; SDL_Delay; @@ -128,8 +129,6 @@ SDL3_0.0.0 { SDL_EnumerateStorageDirectory; SDL_Error; SDL_EventEnabled; - SDL_FileTimeFromWindows; - SDL_FileTimeToWindows; SDL_FillSurfaceRect; SDL_FillSurfaceRects; SDL_FilterEvents; @@ -202,8 +201,12 @@ SDL3_0.0.0 { SDL_GetCurrentDisplayOrientation; SDL_GetCurrentRenderOutputSize; SDL_GetCurrentThreadID; + SDL_GetCurrentTime; SDL_GetCurrentVideoDriver; SDL_GetCursor; + SDL_GetDayOfWeek; + SDL_GetDayOfYear; + SDL_GetDaysInMonth; SDL_GetDefaultAssertionHandler; SDL_GetDefaultCursor; SDL_GetDesktopDisplayMode; @@ -781,6 +784,9 @@ SDL3_0.0.0 { SDL_TellIO; SDL_TextInputActive; SDL_TextInputShown; + SDL_TimeFromWindows; + SDL_TimeToDateTime; + SDL_TimeToWindows; SDL_TryLockMutex; SDL_TryLockRWLockForReading; SDL_TryLockRWLockForWriting; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index cf3fb9fd3..ac9310c48 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -117,6 +117,7 @@ #define SDL_CreateWindowWithProperties SDL_CreateWindowWithProperties_REAL #define SDL_CursorVisible SDL_CursorVisible_REAL #define SDL_DXGIGetOutputInfo SDL_DXGIGetOutputInfo_REAL +#define SDL_DateTimeToTime SDL_DateTimeToTime_REAL #define SDL_DelEventWatch SDL_DelEventWatch_REAL #define SDL_DelHintCallback SDL_DelHintCallback_REAL #define SDL_Delay SDL_Delay_REAL @@ -153,8 +154,6 @@ #define SDL_EnumerateStorageDirectory SDL_EnumerateStorageDirectory_REAL #define SDL_Error SDL_Error_REAL #define SDL_EventEnabled SDL_EventEnabled_REAL -#define SDL_FileTimeFromWindows SDL_FileTimeFromWindows_REAL -#define SDL_FileTimeToWindows SDL_FileTimeToWindows_REAL #define SDL_FillSurfaceRect SDL_FillSurfaceRect_REAL #define SDL_FillSurfaceRects SDL_FillSurfaceRects_REAL #define SDL_FilterEvents SDL_FilterEvents_REAL @@ -227,8 +226,12 @@ #define SDL_GetCurrentDisplayOrientation SDL_GetCurrentDisplayOrientation_REAL #define SDL_GetCurrentRenderOutputSize SDL_GetCurrentRenderOutputSize_REAL #define SDL_GetCurrentThreadID SDL_GetCurrentThreadID_REAL +#define SDL_GetCurrentTime SDL_GetCurrentTime_REAL #define SDL_GetCurrentVideoDriver SDL_GetCurrentVideoDriver_REAL #define SDL_GetCursor SDL_GetCursor_REAL +#define SDL_GetDayOfWeek SDL_GetDayOfWeek_REAL +#define SDL_GetDayOfYear SDL_GetDayOfYear_REAL +#define SDL_GetDaysInMonth SDL_GetDaysInMonth_REAL #define SDL_GetDefaultAssertionHandler SDL_GetDefaultAssertionHandler_REAL #define SDL_GetDefaultCursor SDL_GetDefaultCursor_REAL #define SDL_GetDesktopDisplayMode SDL_GetDesktopDisplayMode_REAL @@ -805,6 +808,9 @@ #define SDL_TellIO SDL_TellIO_REAL #define SDL_TextInputActive SDL_TextInputActive_REAL #define SDL_TextInputShown SDL_TextInputShown_REAL +#define SDL_TimeFromWindows SDL_TimeFromWindows_REAL +#define SDL_TimeToDateTime SDL_TimeToDateTime_REAL +#define SDL_TimeToWindows SDL_TimeToWindows_REAL #define SDL_TryLockMutex SDL_TryLockMutex_REAL #define SDL_TryLockRWLockForReading SDL_TryLockRWLockForReading_REAL #define SDL_TryLockRWLockForWriting SDL_TryLockRWLockForWriting_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 1cb3f0738..f1ff330c6 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -155,6 +155,7 @@ SDL_DYNAPI_PROC(int,SDL_CreateWindowAndRenderer,(int a, int b, Uint32 c, SDL_Win SDL_DYNAPI_PROC(SDL_Window*,SDL_CreateWindowWithProperties,(SDL_PropertiesID a),(a),return) SDL_DYNAPI_PROC(SDL_bool,SDL_CursorVisible,(void),(),return) SDL_DYNAPI_PROC(SDL_bool,SDL_DXGIGetOutputInfo,(SDL_DisplayID a, int *b, int *c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_DateTimeToTime,(const SDL_DateTime *a, SDL_Time *b),(a,b),return) SDL_DYNAPI_PROC(void,SDL_DelEventWatch,(SDL_EventFilter a, void *b),(a,b),) SDL_DYNAPI_PROC(void,SDL_DelHintCallback,(const char *a, SDL_HintCallback b, void *c),(a,b,c),) SDL_DYNAPI_PROC(void,SDL_Delay,(Uint32 a),(a),) @@ -191,8 +192,6 @@ SDL_DYNAPI_PROC(int,SDL_EnumerateProperties,(SDL_PropertiesID a, SDL_EnumeratePr SDL_DYNAPI_PROC(int,SDL_EnumerateStorageDirectory,(SDL_Storage *a, const char *b, SDL_EnumerateDirectoryCallback c, void *d),(a,b,c,d),return) SDL_DYNAPI_PROC(int,SDL_Error,(SDL_errorcode a),(a),return) SDL_DYNAPI_PROC(SDL_bool,SDL_EventEnabled,(Uint32 a),(a),return) -SDL_DYNAPI_PROC(SDL_FileTime,SDL_FileTimeFromWindows,(Uint32 a, Uint32 b),(a,b),return) -SDL_DYNAPI_PROC(void,SDL_FileTimeToWindows,(Sint64 a, Uint32 *b, Uint32 *c),(a,b,c),) SDL_DYNAPI_PROC(int,SDL_FillSurfaceRect,(SDL_Surface *a, const SDL_Rect *b, Uint32 c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_FillSurfaceRects,(SDL_Surface *a, const SDL_Rect *b, int c, Uint32 d),(a,b,c,d),return) SDL_DYNAPI_PROC(void,SDL_FilterEvents,(SDL_EventFilter a, void *b),(a,b),) @@ -265,8 +264,12 @@ SDL_DYNAPI_PROC(const SDL_DisplayMode*,SDL_GetCurrentDisplayMode,(SDL_DisplayID SDL_DYNAPI_PROC(SDL_DisplayOrientation,SDL_GetCurrentDisplayOrientation,(SDL_DisplayID a),(a),return) SDL_DYNAPI_PROC(int,SDL_GetCurrentRenderOutputSize,(SDL_Renderer *a, int *b, int *c),(a,b,c),return) SDL_DYNAPI_PROC(SDL_ThreadID,SDL_GetCurrentThreadID,(void),(),return) +SDL_DYNAPI_PROC(int,SDL_GetCurrentTime,(SDL_Time *a),(a),return) SDL_DYNAPI_PROC(const char*,SDL_GetCurrentVideoDriver,(void),(),return) SDL_DYNAPI_PROC(SDL_Cursor*,SDL_GetCursor,(void),(),return) +SDL_DYNAPI_PROC(int,SDL_GetDayOfWeek,(int a, int b, int c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_GetDayOfYear,(int a, int b, int c),(a,b,c),return) +SDL_DYNAPI_PROC(int,SDL_GetDaysInMonth,(int a, int b),(a,b),return) SDL_DYNAPI_PROC(SDL_AssertionHandler,SDL_GetDefaultAssertionHandler,(void),(),return) SDL_DYNAPI_PROC(SDL_Cursor*,SDL_GetDefaultCursor,(void),(),return) SDL_DYNAPI_PROC(const SDL_DisplayMode*,SDL_GetDesktopDisplayMode,(SDL_DisplayID a),(a),return) @@ -825,6 +828,9 @@ SDL_DYNAPI_PROC(int,SDL_SyncWindow,(SDL_Window *a),(a),return) SDL_DYNAPI_PROC(Sint64,SDL_TellIO,(SDL_IOStream *a),(a),return) SDL_DYNAPI_PROC(SDL_bool,SDL_TextInputActive,(void),(),return) SDL_DYNAPI_PROC(SDL_bool,SDL_TextInputShown,(void),(),return) +SDL_DYNAPI_PROC(SDL_Time,SDL_TimeFromWindows,(Uint32 a, Uint32 b),(a,b),return) +SDL_DYNAPI_PROC(int,SDL_TimeToDateTime,(SDL_Time a, SDL_DateTime *b, SDL_bool c),(a,b,c),return) +SDL_DYNAPI_PROC(void,SDL_TimeToWindows,(SDL_Time a, Uint32 *b, Uint32 *c),(a,b,c),) SDL_DYNAPI_PROC(int,SDL_TryLockMutex,(SDL_Mutex *a),(a),return) SDL_DYNAPI_PROC(int,SDL_TryLockRWLockForReading,(SDL_RWLock *a),(a),return) SDL_DYNAPI_PROC(int,SDL_TryLockRWLockForWriting,(SDL_RWLock *a),(a),return) diff --git a/src/filesystem/SDL_filesystem.c b/src/filesystem/SDL_filesystem.c index 7fee9008a..2a8667ffa 100644 --- a/src/filesystem/SDL_filesystem.c +++ b/src/filesystem/SDL_filesystem.c @@ -22,38 +22,6 @@ #include "SDL_internal.h" #include "SDL_sysfilesystem.h" -static const Sint64 delta_1601_epoch_100ns = 11644473600ll * 10000000ll; // [100 ns] (100 ns units between 1/1/1601 and 1/1/1970, 11644473600 seconds) - -void SDL_FileTimeToWindows(SDL_FileTime ftime, Uint32 *dwLowDateTime, Uint32 *dwHighDateTime) -{ - Uint64 wtime; - - // Convert ftime to 100ns units - Sint64 ftime_100ns = (ftime / 100); - - if (ftime_100ns < 0 && -ftime_100ns > delta_1601_epoch_100ns) { - // If we're trying to show a timestamp from before before the Windows epoch, (Jan 1, 1601), clamp it to zero - wtime = 0; - } else { - wtime = (Uint64)(delta_1601_epoch_100ns + ftime_100ns); - } - - if (dwLowDateTime) { - *dwLowDateTime = (Uint32)wtime; - } - - if (dwHighDateTime) { - *dwHighDateTime = (Uint32)(wtime >> 32); - } -} - -SDL_FileTime SDL_FileTimeFromWindows(Uint32 dwLowDateTime, Uint32 dwHighDateTime) -{ - Uint64 wtime = (((Uint64)dwHighDateTime << 32) | dwLowDateTime); - - return (Sint64)(wtime - delta_1601_epoch_100ns) * 100; -} - int SDL_RemovePath(const char *path) { if (!path) { diff --git a/src/filesystem/posix/SDL_sysfsops.c b/src/filesystem/posix/SDL_sysfsops.c index 20ffce905..8e4939f1a 100644 --- a/src/filesystem/posix/SDL_sysfsops.c +++ b/src/filesystem/posix/SDL_sysfsops.c @@ -124,10 +124,16 @@ int SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info) info->size = (Uint64) statbuf.st_size; } - info->create_time = (SDL_FileTime)SDL_SECONDS_TO_NS(statbuf.st_ctime); - info->modify_time = (SDL_FileTime)SDL_SECONDS_TO_NS(statbuf.st_mtime); - info->access_time = (SDL_FileTime)SDL_SECONDS_TO_NS(statbuf.st_atime); - +#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200809L) || (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700) + /* Use high-res file times, if available. */ + info->create_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_ctim.tv_sec) + statbuf.st_ctim.tv_nsec; + info->modify_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_mtim.tv_sec) + statbuf.st_mtim.tv_nsec; + info->access_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_atim.tv_sec) + statbuf.st_atim.tv_nsec; +#else + info->create_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_ctime); + info->modify_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_mtime); + info->access_time = (SDL_Time)SDL_SECONDS_TO_NS(statbuf.st_atime); +#endif return 0; } diff --git a/src/filesystem/windows/SDL_sysfsops.c b/src/filesystem/windows/SDL_sysfsops.c index 177d4ba80..b688365d1 100644 --- a/src/filesystem/windows/SDL_sysfsops.c +++ b/src/filesystem/windows/SDL_sysfsops.c @@ -165,9 +165,9 @@ int SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info) info->size = ((((Uint64) winstat.nFileSizeHigh) << 32) | winstat.nFileSizeLow); } - info->create_time = SDL_FileTimeFromWindows(winstat.ftCreationTime.dwLowDateTime, winstat.ftCreationTime.dwHighDateTime); - info->modify_time = SDL_FileTimeFromWindows(winstat.ftLastWriteTime.dwLowDateTime, winstat.ftLastWriteTime.dwHighDateTime); - info->access_time = SDL_FileTimeFromWindows(winstat.ftLastAccessTime.dwLowDateTime, winstat.ftLastAccessTime.dwHighDateTime); + info->create_time = SDL_TimeFromWindows(winstat.ftCreationTime.dwLowDateTime, winstat.ftCreationTime.dwHighDateTime); + info->modify_time = SDL_TimeFromWindows(winstat.ftLastWriteTime.dwLowDateTime, winstat.ftLastWriteTime.dwHighDateTime); + info->access_time = SDL_TimeFromWindows(winstat.ftLastAccessTime.dwLowDateTime, winstat.ftLastAccessTime.dwHighDateTime); return 1; } diff --git a/src/time/SDL_time.c b/src/time/SDL_time.c new file mode 100644 index 000000000..f582575bc --- /dev/null +++ b/src/time/SDL_time.c @@ -0,0 +1,232 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#include "SDL_time_c.h" + +static SDL_bool time_initialized; + +/* The following algorithms are based on those of Howard Hinnant and are in the public domain. + * + * http://howardhinnant.github.io/date_algorithms.html + */ + +/* Given a calendar date, returns days since Jan 1 1970, and optionally + * the day of the week [0-6, 0 is Sunday] and day of the year [0-365]. + */ +Sint64 SDL_CivilToDays(int year, int month, int day, int *day_of_week, int *day_of_year) +{ + + year -= month <= 2; + const int era = (year >= 0 ? year : year - 399) / 400; + const unsigned yoe = (unsigned)(year - era * 400); // [0, 399] + const unsigned doy = (153 * (month > 2 ? month - 3 : month + 9) + 2) / 5 + day - 1; // [0, 365] + const unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; // [0, 146096] + const Sint64 z = (Sint64)(era) * 146097 + (Sint64)(doe)-719468; + + if (day_of_week) { + *day_of_week = (int)(z >= -4 ? (z + 4) % 7 : (z + 5) % 7 + 6); + } + if (day_of_year) { + /* This algorithm considers March 1 to be the first day of the year, so offset by Jan + Feb. */ + if (doy > 305) { + /* Day 0 is the first day of the year. */ + *day_of_year = doy - 306; + } else { + const int doy_offset = 59 + (!(year % 4) && ((year % 100) || !(year % 400))); + *day_of_year = doy + doy_offset; + } + } + + return z; +} + +void SDL_InitTime() +{ + if (time_initialized) { + return; + } + + /* Default to ISO 8061 date format, as it is unambiguous, and 24 hour time. */ + SDL_DATE_FORMAT dateFormat = SDL_DATE_FORMAT_YYYYMMDD; + SDL_TIME_FORMAT timeFormat = SDL_TIME_FORMAT_24HR; + SDL_PropertiesID props = SDL_GetGlobalProperties(); + + SDL_GetSystemTimeLocalePreferences(&dateFormat, &timeFormat); + + if (!SDL_HasProperty(props, SDL_PROP_GLOBAL_SYSTEM_DATE_FORMAT_NUMBER)) { + SDL_SetNumberProperty(props, SDL_PROP_GLOBAL_SYSTEM_DATE_FORMAT_NUMBER, dateFormat); + } + if (!SDL_HasProperty(props, SDL_PROP_GLOBAL_SYSTEM_TIME_FORMAT_NUMBER)) { + SDL_SetNumberProperty(props, SDL_PROP_GLOBAL_SYSTEM_TIME_FORMAT_NUMBER, timeFormat); + } + + time_initialized = SDL_TRUE; +} + +void SDL_QuitTime() +{ + time_initialized = SDL_FALSE; +} + +int SDL_GetDaysInMonth(int year, int month) +{ + static const int DAYS_IN_MONTH[] = { + 30, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + + if (month < 1 || month > 12) { + return SDL_SetError("Month out of range [1-12], requested: %i", month); + } + + int days = DAYS_IN_MONTH[month - 1]; + + /* A leap year occurs every 4 years... + * but not every 100 years... + * except for every 400 years. + */ + if (month == 2 && (!(year % 4) && ((year % 100) || !(year % 400)))) { + ++days; + } + + return days; +} + +int SDL_GetDayOfYear(int year, int month, int day) +{ + int dayOfYear; + + if (month < 1 || month > 12) { + return SDL_SetError("Month out of range [1-12], requested: %i", month); + } + if (day < 1 || day > SDL_GetDaysInMonth(year, month)) { + return SDL_SetError("Day out of range [1-%i], requested: %i", SDL_GetDaysInMonth(year, month), month); + } + + SDL_CivilToDays(year, month, day, NULL, &dayOfYear); + return dayOfYear; +} + +int SDL_GetDayOfWeek(int year, int month, int day) +{ + int dayOfWeek; + + if (month < 1 || month > 12) { + return SDL_SetError("Month out of range [1-12], requested: %i", month); + } + if (day < 1 || day > SDL_GetDaysInMonth(year, month)) { + return SDL_SetError("Day out of range [1-%i], requested: %i", SDL_GetDaysInMonth(year, month), month); + } + + SDL_CivilToDays(year, month, day, &dayOfWeek, NULL); + return dayOfWeek; +} + +static SDL_bool SDL_DateTimeIsValid(const SDL_DateTime *dt) +{ + if (dt->month < 1 || dt->month > 12) { + SDL_SetError("Malformed SDL_DateTime: month out of range [1-12], current: %i", dt->month); + return SDL_FALSE; + } + + const int daysInMonth = SDL_GetDaysInMonth(dt->year, dt->month); + if (dt->day < 1 || dt->day > daysInMonth) { + SDL_SetError("Malformed SDL_DateTime: day of month out of range [1-%i], current: %i", daysInMonth, dt->month); + return SDL_FALSE; + } + if (dt->hour < 0 || dt->hour > 23) { + SDL_SetError("Malformed SDL_DateTime: hour out of range [0-23], current: %i", dt->hour); + return SDL_FALSE; + } + if (dt->minute < 0 || dt->minute > 59) { + SDL_SetError("Malformed SDL_DateTime: minute out of range [0-59], current: %i", dt->minute); + return SDL_FALSE; + } + if (dt->second < 0 || dt->second > 60) { + SDL_SetError("Malformed SDL_DateTime: second out of range [0-60], current: %i", dt->second); + return SDL_FALSE; /* 60 accounts for a possible leap second. */ + } + if (dt->nanosecond < 0 || dt->nanosecond >= SDL_NS_PER_SECOND) { + SDL_SetError("Malformed SDL_DateTime: nanosecond out of range [0-999999999], current: %i", dt->nanosecond); + return SDL_FALSE; + } + + return SDL_TRUE; +} + +int SDL_DateTimeToTime(const SDL_DateTime *dt, SDL_Time *ticks) +{ + static const Sint64 max_seconds = SDL_NS_TO_SECONDS(SDL_MAX_TIME) - 1; + static const Sint64 min_seconds = SDL_NS_TO_SECONDS(SDL_MIN_TIME) + 1; + int ret = 0; + + if (!dt) { + return SDL_InvalidParamError("dt"); + } + if (!ticks) { + return SDL_InvalidParamError("ticks"); + } + if (!SDL_DateTimeIsValid(dt)) { + /* The validation function sets the error string. */ + return -1; + } + + *ticks = SDL_CivilToDays(dt->year, dt->month, dt->day, NULL, NULL) * SDL_SECONDS_PER_DAY; + *ticks += (((dt->hour * 60) + dt->minute) * 60) + dt->second - dt->utc_offset; + if (*ticks > max_seconds || *ticks < min_seconds) { + *ticks = SDL_clamp(*ticks, min_seconds, max_seconds); + ret = SDL_SetError("Date out of range for SDL_Time representation; SDL_Time value clamped"); + } + *ticks = SDL_SECONDS_TO_NS(*ticks) + dt->nanosecond; + + return ret; +} + +#define DELTA_EPOCH_1601_100NS (11644473600ll * 10000000ll) // [100 ns] (100 ns units between 1601-01-01 and 1970-01-01, 11644473600 seconds) + +void SDL_TimeToWindows(SDL_Time ticks, Uint32 *dwLowDateTime, Uint32 *dwHighDateTime) +{ + /* Convert nanoseconds to Win32 ticks. + * SDL_Time has a range of roughly 292 years, so even SDL_MIN_TIME can't underflow thw Win32 epoch. + */ + const Uint64 wtime = (Uint64)((ticks / 100) + DELTA_EPOCH_1601_100NS); + + if (dwLowDateTime) { + *dwLowDateTime = (Uint32)wtime; + } + + if (dwHighDateTime) { + *dwHighDateTime = (Uint32)(wtime >> 32); + } +} + +SDL_Time SDL_TimeFromWindows(Uint32 dwLowDateTime, Uint32 dwHighDateTime) +{ + static const Uint64 wintime_min = (Uint64)((SDL_MIN_TIME / 100) + DELTA_EPOCH_1601_100NS); + static const Uint64 wintime_max = (Uint64)((SDL_MAX_TIME / 100) + DELTA_EPOCH_1601_100NS); + + Uint64 wtime = (((Uint64)dwHighDateTime << 32) | dwLowDateTime); + + /* Clamp the windows time range to the SDL_Time min/max */ + wtime = SDL_clamp(wtime, wintime_min, wintime_max); + + return (SDL_Time)(wtime - DELTA_EPOCH_1601_100NS) * 100; +} diff --git a/src/time/SDL_time_c.h b/src/time/SDL_time_c.h new file mode 100644 index 000000000..89c7621a5 --- /dev/null +++ b/src/time/SDL_time_c.h @@ -0,0 +1,39 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef SDL_time_c_h_ +#define SDL_time_c_h_ + +#include "SDL_internal.h" + +#define SDL_SECONDS_PER_DAY 86400 + +extern void SDL_InitTime(void); +extern void SDL_QuitTime(void); + +/* Given a calendar date, returns days since Jan 1 1970, and optionally + * the day of the week (0-6, 0 is Sunday) and day of the year (0-365). + */ +extern Sint64 SDL_CivilToDays(int year, int month, int day, int *day_of_week, int *day_of_year); + +extern void SDL_GetSystemTimeLocalePreferences(SDL_DATE_FORMAT *df, SDL_TIME_FORMAT *tf); + +#endif /* SDL_time_c_h_ */ diff --git a/src/time/n3ds/SDL_systime.c b/src/time/n3ds/SDL_systime.c new file mode 100644 index 000000000..2ffe5e302 --- /dev/null +++ b/src/time/n3ds/SDL_systime.c @@ -0,0 +1,102 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_TIME_N3DS + +#include "../SDL_time_c.h" +#include <3ds.h> + +/* + * The 3DS clock is essentially a simple digital watch and provides + * no timezone or DST functionality. + */ + +/* 3DS epoch is Jan 1 1900 */ +#define DELTA_EPOCH_1900_OFFSET_MS 2208988800000LL + +/* Returns year/month/day triple in civil calendar + * Preconditions: z is number of days since 1970-01-01 and is in the range: + * [INT_MIN, INT_MAX-719468]. + * + * http://howardhinnant.github.io/date_algorithms.html#civil_from_days + */ +static void civil_from_days(int days, int *year, int *month, int *day) +{ + days += 719468; + const int era = (days >= 0 ? days : days - 146096) / 146097; + const unsigned doe = (unsigned)(days - era * 146097); // [0, 146096] + const unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; // [0, 399] + const int y = (int)(yoe) + era * 400; + const unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100); // [0, 365] + const unsigned mp = (5 * doy + 2) / 153; // [0, 11] + const unsigned d = doy - (153 * mp + 2) / 5 + 1; // [1, 31] + const unsigned m = mp < 10 ? mp + 3 : mp - 9; // [1, 12] + + *year = y + (m <= 2); + *month = (int)m; + *day = (int)d; +} + +void SDL_GetSystemTimeLocalePreferences(SDL_DATE_FORMAT *df, SDL_TIME_FORMAT *tf) +{ + /* NOP */ +} + +int SDL_GetCurrentTime(SDL_Time *ticks) +{ + if (!ticks) { + return SDL_InvalidParamError("ticks"); + } + + /* Returns milliseconds since the epoch. */ + const Uint64 ndsTicksMax = (SDL_MAX_TIME / SDL_NS_PER_MS) + DELTA_EPOCH_1900_OFFSET_MS; + const Uint64 ndsTicks = SDL_min(osGetTime(), ndsTicksMax); + + *ticks = SDL_MS_TO_NS(ndsTicks - DELTA_EPOCH_1900_OFFSET_MS); + + return 0; +} + +int SDL_TimeToDateTime(SDL_Time ticks, SDL_DateTime *dt, SDL_bool localTime) +{ + if (!dt) { + return SDL_InvalidParamError("dt"); + } + + const int days = (int)(SDL_NS_TO_SECONDS(ticks) / SDL_SECONDS_PER_DAY); + civil_from_days(days, &dt->year, &dt->month, &dt->day); + + int rem = (int)(SDL_NS_TO_SECONDS(ticks) - (days * SDL_SECONDS_PER_DAY)); + dt->hour = rem / (60 * 60); + rem -= dt->hour * 60 * 60; + dt->minute = rem / 60; + rem -= dt->minute * 60; + dt->second = rem; + dt->nanosecond = ticks % SDL_NS_PER_SECOND; + dt->utc_offset = 0; /* Unknown */ + + SDL_CivilToDays(dt->year, dt->month, dt->day, &dt->day_of_week, NULL); + + return 0; +} + +#endif /* SDL_TIME_N3DS */ diff --git a/src/time/ps2/SDL_systime.c b/src/time/ps2/SDL_systime.c new file mode 100644 index 000000000..e869aaa64 --- /dev/null +++ b/src/time/ps2/SDL_systime.c @@ -0,0 +1,65 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_TIME_PS2 + +#include "../SDL_time_c.h" + +/* PS2 epoch is Jan 1 2000 JST (UTC +9) */ +#define UNIX_EPOCH_OFFSET_SEC 946717200 + +/* TODO: Implement this... */ +void SDL_GetSystemTimeLocalePreferences(SDL_DATE_FORMAT *df, SDL_TIME_FORMAT *tf) +{ +} + +int SDL_GetCurrentTime(SDL_Time *ticks) +{ + if (!ticks) { + return SDL_InvalidParamError("ticks"); + } + + *ticks = 0; + + return 0; +} + +int SDL_TimeToDateTime(SDL_Time ticks, SDL_DateTime *dt, SDL_bool localTime) +{ + if (!dt) { + return SDL_InvalidParamError("dt"); + } + + dt->year = 1970; + dt->month = 1; + dt->day = 1; + dt->hour = 0; + dt->minute = 0; + dt->second = 0; + dt->nanosecond = 0; + dt->day_of_week = 4; + dt->utc_offset = 0; + + return 0; +} + +#endif /* SDL_TIME_PS2 */ diff --git a/src/time/psp/SDL_systime.c b/src/time/psp/SDL_systime.c new file mode 100644 index 000000000..4bad7b5bf --- /dev/null +++ b/src/time/psp/SDL_systime.c @@ -0,0 +1,136 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_TIME_PSP + +#include +#include + +#include "../SDL_time_c.h" + +/* Sony seems to use 0001-01-01T00:00:00 as an epoch. */ +#define DELTA_EPOCH_0001_OFFSET 62135596800ULL + +void SDL_GetSystemTimeLocalePreferences(SDL_DATE_FORMAT *df, SDL_TIME_FORMAT *tf) +{ + int val; + + if (sceUtilityGetSystemParamInt(PSP_SYSTEMPARAM_ID_INT_DATE_FORMAT, &val) == 0) { + switch (val) { + case PSP_SYSTEMPARAM_DATE_FORMAT_YYYYMMDD: + *df = SDL_DATE_FORMAT_YYYYMMDD; + break; + case PSP_SYSTEMPARAM_DATE_FORMAT_MMDDYYYY: + *df = SDL_DATE_FORMAT_MMDDYYYY; + break; + case PSP_SYSTEMPARAM_DATE_FORMAT_DDMMYYYY: + *df = SDL_DATE_FORMAT_DDMMYYYY; + break; + default: + break; + } + } + + if (sceUtilityGetSystemParamInt(PSP_SYSTEMPARAM_ID_INT_TIME_FORMAT, &val) == 0) { + switch (val) { + case PSP_SYSTEMPARAM_TIME_FORMAT_24HR: + *tf = SDL_TIME_FORMAT_24HR; + break; + case PSP_SYSTEMPARAM_TIME_FORMAT_12HR: + *tf = SDL_TIME_FORMAT_12HR; + break; + default: + break; + } + } +} + +int SDL_GetCurrentTime(SDL_Time *ticks) +{ + u64 sceTicks; + + if (!ticks) { + return SDL_InvalidParamError("ticks"); + } + + const int ret = sceRtcGetCurrentTick(&sceTicks); + if (!ret) { + const u32 res = sceRtcGetTickResolution(); + const u32 div = SDL_NS_PER_SECOND / res; + const Uint64 epoch_offset = DELTA_EPOCH_0001_OFFSET * res; + + const Uint64 scetime_min = (Uint64)((SDL_MIN_TIME / div) + epoch_offset); + const Uint64 scetime_max = (Uint64)((SDL_MAX_TIME / div) + epoch_offset); + + /* Clamp to the valid SDL_Time range. */ + sceTicks = SDL_clamp(sceTicks, scetime_min, scetime_max); + + *ticks = (SDL_Time)(sceTicks - epoch_offset) * div; + + return 0; + } + + return SDL_SetError("Failed to retrieve system time (%i)", ret); +} + +int SDL_TimeToDateTime(SDL_Time ticks, SDL_DateTime *dt, SDL_bool localTime) +{ + ScePspDateTime t; + u64 local; + int ret = 0; + + if (!dt) { + return SDL_InvalidParamError("dt"); + } + + const u32 res = sceRtcGetTickResolution(); + const u32 div = (SDL_NS_PER_SECOND / res); + const u64 sceTicks = (u64)((ticks / div) + (DELTA_EPOCH_0001_OFFSET * div)); + + if (localTime) { + ret = sceRtcConvertUtcToLocalTime(&sceTicks, &local); + } else { + local = sceTicks; + } + + if (!ret) { + ret = sceRtcSetTick(&t, &local); + if (!ret) { + dt->year = t.year; + dt->month = t.month; + dt->day = t.day; + dt->hour = t.hour; + dt->minute = t.minute; + dt->second = t.second; + dt->nanosecond = ticks % SDL_NS_PER_SECOND; + dt->utc_offset = (int)(((Sint64)local - (Sint64)sceTicks) / (Sint64)res); + + SDL_CivilToDays(dt->year, dt->month, dt->day, &dt->day_of_week, NULL); + + return 0; + } + } + + return SDL_SetError("Local time conversion failed (%i)", ret); +} + +#endif /* SDL_TIME_PSP */ diff --git a/src/time/unix/SDL_systime.c b/src/time/unix/SDL_systime.c new file mode 100644 index 000000000..088722aa5 --- /dev/null +++ b/src/time/unix/SDL_systime.c @@ -0,0 +1,193 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_TIME_UNIX + +#include "../SDL_time_c.h" +#include +#include +#include +#include + +#if !defined(HAVE_CLOCK_GETTIME) && defined(SDL_PLATFORM_APPLE) +#include +#include +#include +#endif + +void SDL_GetSystemTimeLocalePreferences(SDL_DATE_FORMAT *df, SDL_TIME_FORMAT *tf) +{ + /* This *should* be well-supported aside from very old legacy systems, but apparently + * Android didn't add this until SDK version 26, so a check is needed... + */ +#ifdef HAVE_NL_LANGINFO + const char *s = nl_langinfo(D_FMT); + + /* Figure out the preferred system date format from the first format character. */ + if (s) { + while (*s) { + switch (*s++) { + case 'Y': + case 'y': + case 'F': + case 'C': + *df = SDL_DATE_FORMAT_YYYYMMDD; + goto found_date; + case 'd': + case 'e': + *df = SDL_DATE_FORMAT_DDMMYYYY; + goto found_date; + case 'b': + case 'D': + case 'h': + case 'm': + *df = SDL_DATE_FORMAT_MMDDYYYY; + goto found_date; + default: + break; + } + } + } + +found_date: + + s = nl_langinfo(T_FMT); + + /* Figure out the preferred system date format. */ + if (s) { + while (*s) { + switch (*s++) { + case 'H': + case 'k': + case 'T': + *tf = SDL_TIME_FORMAT_24HR; + return; + case 'I': + case 'l': + case 'r': + *tf = SDL_TIME_FORMAT_12HR; + return; + default: + break; + } + } + } +#endif +} + +int SDL_GetCurrentTime(SDL_Time *ticks) +{ + if (!ticks) { + return SDL_InvalidParamError("ticks"); + } +#ifdef HAVE_CLOCK_GETTIME + struct timespec tp; + + if (clock_gettime(CLOCK_REALTIME, &tp) == 0) { + tp.tv_sec = SDL_min(tp.tv_sec, SDL_NS_TO_SECONDS(SDL_MAX_TIME) - 1); + *ticks = SDL_SECONDS_TO_NS(tp.tv_sec) + tp.tv_nsec; + return 0; + } + + SDL_SetError("Failed to retrieve system time (%i)", errno); + +#elif defined(SDL_PLATFORM_APPLE) + clock_serv_t cclock; + int ret = host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + if (ret == 0) { + mach_timespec_t mts; + + SDL_zero(mts); + ret = clock_get_time(cclock, &mts); + if (ret == 0) { + /* mach_timespec_t tv_sec is 32-bit, so no overflow possible */ + *ticks = SDL_SECONDS_TO_NS(mts.tv_sec) + mts.tv_nsec; + } + mach_port_deallocate(mach_task_self(), cclock); + + if (!ret) { + return 0; + } + } + + SDL_SetError("Failed to retrieve system time (%i)", ret); + +#else + struct timeval tv; + SDL_zero(tv); + if (gettimeofday(&tv, NULL) == 0) { + tv.tv_sec = SDL_min(tv.tv_sec, SDL_NS_TO_SECONDS(SDL_MAX_TIME) - 1); + *ticks = SDL_SECONDS_TO_NS(tv.tv_sec) + SDL_US_TO_NS(tv.tv_usec); + return 0; + } + + SDL_SetError("Failed to retrieve system time (%i)", errno); +#endif + + return -1; +} + +int SDL_TimeToDateTime(SDL_Time ticks, SDL_DateTime *dt, SDL_bool localTime) +{ +#if defined (HAVE_GMTIME_R) || defined(HAVE_LOCALTIME_R) + struct tm tm_storage; +#endif + struct tm *tm = NULL; + + if (!dt) { + return SDL_InvalidParamError("dt"); + } + + const time_t tval = (time_t)SDL_NS_TO_SECONDS(ticks); + + if (localTime) { +#ifdef HAVE_LOCALTIME_R + tm = localtime_r(&tval, &tm_storage); +#else + tm = localtime(&tval); +#endif + } else { +#ifdef HAVE_GMTIME_R + tm = gmtime_r(&tval, &tm_storage); +#else + tm = gmtime(&tval); +#endif + } + + if (tm) { + dt->year = tm->tm_year + 1900; + dt->month = tm->tm_mon + 1; + dt->day = tm->tm_mday; + dt->hour = tm->tm_hour; + dt->minute = tm->tm_min; + dt->second = tm->tm_sec; + dt->nanosecond = ticks % SDL_NS_PER_SECOND; + dt->day_of_week = tm->tm_wday; + dt->utc_offset = tm->tm_gmtoff; + + return 0; + } + + return SDL_SetError("SDL_DateTime conversion failed (%i)", errno); +} + +#endif /* SDL_TIME_UNIX */ diff --git a/src/time/vita/SDL_systime.c b/src/time/vita/SDL_systime.c new file mode 100644 index 000000000..6b165f186 --- /dev/null +++ b/src/time/vita/SDL_systime.c @@ -0,0 +1,135 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_TIME_VITA + +#include "../SDL_time_c.h" +#include +#include +#include + +/* Sony seems to use 0001-01-01T00:00:00 as an epoch. */ +#define DELTA_EPOCH_0001_OFFSET 62135596800ULL + +void SDL_GetSystemTimeLocalePreferences(SDL_DATE_FORMAT *df, SDL_TIME_FORMAT *tf) +{ + int val; + + if (sceAppUtilSystemParamGetInt(SCE_SYSTEM_PARAM_ID_DATE_FORMAT, &val) == 0) { + switch (val) { + case SCE_SYSTEM_PARAM_DATE_FORMAT_YYYYMMDD: + *df = SDL_DATE_FORMAT_YYYYMMDD; + break; + case SCE_SYSTEM_PARAM_DATE_FORMAT_MMDDYYYY: + *df = SDL_DATE_FORMAT_MMDDYYYY; + break; + case SCE_SYSTEM_PARAM_DATE_FORMAT_DDMMYYYY: + *df = SDL_DATE_FORMAT_DDMMYYYY; + break; + default: + break; + } + } + + if (sceAppUtilSystemParamGetInt(SCE_SYSTEM_PARAM_ID_DATE_FORMAT, &val) == 0) { + switch (val) { + case SCE_SYSTEM_PARAM_TIME_FORMAT_24HR: + *tf = SDL_TIME_FORMAT_24HR; + break; + case SCE_SYSTEM_PARAM_TIME_FORMAT_12HR: + *tf = SDL_TIME_FORMAT_12HR; + break; + default: + break; + } + } +} + +int SDL_GetCurrentTime(SDL_Time *ticks) +{ + SceRtcTick sceTicks; + + if (!ticks) { + return SDL_InvalidParamError("ticks"); + } + + const int ret = sceRtcGetCurrentTick(&sceTicks); + if (!ret) { + const unsigned int res = sceRtcGetTickResolution(); + const unsigned int div = SDL_NS_PER_SECOND / res; + const Uint64 epoch_offset = DELTA_EPOCH_0001_OFFSET * res; + + const Uint64 scetime_min = (Uint64)((SDL_MIN_TIME / div) + epoch_offset); + const Uint64 scetime_max = (Uint64)((SDL_MAX_TIME / div) + epoch_offset); + + /* Clamp to the valid SDL_Time range. */ + sceTicks.tick = SDL_clamp(sceTicks.tick, scetime_min, scetime_max); + *ticks = (SDL_Time)(sceTicks.tick - epoch_offset) * div; + + return 0; + } + + return SDL_SetError("Failed to retrieve system time (%i)", ret); +} + +int SDL_TimeToDateTime(SDL_Time ticks, SDL_DateTime *dt, SDL_bool localTime) +{ + SceDateTime t; + SceRtcTick sceTicks, sceLocalTicks; + int ret = 0; + + if (!dt) { + return SDL_InvalidParamError("dt"); + } + + const unsigned int res = sceRtcGetTickResolution(); + const unsigned int div = (SDL_NS_PER_SECOND / res); + sceTicks.tick = (Uint64)((ticks / div) + (DELTA_EPOCH_0001_OFFSET * div)); + + if (localTime) { + ret = sceRtcConvertUtcToLocalTime(&sceTicks, &sceLocalTicks); + } else { + sceLocalTicks.tick = sceTicks.tick; + } + + if (!ret) { + ret = sceRtcSetTick(&t, &sceLocalTicks); + if (!ret) { + dt->year = t.year; + dt->month = t.month; + dt->day = t.day; + dt->hour = t.hour; + dt->minute = t.minute; + dt->second = t.second; + dt->nanosecond = ticks % SDL_NS_PER_SECOND; + dt->utc_offset = (int)(((Sint64)sceLocalTicks.tick - (Sint64)sceTicks.tick) / (Sint64)res); + + SDL_CivilToDays(dt->year, dt->month, dt->day, &dt->day_of_week, NULL); + + return 0; + } + } + + return SDL_SetError("Local time conversion failed (%i)", ret); +} + +#endif /* SDL_TIME_VITA */ diff --git a/src/time/windows/SDL_systime.c b/src/time/windows/SDL_systime.c new file mode 100644 index 000000000..2058efebf --- /dev/null +++ b/src/time/windows/SDL_systime.c @@ -0,0 +1,139 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_internal.h" + +#ifdef SDL_TIME_WINDOWS + +#include "../../core/windows/SDL_windows.h" +#include +#include + +#include "../SDL_time_c.h" + +#define NS_PER_WINDOWS_TICK 100ULL +#define WINDOWS_TICK 10000000ULL +#define UNIX_EPOCH_OFFSET_SEC 11644473600ULL + +void SDL_GetSystemTimeLocalePreferences(SDL_DATE_FORMAT *df, SDL_TIME_FORMAT *tf) +{ + WCHAR str[80]; /* Per the docs, the time and short date format strings can be a max of 80 characters. */ + + if (GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, str, sizeof(str) / sizeof(WCHAR))) { + LPWSTR s = str; + while (*s) { + switch (*s++) { + case L'y': + *df = SDL_DATE_FORMAT_YYYYMMDD; + goto found_date; + case L'd': + *df = SDL_DATE_FORMAT_DDMMYYYY; + goto found_date; + case L'M': + *df = SDL_DATE_FORMAT_MMDDYYYY; + goto found_date; + default: + break; + } + } + } + +found_date: + + /* Figure out the preferred system date format. */ + if (GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_STIMEFORMAT, str, sizeof(str) / sizeof(WCHAR))) { + LPWSTR s = str; + while (*s) { + switch (*s++) { + case L'H': + *tf = SDL_TIME_FORMAT_24HR; + return; + case L'h': + *tf = SDL_TIME_FORMAT_12HR; + return; + default: + break; + } + } + } +} + +int SDL_GetCurrentTime(SDL_Time *ticks) +{ + FILETIME ft; + + if (!ticks) { + return SDL_InvalidParamError("ticks"); + } + + SDL_zero(ft); + GetSystemTimePreciseAsFileTime(&ft); + *ticks = SDL_TimeFromWindows(ft.dwLowDateTime, ft.dwHighDateTime); + + return 0; +} + +int SDL_TimeToDateTime(SDL_Time ticks, SDL_DateTime *dt, SDL_bool localTime) +{ + FILETIME ft, local_ft; + SYSTEMTIME utc_st, local_st; + SYSTEMTIME *st = NULL; + Uint32 low, high; + + if (!dt) { + return SDL_InvalidParamError("dt"); + } + + SDL_TimeToWindows(ticks, &low, &high); + ft.dwLowDateTime = (DWORD)low; + ft.dwHighDateTime = (DWORD)high; + + if (FileTimeToSystemTime(&ft, &utc_st)) { + if (localTime) { + if (SystemTimeToTzSpecificLocalTime(NULL, &utc_st, &local_st)) { + /* Calculate the difference for the UTC offset. */ + SystemTimeToFileTime(&local_st, &local_ft); + const SDL_Time local_ticks = SDL_TimeFromWindows(local_ft.dwLowDateTime, local_ft.dwHighDateTime); + dt->utc_offset = SDL_NS_TO_SECONDS(local_ticks - ticks); + st = &local_st; + } + } else { + dt->utc_offset = 0; + st = &utc_st; + } + + if (st) { + dt->year = st->wYear; + dt->month = st->wMonth; + dt->day = st->wDay; + dt->hour = st->wHour; + dt->minute = st->wMinute; + dt->second = st->wSecond; + dt->nanosecond = ticks % SDL_NS_PER_SECOND; + dt->day_of_week = st->wDayOfWeek; + + return 0; + } + } + + return SDL_SetError("SDL_DateTime conversion failed (%lu)", GetLastError()); +} + +#endif /* SDL_TIME_WINDOWS */ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9efaf646f..338b29b7a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -413,6 +413,7 @@ add_sdl_test_executable(testvulkan NO_C90 SOURCES testvulkan.c) add_sdl_test_executable(testoffscreen SOURCES testoffscreen.c) add_sdl_test_executable(testpopup SOURCES testpopup.c) add_sdl_test_executable(testdialog SOURCES testdialog.c) +add_sdl_test_executable(testtime SOURCES testtime.c) if (HAVE_WAYLAND) # Set the GENERATED property on the protocol file, since it is first created at build time diff --git a/test/testautomation.c b/test/testautomation.c index 847cb3b16..e05dc1fbe 100644 --- a/test/testautomation.c +++ b/test/testautomation.c @@ -44,6 +44,7 @@ static SDLTest_TestSuiteReference *testSuites[] = { &sdltestTestSuite, &stdlibTestSuite, &surfaceTestSuite, + &timeTestSuite, &timerTestSuite, &videoTestSuite, &subsystemsTestSuite, /* run last, not interfere with other test enviroment */ diff --git a/test/testautomation_suites.h b/test/testautomation_suites.h index 964d047a7..39ae9e44e 100644 --- a/test/testautomation_suites.h +++ b/test/testautomation_suites.h @@ -32,6 +32,7 @@ extern SDLTest_TestSuiteReference sdltestTestSuite; extern SDLTest_TestSuiteReference stdlibTestSuite; extern SDLTest_TestSuiteReference subsystemsTestSuite; extern SDLTest_TestSuiteReference surfaceTestSuite; +extern SDLTest_TestSuiteReference timeTestSuite; extern SDLTest_TestSuiteReference timerTestSuite; extern SDLTest_TestSuiteReference videoTestSuite; diff --git a/test/testautomation_time.c b/test/testautomation_time.c new file mode 100644 index 000000000..11f00a2b5 --- /dev/null +++ b/test/testautomation_time.c @@ -0,0 +1,201 @@ +/** + * Timer test suite + */ +#include "testautomation_suites.h" +#include +#include + +/* 2000-01-01T16:35:42 UTC */ +#define JAN_1_2000_NS SDL_SECONDS_TO_NS(946744542) + +/* Test case functions */ + +/** + * Call to SDL_GetRealtimeClock + */ +static int time_getRealtimeClock(void *arg) +{ + int result; + SDL_Time ticks; + + result = SDL_GetCurrentTime(&ticks); + SDLTest_AssertPass("Call to SDL_GetRealtimeClockTicks()"); + SDLTest_AssertCheck(result == 0, "Check result value, expected 0, got: %i", result); + + return TEST_COMPLETED; +} + +/** + * Test bidirectional SDL_DateTime conversions. + */ +static int time_dateTimeConversion(void *arg) +{ + int result; + SDL_Time ticks[2]; + SDL_DateTime dt; + + ticks[0] = JAN_1_2000_NS; + + result = SDL_TimeToDateTime(ticks[0], &dt, SDL_FALSE); + SDLTest_AssertPass("Call to SDL_TimeToUTCDateTime()"); + SDLTest_AssertCheck(result == 0, "Check result value, expected 0, got: %i", result); + SDLTest_AssertCheck(dt.year == 2000, "Check year value, expected 2000, got: %i", dt.year); + SDLTest_AssertCheck(dt.month == 1, "Check month value, expected 1, got: %i", dt.month); + SDLTest_AssertCheck(dt.day == 1, "Check day value, expected 1, got: %i", dt.day); + SDLTest_AssertCheck(dt.hour == 16, "Check hour value, expected 16, got: %i", dt.hour); + SDLTest_AssertCheck(dt.minute == 35, "Check hour value, expected 35, got: %i", dt.minute); + SDLTest_AssertCheck(dt.second == 42, "Check hour value, expected 42, got: %i", dt.second); + + result = SDL_DateTimeToTime(&dt, &ticks[1]); + SDLTest_AssertPass("Call to SDL_DateTimeToTime()"); + SDLTest_AssertCheck(result == 0, "Check result value, expected 0, got: %i", result); + + result = ticks[0] == ticks[1]; + SDLTest_AssertCheck(result, "Check that original and converted SDL_Time values match: ticks0 = %" SDL_PRIs64 ", ticks1 = %" SDL_PRIs64, ticks[0], ticks[1]); + + /* Local time unknown, so just verify success. */ + result = SDL_TimeToDateTime(ticks[0], &dt, SDL_TRUE); + SDLTest_AssertPass("Call to SDL_TimeToLocalDateTime()"); + SDLTest_AssertCheck(result == 0, "Check result value, expected 0, got: %i", result); + + /* Convert back and verify result. */ + result = SDL_DateTimeToTime(&dt, &ticks[1]); + SDLTest_AssertPass("Call to SDL_DateTimeToTime()"); + SDLTest_AssertCheck(result == 0, "Check result value, expected 0, got: %i", result); + + result = ticks[0] == ticks[1]; + SDLTest_AssertCheck(result, "Check that original and converted SDL_Time values match: ticks0 = %" SDL_PRIs64 ", ticks1 = %" SDL_PRIs64, ticks[0], ticks[1]); + + /* Advance the time one day. */ + ++dt.day; + if (dt.day > SDL_GetDaysInMonth(dt.year, dt.month)) { + dt.day = 1; + ++dt.month; + } + if (dt.month > 12) { + dt.month = 1; + ++dt.year; + } + + result = SDL_DateTimeToTime(&dt, &ticks[1]); + SDLTest_AssertPass("Call to SDL_DateTimeToTime() (one day advanced)"); + SDLTest_AssertCheck(result == 0, "Check result value, expected 0, got: %i", result); + + result = (ticks[0] + SDL_SECONDS_TO_NS(86400)) == ticks[1]; + SDLTest_AssertCheck(result, "Check that the difference is exactly 86400 seconds, got: %" SDL_PRIs64, (Sint64)SDL_NS_TO_SECONDS(ticks[1] - ticks[0])); + + /* Check dates that overflow/underflow an SDL_Time */ + dt.year = 2400; + dt.month = 1; + dt.day = 1; + result = SDL_DateTimeToTime(&dt, &ticks[0]); + SDLTest_AssertPass("Call to SDL_DateTimeToTime() (year overflows an SDL_Time)"); + SDLTest_AssertCheck(result == -1, "Check result value, expected -1, got: %i", result); + + dt.year = 1601; + result = SDL_DateTimeToTime(&dt, &ticks[0]); + SDLTest_AssertPass("Call to SDL_DateTimeToTime() (year underflows an SDL_Time)"); + SDLTest_AssertCheck(result == -1, "Check result value, expected -1, got: %i", result); + + return TEST_COMPLETED; +} + +/** + * Test time utility functions. + */ +static int time_dateTimeUtilities(void *arg) +{ + int result; + + /* Leap-year */ + result = SDL_GetDaysInMonth(2000, 2); + SDLTest_AssertPass("Call to SDL_GetDaysInMonth(2000, 2)"); + SDLTest_AssertCheck(result == 29, "Check result value, expected 29, got: %i", result); + + result = SDL_GetDaysInMonth(2001, 2); + SDLTest_AssertPass("Call to SDL_GetDaysInMonth(2001, 2)"); + SDLTest_AssertCheck(result == 28, "Check result value, expected 28, got: %i", result); + + result = SDL_GetDaysInMonth(2001, 13); + SDLTest_AssertPass("Call to SDL_GetDaysInMonth(2001, 13)"); + SDLTest_AssertCheck(result == -1, "Check result value, expected -1, got: %i", result); + + result = SDL_GetDaysInMonth(2001, -1); + SDLTest_AssertPass("Call to SDL_GetDaysInMonth(2001, 13)"); + SDLTest_AssertCheck(result == -1, "Check result value, expected -1, got: %i", result); + + /* 2000-02-29 was a Tuesday */ + result = SDL_GetDayOfWeek(2000, 2, 29); + SDLTest_AssertPass("Call to SDL_GetDayOfWeek(2000, 2, 29)"); + SDLTest_AssertCheck(result == 2, "Check result value, expected %i, got: %i", 2, result); + + /* Nonexistent day */ + result = SDL_GetDayOfWeek(2001, 2, 29); + SDLTest_AssertPass("Call to SDL_GetDayOfWeek(2001, 2, 29)"); + SDLTest_AssertCheck(result == -1, "Check result value, expected -1, got: %i", result); + + result = SDL_GetDayOfYear(2000, 1, 1); + SDLTest_AssertPass("Call to SDL_GetDayOfWeek(2001, 1, 1)"); + SDLTest_AssertCheck(result == 0, "Check result value, expected 0, got: %i", result); + + /* Leap-year */ + result = SDL_GetDayOfYear(2000, 12, 31); + SDLTest_AssertPass("Call to SDL_GetDayOfYear(2000, 12, 31)"); + SDLTest_AssertCheck(result == 365, "Check result value, expected 365, got: %i", result); + + result = SDL_GetDayOfYear(2001, 12, 31); + SDLTest_AssertPass("Call to SDL_GetDayOfYear(2000, 12, 31)"); + SDLTest_AssertCheck(result == 364, "Check result value, expected 364, got: %i", result); + + /* Nonexistent day */ + result = SDL_GetDayOfYear(2001, 2, 29); + SDLTest_AssertPass("Call to SDL_GetDayOfYear(2001, 2, 29)"); + SDLTest_AssertCheck(result == -1, "Check result value, expected -1, got: %i", result); + + /* Test Win32 time conversion */ + Uint64 wintime = 11644473600LL * 10000000LL; /* The epoch */ + SDL_Time ticks = SDL_TimeFromWindows((Uint32)(wintime & 0xFFFFFFFF), (Uint32)(wintime >> 32)); + SDLTest_AssertPass("Call to SDL_TimeFromWindows()"); + SDLTest_AssertCheck(ticks == 0, "Check result value, expected 0, got: %" SDL_PRIs64, ticks); + + /* Out of range times should be clamped instead of rolling over */ + wintime = 0; + ticks = SDL_TimeFromWindows((Uint32)(wintime & 0xFFFFFFFF), (Uint32)(wintime >> 32)); + SDLTest_AssertPass("Call to SDL_TimeFromWindows()"); + SDLTest_AssertCheck(ticks < 0 && ticks >= SDL_MIN_TIME, "Check result value, expected <0 && >=%" SDL_PRIs64 ", got: %" SDL_PRIs64, SDL_MIN_TIME, ticks); + + wintime = 0xFFFFFFFFFFFFFFFFULL; + ticks = SDL_TimeFromWindows((Uint32)(wintime & 0xFFFFFFFF), (Uint32)(wintime >> 32)); + SDLTest_AssertPass("Call to SDL_TimeFromWindows()"); + SDLTest_AssertCheck(ticks > 0 && ticks <= SDL_MAX_TIME, "Check result value, expected >0 && <=%" SDL_PRIs64 ", got: %" SDL_PRIs64, SDL_MAX_TIME, ticks); + + return TEST_COMPLETED; +} + +/* ================= Test References ================== */ + +/* Time test cases */ +static const SDLTest_TestCaseReference timeTest1 = { + (SDLTest_TestCaseFp)time_getRealtimeClock, "time_getRealtimeClock", "Call to SDL_GetRealtimeClockTicks", TEST_ENABLED +}; + +static const SDLTest_TestCaseReference timeTest2 = { + (SDLTest_TestCaseFp)time_dateTimeConversion, "time_dateTimeConversion", "Call to SDL_TimeToDateTime/SDL_DateTimeToTime", TEST_ENABLED +}; + +static const SDLTest_TestCaseReference timeTest3 = { + (SDLTest_TestCaseFp)time_dateTimeUtilities, "time_dateTimeUtilities", "Call to SDL_TimeToDateTime/SDL_DateTimeToTime", TEST_ENABLED +}; + +/* Sequence of Timer test cases */ +static const SDLTest_TestCaseReference *timeTests[] = { + &timeTest1, &timeTest2, &timeTest3, NULL +}; + +/* Time test suite (global) */ +SDLTest_TestSuiteReference timeTestSuite = { + "Time", + NULL, + timeTests, + NULL +}; diff --git a/test/testtime.c b/test/testtime.c new file mode 100644 index 000000000..f74841f31 --- /dev/null +++ b/test/testtime.c @@ -0,0 +1,214 @@ +/* + Copyright (C) 1997-2024 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely. +*/ + +/* Test program to check the resolution of the SDL timer on the current + platform +*/ +#include +#include +#include + +#define CAL_Y_OFF 100 +#define CAL_X_OFF 19 +#define CELL_WIDTH 86 +#define CELL_HEIGHT 60 + +static int cal_year; +static int cal_month; +static SDL_TIME_FORMAT time_format; +static SDL_DATE_FORMAT date_format; + +static void RenderDateTime(SDL_Renderer *r) +{ + const char *const WDAY[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + const char *const MNAME[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", + "Aug", "Sep", "Oct", "Nov", "Dec" }; + const char *const TIMEPOST[] = { "", " AM", " PM" }; + + int x, y, day, len; + const char *postfix = TIMEPOST[0]; + const int x_max = CAL_X_OFF + (CELL_WIDTH * 7); + const int y_max = CAL_Y_OFF + (CELL_HEIGHT * 6); + char str[256]; + char short_date[128]; + SDL_Time ticks; + SDL_DateTime dt; + + SDL_SetRenderDrawColor(r, 0xFF, 0xFF, 0xFF, 0xFF); + + /* Query the current time and print it. */ + SDL_GetCurrentTime(&ticks); + SDL_TimeToDateTime(ticks, &dt, SDL_FALSE); + + switch (date_format) { + case SDL_DATE_FORMAT_YYYYMMDD: + SDL_snprintf(short_date, sizeof(short_date), "%04d-%02d-%02d", dt.year, dt.month, dt.day); + break; + case SDL_DATE_FORMAT_DDMMYYYY: + SDL_snprintf(short_date, sizeof(short_date), "%02d.%02d.%04d", dt.day, dt.month, dt.year); + break; + case SDL_DATE_FORMAT_MMDDYYYY: + SDL_snprintf(short_date, sizeof(short_date), "%02d/%02d/%04d", dt.month, dt.day, dt.year); + break; + } + + if (time_format) { + if (dt.hour > 12) { /* PM */ + dt.hour -= 12; + postfix = TIMEPOST[2]; + } else { + if (!dt.hour) { /* AM */ + dt.hour = 12; /* Midnight */ + } + postfix = TIMEPOST[1]; + } + } + + SDL_snprintf(str, sizeof(str), "UTC: %s %02d %s %04d (%s) %02d:%02d:%02d.%09d%s %+05d", + WDAY[dt.day_of_week], dt.day, MNAME[dt.month - 1], dt.year, short_date, + dt.hour, dt.minute, dt.second, dt.nanosecond, postfix, ((dt.utc_offset / 3600) * 100) + (dt.utc_offset % 3600)); + + SDLTest_DrawString(r, 10, 15, str); + + SDL_TimeToDateTime(ticks, &dt, SDL_TRUE); + SDL_snprintf(str, sizeof(str), "Local: %s %02d %s %04d (%s) %02d:%02d:%02d.%09d%s %+05d", + WDAY[dt.day_of_week], dt.day, MNAME[dt.month - 1], dt.year, short_date, + dt.hour, dt.minute, dt.second, dt.nanosecond, postfix, + ((dt.utc_offset / 3600) * 100) + (dt.utc_offset % 3600)); + SDLTest_DrawString(r, 10, 30, str); + + /* Draw a calendar. */ + if (!cal_month) { + cal_month = dt.month; + cal_year = dt.year; + } + + for (y = CAL_Y_OFF; y <= CAL_Y_OFF + (CELL_HEIGHT * 6); y += CELL_HEIGHT) { + SDL_RenderLine(r, CAL_X_OFF, y, x_max, y); + } + for (x = CAL_X_OFF; x <= CAL_X_OFF + (CELL_WIDTH * 7); x += CELL_WIDTH) { + SDL_RenderLine(r, x, CAL_Y_OFF, x, y_max); + } + + /* Draw the month and year. */ + len = SDL_snprintf(str, sizeof(str), "%s %04d", MNAME[cal_month - 1], cal_year); + SDLTest_DrawString(r, (CAL_X_OFF + ((x_max - CAL_X_OFF) / 2)) - ((FONT_CHARACTER_SIZE * len) / 2), CAL_Y_OFF - (FONT_LINE_HEIGHT * 3), str); + + /* Draw day names */ + for (x = 0; x < 7; ++x) { + int offset = ((CAL_X_OFF + (CELL_WIDTH * x)) + (CELL_WIDTH / 2)) - ((FONT_CHARACTER_SIZE * 3) / 2); + SDLTest_DrawString(r, offset, CAL_Y_OFF - FONT_LINE_HEIGHT, WDAY[x]); + } + + day = SDL_GetDayOfWeek(cal_year, cal_month, 1); + x = CAL_X_OFF + (day * CELL_WIDTH + (CELL_WIDTH - (FONT_CHARACTER_SIZE * 3))); + day = 0; + y = CAL_Y_OFF + FONT_LINE_HEIGHT; + while (++day <= SDL_GetDaysInMonth(cal_year, cal_month)) { + SDL_snprintf(str, sizeof(str), "%02d", day); + + /* Highlight the current day in red. */ + if (cal_year == dt.year && cal_month == dt.month && day == dt.day) { + SDL_SetRenderDrawColor(r, 0xFF, 0, 0, 0xFF); + } + SDLTest_DrawString(r, x, y, str); + SDL_SetRenderDrawColor(r, 0xFF, 0xFF, 0xFF, 0xFF); + + x += CELL_WIDTH; + if (x >= x_max) { + x = CAL_X_OFF + (CELL_WIDTH - (FONT_CHARACTER_SIZE * 3)); + y += CELL_HEIGHT; + } + } +} + +int main(int argc, char *argv[]) +{ + SDLTest_CommonState *state; + SDL_Event event; + int done; + + /* Initialize test framework */ + state = SDLTest_CommonCreateState(argv, SDL_INIT_VIDEO); + if (!state) { + return 1; + } + + /* Enable standard application logging */ + SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); + + /* Parse commandline */ + if (!SDLTest_CommonDefaultArgs(state, argc, argv)) { + return 1; + } + + if (!SDLTest_CommonInit(state)) { + goto quit; + } + + time_format = SDL_GetNumberProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_SYSTEM_TIME_FORMAT_NUMBER, SDL_TIME_FORMAT_24HR); + date_format = SDL_GetNumberProperty(SDL_GetGlobalProperties(), SDL_PROP_GLOBAL_SYSTEM_DATE_FORMAT_NUMBER, SDL_DATE_FORMAT_YYYYMMDD); + + /* Main render loop */ + done = 0; + + while (!done) { + /* Check for events */ + while (SDL_PollEvent(&event)) { + SDLTest_CommonEvent(state, &event, &done); + if (event.type == SDL_EVENT_KEY_DOWN) { + switch (event.key.keysym.sym) { + case SDLK_UP: + if (++cal_month > 12) { + cal_month = 1; + ++cal_year; + } + break; + case SDLK_DOWN: + if (--cal_month < 1) { + cal_month = 12; + --cal_year; + } + break; + case SDLK_1: + time_format = SDL_TIME_FORMAT_24HR; + break; + case SDLK_2: + time_format = SDL_TIME_FORMAT_12HR; + break; + case SDLK_3: + date_format = SDL_DATE_FORMAT_YYYYMMDD; + break; + case SDLK_4: + date_format = SDL_DATE_FORMAT_DDMMYYYY; + break; + case SDLK_5: + date_format = SDL_DATE_FORMAT_MMDDYYYY; + break; + default: + break; + } + } + } + + SDL_SetRenderDrawColor(state->renderers[0], 0x00, 0x00, 0x00, 0xFF); + SDL_RenderClear(state->renderers[0]); + + RenderDateTime(state->renderers[0]); + + SDL_RenderPresent(state->renderers[0]); + } + +quit: + SDLTest_CommonQuit(state); + return 0; +} \ No newline at end of file