diff --git a/Android.mk b/Android.mk
index 2b42555596..a60a0d3636 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 8017bc2b79..9b288c932b 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 948e75578a..53926d49a1 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 bc62d7a0ba..39aac2b531 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 4948abf9b5..75ffad0c47 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 ca56e1757e..144061e656 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 24c397472c..d50987c370 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 5d3e43001a..766c3bc8d4 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 7fc14cb70f..84539df432 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 1f066dde06..d39f5b860b 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 4a25af8c19..8dac3e9f97 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 f39a9e10cb..239a6513d0 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 281aa56d16..0a5cf33b44 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 0000000000..f87118e445
--- /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 c71a0cafb0..951771e494 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 b784afd000..19df2278f2 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 875f1942aa..44c185cce6 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 c853e58a9b..1734beb821 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 69852a766d..9f55afffdd 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 f760cf477e..3fdaf4c769 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 9a7702d870..c9baf5aeed 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 3653552a12..f010ad380e 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 f8b773448e..e651008fc9 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 cf3fb9fd36..ac9310c489 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 1cb3f0738f..f1ff330c61 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 7fee9008ac..2a8667ffa7 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 20ffce9056..8e4939f1a9 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 177d4ba805..b688365d14 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 0000000000..f582575bcb
--- /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 0000000000..89c7621a58
--- /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 0000000000..2ffe5e302c
--- /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 0000000000..e869aaa647
--- /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 0000000000..4bad7b5bf8
--- /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 0000000000..088722aa58
--- /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 0000000000..6b165f1863
--- /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 0000000000..2058efebfe
--- /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 9efaf646ff..338b29b7a5 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 847cb3b16b..e05dc1fbeb 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 964d047a70..39ae9e44ef 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 0000000000..11f00a2b5f
--- /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 0000000000..f74841f315
--- /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