Skip to content

Commit 1249ad1

Browse files
authored
Replace strptime with portable code (#1185)
* Prepare CMake code for tests to module code * Add tests for DatetimeXMLstring_to_mseconds * Get rid of strptime strptime is not portable as it is not supported by MinGW and MSVC. We only use it for the GPS filters which use Qt anyways hence we can use QDateTime instead.
1 parent 1ade137 commit 1249ad1

9 files changed

Lines changed: 163 additions & 640 deletions

File tree

.github/workflows/static-code-analysis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ jobs:
1414
-i src/modules/glaxnimate/glaxnimate/
1515
-i src/modules/plus/ebur128/
1616
-i src/modules/xml/common.c
17-
-i src/win32/strptime.c
1817
--include=src/framework/mlt_log.h
1918
--include=src/framework/mlt_types.h
2019
--library=cppcheck.cfg

makefile

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ cppcheck:
2525
-i src/modules/glaxnimate/glaxnimate/ \
2626
-i src/modules/plus/ebur128/ \
2727
-i src/modules/xml/common.c \
28-
-i src/win32/strptime.c \
2928
--include=src/framework/mlt_log.h \
3029
--include=src/framework/mlt_types.h \
3130
--library=cppcheck.cfg \

src/framework/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ if(WIN32)
111111
target_link_options(mlt PRIVATE -Wl,--output-def,${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/libmlt-${MLT_VERSION_MAJOR}.def)
112112
install(FILES "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/libmlt-${MLT_VERSION_MAJOR}.def" DESTINATION ${CMAKE_INSTALL_LIBDIR})
113113
endif()
114-
target_sources(mlt PRIVATE ../win32/win32.c ../win32/strptime.c)
114+
target_sources(mlt PRIVATE ../win32/win32.c)
115115
target_link_libraries(mlt PRIVATE Iconv::Iconv)
116116
if(NOT WINDOWS_DEPLOY)
117117
target_compile_definitions(mlt PRIVATE NODEPLOY)

src/framework/mlt_types.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,6 @@ MLT_EXPORT FILE *win32_fopen(const char *filename_utf8, const char *mode_utf8);
346346
#include <sys/types.h>
347347
MLT_EXPORT int win32_stat(const char *filename_utf8, struct stat *buffer);
348348
#include <time.h>
349-
MLT_EXPORT char *strptime(const char *buf, const char *fmt, struct tm *tm);
350349
#define mlt_fopen win32_fopen
351350
#define mlt_stat win32_stat
352351
#define MLT_DIRLIST_DELIMITER ";"

src/modules/qt/filter_gpstext.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,7 @@ static void process_filter_properties(mlt_filter filter, mlt_frame frame)
425425
if (strlen(read_gps_processing_start_time) != 0
426426
&& strcmp(read_gps_processing_start_time, "yyyy-MM-dd hh:mm:ss"))
427427
gps_proc_t = datetimeXMLstring_to_mseconds(read_gps_processing_start_time,
428-
(char *) "%Y-%m-%d %H:%M:%S");
428+
(char *) "yyyy-MM-dd hh:mm:ss");
429429
if (gps_proc_t != pdata->gps_proc_start_t) {
430430
pdata->gps_proc_start_t = gps_proc_t;
431431
do_processing = 1;

src/modules/qt/gps_parser.cpp

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#define _GNU_SOURCE
2323
#endif
2424
#include "gps_parser.h"
25+
#include <QDateTime>
26+
#include <QTimeZone>
2527

2628
#define _x (const xmlChar *)
2729
#define _s (const char *)
@@ -70,36 +72,29 @@ double get_avg_gps_time_ms(gps_private_data gdata)
7072
*/
7173
int64_t datetimeXMLstring_to_mseconds(const char *text, char *format /* = NULL*/)
7274
{
73-
char def_format[] = "%Y-%m-%dT%H:%M:%S";
7475
int64_t ret = 0;
75-
int ms = 0;
76-
struct tm tm_time;
77-
//samples: 2020-07-11T09:03:23.000Z or 2021-02-27T12:10:00+00:00
78-
tm_time.tm_isdst = -1; //force dst detection
7976

80-
if (format == NULL)
81-
format = def_format;
77+
QDateTime datetime;
78+
if (format != NULL)
79+
datetime = QDateTime::fromString(QString(text), QString(format));
80+
else
81+
datetime = QDateTime::fromString(QString(text), Qt::ISODateWithMs);
8282

83-
if (strptime(text, format, &tm_time) == NULL) {
83+
if (!datetime.isValid()) {
8484
mlt_log_warning(
8585
NULL,
86-
"filter_gpsText.c datetimeXMLstring_to_seconds strptime failed on string: %.25s",
86+
"filter_gpsText.c datetimeXMLstring_to_seconds conversion failed on string: %.25s",
8787
text);
8888
return 0;
8989
}
9090

91-
ret = internal_timegm(&tm_time);
92-
93-
//check if we have miliseconds, 3 digits only
94-
const char *ms_part = strchr(text, '.');
95-
if (ms_part != NULL) {
96-
ms = strtol(ms_part + 1, NULL, 10);
97-
while (abs(ms) > 999)
98-
ms /= 10;
99-
}
100-
ret = ret * 1000 + ms;
91+
#if QT_VERSION > QT_VERSION_CHECK(6, 5, 0)
92+
datetime.setTimeZone(QTimeZone::UTC);
93+
#else
94+
datetime.setTimeZone(QTimeZone(0));
95+
#endif
10196

102-
// mlt_log_info(NULL, "datetimeXMLstring_to_mseconds: text:%s, ms:%d (/1000)", text, ret/1000);
97+
ret = datetime.toMSecsSinceEpoch();
10398
return ret;
10499
}
105100

src/tests/CMakeLists.txt

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,56 @@
11
set(CMAKE_AUTOMOC ON)
22

3-
foreach(QT_TEST_NAME animation audio events filter frame image multitrack playlist producer properties repository service tractor xml)
4-
add_executable(test_${QT_TEST_NAME} test_${QT_TEST_NAME}/test_${QT_TEST_NAME}.cpp)
5-
target_compile_options(test_${QT_TEST_NAME} PRIVATE ${MLT_COMPILE_OPTIONS})
6-
target_link_libraries(test_${QT_TEST_NAME} PRIVATE Qt${QT_MAJOR_VERSION}::Core Qt${QT_MAJOR_VERSION}::Test mlt++)
7-
add_test(NAME "QtTest:${QT_TEST_NAME}" COMMAND test_${QT_TEST_NAME})
3+
function(add_qt_test)
4+
set(options "")
5+
set(oneValueArgs TEST_NAME)
6+
set(multiValueArgs LINK_LIBRARIES INCLUDE_DIRS SOURCE_FILES)
7+
cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
8+
9+
set(_testname test_${arg_TEST_NAME})
10+
11+
add_executable(${_testname} ${_testname}/${_testname}.cpp ${arg_SOURCE_FILES})
12+
13+
target_compile_options(${_testname} PRIVATE ${MLT_COMPILE_OPTIONS})
14+
15+
target_link_libraries(${_testname} PRIVATE
16+
Qt${QT_MAJOR_VERSION}::Core
17+
Qt${QT_MAJOR_VERSION}::Test
18+
mlt++
19+
${ARG_LINK_LIBRARIES}
20+
)
21+
target_include_directories(${_testname} PRIVATE ${arg_INCLUDE_DIRS})
22+
add_test(NAME "QtTest:${arg_TEST_NAME}" COMMAND ${_testname})
23+
824
if(NOT WIN32)
9-
set_tests_properties("QtTest:${QT_TEST_NAME}" PROPERTIES ENVIRONMENT "LANG=en_US")
25+
set_tests_properties("QtTest:${arg_TEST_NAME}" PROPERTIES ENVIRONMENT "LANG=en_US")
1026
endif()
11-
endforeach()
27+
28+
endfunction()
29+
30+
add_qt_test(TEST_NAME animation)
31+
add_qt_test(TEST_NAME audio)
32+
add_qt_test(TEST_NAME events)
33+
add_qt_test(TEST_NAME filter)
34+
add_qt_test(TEST_NAME frame)
35+
add_qt_test(TEST_NAME image)
36+
add_qt_test(TEST_NAME multitrack)
37+
add_qt_test(TEST_NAME playlist)
38+
add_qt_test(TEST_NAME producer)
39+
add_qt_test(TEST_NAME properties)
40+
add_qt_test(TEST_NAME repository)
41+
add_qt_test(TEST_NAME service)
42+
add_qt_test(TEST_NAME tractor)
43+
add_qt_test(TEST_NAME xml)
44+
45+
if(MOD_QT6)
46+
add_qt_test(
47+
TEST_NAME mod_qt_gps
48+
SOURCE_FILES
49+
"${CMAKE_SOURCE_DIR}/src/modules/qt/gps_parser.h"
50+
"${CMAKE_SOURCE_DIR}/src/modules/qt/gps_parser.cpp"
51+
)
52+
endif()
53+
1254

1355
file(GLOB YML_FILES "${CMAKE_SOURCE_DIR}/src/modules/*/*.yml")
1456
foreach(YML_FILE ${YML_FILES})
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright (C) 2025 Julius Künzel <julius.kuenzel@kde.org>
3+
*
4+
* This library is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU Lesser General Public
6+
* License as published by the Free Software Foundation; either
7+
* version 2.1 of the License, or (at your option) any later version.
8+
*
9+
* This library is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public
15+
* License along with consumer library; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17+
*/
18+
19+
#include <QtTest>
20+
21+
#include <modules/qt/gps_parser.h>
22+
23+
extern int64_t datetimeXMLstring_to_mseconds(const char *text, char *format);
24+
25+
class TestModQt : public QObject
26+
{
27+
Q_OBJECT
28+
29+
public:
30+
TestModQt() { }
31+
32+
~TestModQt() { }
33+
34+
private Q_SLOTS:
35+
void DatetimeXMLstring_to_mseconds_ValidConversion()
36+
{
37+
int64_t gps_proc_t = 0;
38+
39+
// ISO 8601 extended date with timezone info Z
40+
const char *text = "2020-07-11T09:03:23.000Z";
41+
gps_proc_t = datetimeXMLstring_to_mseconds(text);
42+
QCOMPARE(gps_proc_t, 1594458203000);
43+
44+
// ISO 8601 extended date with timezone offset
45+
text = "2021-02-27T12:10:00+00:00";
46+
gps_proc_t = datetimeXMLstring_to_mseconds(text);
47+
QCOMPARE(gps_proc_t, 1614427800000);
48+
49+
// ISO 8601 extended date with ms and timezone offset
50+
text = "2020-07-11T09:03:23.000+00:00";
51+
gps_proc_t = datetimeXMLstring_to_mseconds(text);
52+
QCOMPARE(gps_proc_t, 1594458203000);
53+
54+
// ISO 8601 extended date without timezone info
55+
text = "2020-07-11T09:03:23.000";
56+
gps_proc_t = datetimeXMLstring_to_mseconds(text);
57+
QCOMPARE(gps_proc_t, 1594458203000);
58+
}
59+
60+
void DatetimeXMLstring_to_mseconds_FormatString()
61+
{
62+
int64_t gps_proc_t = 0;
63+
64+
// Unknown input, with format string as hint
65+
const char *text = "12:10:00 27-02-2021";
66+
gps_proc_t = datetimeXMLstring_to_mseconds(text, (char *) "HH:mm:ss dd-MM-yyyy");
67+
QCOMPARE(gps_proc_t, 1614427800000);
68+
69+
// Unknown input, without format string
70+
text = "12:10:00 27-02-2021";
71+
gps_proc_t = datetimeXMLstring_to_mseconds(text);
72+
QCOMPARE(gps_proc_t, 0);
73+
74+
// Valid input, but mismatching format string
75+
text = "2021-02-27T12:10:00+00:00";
76+
gps_proc_t = datetimeXMLstring_to_mseconds(text, (char *) "HH:mm:ss dd-MM-yyyy");
77+
QCOMPARE(gps_proc_t, 0);
78+
}
79+
80+
void DatetimeXMLstring_to_mseconds_InvalidInput()
81+
{
82+
int64_t gps_proc_t = 0;
83+
84+
// Invalid input, without format string
85+
const char *text = "invalid-date-format";
86+
gps_proc_t = datetimeXMLstring_to_mseconds(text);
87+
QCOMPARE(gps_proc_t, 0);
88+
89+
// Invalid input, with format string
90+
gps_proc_t = datetimeXMLstring_to_mseconds(text, (char *) "%Y-%m-%dT%H:%M:%S");
91+
QCOMPARE(gps_proc_t, 0);
92+
}
93+
};
94+
95+
QTEST_APPLESS_MAIN(TestModQt)
96+
97+
#include "test_mod_qt_gps.moc"

0 commit comments

Comments
 (0)