From 861ec8a339c9b0aae227e82a93df14a5eaaf59d4 Mon Sep 17 00:00:00 2001 From: Allan CORNET Date: Sat, 14 Dec 2024 08:42:25 +0100 Subject: [PATCH] feat(time): add hour, minute, second time extraction functions (#1305) --- CHANGELOG.md | 1 + modules/time/builtin/cpp/datevecBuiltin.cpp | 10 ++- modules/time/functions/hour.m | 57 +++++++++++++++ modules/time/functions/minute.m | 55 +++++++++++++++ modules/time/functions/second.m | 53 ++++++++++++++ modules/time/help/en_US/xml/hour.xml | 76 ++++++++++++++++++++ modules/time/help/en_US/xml/minute.xml | 77 +++++++++++++++++++++ modules/time/help/en_US/xml/second.xml | 77 +++++++++++++++++++++ modules/time/src/cpp/Calendar.cpp | 2 +- modules/time/src/include/DateVector.hpp | 2 +- modules/time/tests/test_hour.m | 43 ++++++++++++ modules/time/tests/test_minute.m | 43 ++++++++++++ modules/time/tests/test_second.m | 43 ++++++++++++ 13 files changed, 534 insertions(+), 5 deletions(-) create mode 100644 modules/time/functions/hour.m create mode 100644 modules/time/functions/minute.m create mode 100644 modules/time/functions/second.m create mode 100644 modules/time/help/en_US/xml/hour.xml create mode 100644 modules/time/help/en_US/xml/minute.xml create mode 100644 modules/time/help/en_US/xml/second.xml create mode 100644 modules/time/tests/test_hour.m create mode 100644 modules/time/tests/test_minute.m create mode 100644 modules/time/tests/test_second.m diff --git a/CHANGELOG.md b/CHANGELOG.md index 62788fe532..a775ceb858 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `join`: Combine strings. - [#1292](http://github.com/nelson-lang/nelson/issues/1292) Large Table Display. - [#1290](http://github.com/nelson-lang/nelson/issues/1290) `VariableTypes` property for table: Specify the data types of table in Nelson. +- `hour`, `minute`, `second` component of input date and time. ### Changed diff --git a/modules/time/builtin/cpp/datevecBuiltin.cpp b/modules/time/builtin/cpp/datevecBuiltin.cpp index 7a7b7cfc7a..dbfe60c1e8 100644 --- a/modules/time/builtin/cpp/datevecBuiltin.cpp +++ b/modules/time/builtin/cpp/datevecBuiltin.cpp @@ -42,13 +42,17 @@ Nelson::TimeGateway::datevecBuiltin(int nLhs, const ArrayOfVector& argIn) for (ompIndexType k = 0; k < (ompIndexType)len; k++) { double DT, Y, M, D, H, MN, S, MS; DT = ptd[k]; - DateVector(DT, Y, M, D, H, MN, S, MS); + DateVector(DT, Y, M, D, H, MN, S, MS, false); res[k] = Y; res[k + len] = M; res[k + len * 2] = D; res[k + len * 3] = H; res[k + len * 4] = MN; - res[k + len * 5] = S; + if (MS > 1) { + res[k + len * 5] = S + (MS / 1000); + } else { + res[k + len * 5] = S; + } } Dimensions dim(len, 6); retval << ArrayOf(NLS_DOUBLE, dim, res); @@ -87,7 +91,7 @@ Nelson::TimeGateway::datevecBuiltin(int nLhs, const ArrayOfVector& argIn) for (ompIndexType k = 0; k < (ompIndexType)len; k++) { double DT = ptd[k]; double V1, V2, V3, V4, V5, V6, V7; - DateVector(DT, V1, V2, V3, V4, V5, V6, V7); + DateVector(DT, V1, V2, V3, V4, V5, V6, V7, false); Y[k] = V1; M[k] = V2; if (nLhs > 2) { diff --git a/modules/time/functions/hour.m b/modules/time/functions/hour.m new file mode 100644 index 0000000000..d211458fe1 --- /dev/null +++ b/modules/time/functions/hour.m @@ -0,0 +1,57 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +function varargout = hour(varargin) + % Extracts the hour component from a given date/time input. + % + % Usage: + % h = hour(d) + % h = hour(d, formatDate) + % + % Input: + % d - Date/time input (can be a string, char, or numeric). + % formatDate - format date (can be a string, char). + % + % Output: + % h - Hour component of the input date/time. + + narginchk(1, 2); + nargoutchk(0, 1); % Ensure at most one output argument. + + d = varargin{1}; + d = convertStringsToChars(d); % Convert strings to char for consistency. + + formatDate = ''; + if nargin == 2 + formatDate = convertStringsToChars(varargin{2}); + end + + % Validate input type + if ~(ischar(d) || isstring(d) || isnumeric(d) || iscell(d)) + error('Invalid date/time format. Input must be a string, char, or numeric.'); + end + % Convert char inputs to numeric using datenum + if ~isnumeric(d) + d = datenum(d, formatDate); + end + + % Convert numeric or parsed inputs to date vector + c = datevec(d(:)); + + % Extract hour component + h = c(:, 4); + + if ~ischar(d) + h = reshape(h, size(d)); % Reshape to match the input size if numeric. + end + + varargout{1} = h; % Assign output +end +%============================================================================= + diff --git a/modules/time/functions/minute.m b/modules/time/functions/minute.m new file mode 100644 index 0000000000..2876fcdc4d --- /dev/null +++ b/modules/time/functions/minute.m @@ -0,0 +1,55 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +function varargout = minute(varargin) + % Extracts the minute component from a given date/time input. + % + % Usage: + % m = minute(d) + % m = minute(d, formatDate) + % + % Input: + % d - Date/time input (can be a string, char, or numeric). + % formatDate - format date (can be a string, char). + % + % Output: + % m - Minute component of the input date/time. + + narginchk(1, 2); + nargoutchk(0, 1); + + inputDate = varargin{1}; + inputDate = convertStringsToChars(inputDate); + + dateFormat = ''; + if nargin == 2 + dateFormat = convertStringsToChars(varargin{2}); + end + + if ischar(inputDate) + inputDate = datenum(inputDate, dateFormat); + inputSize = size(inputDate); + elseif iscell(inputDate) + inputSize = size(inputDate); + inputDate = datenum(inputDate, dateFormat); + elseif isnumeric(inputDate) + inputSize = size(inputDate); + else + error(_('Invalid date/time format. Input must be a string, char, or numeric.')); + end + + dateVector = datevec(inputDate(:)); + minuteValues = dateVector(:, 5); + + requiresMinuteIncrement = (dateVector(:, 6) > 59.5); + minuteValues(requiresMinuteIncrement) = minuteValues(requiresMinuteIncrement) + 1; + + varargout{1} = reshape(minuteValues, inputSize); +end +%============================================================================= diff --git a/modules/time/functions/second.m b/modules/time/functions/second.m new file mode 100644 index 0000000000..6b3d0027c5 --- /dev/null +++ b/modules/time/functions/second.m @@ -0,0 +1,53 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +function varargout = second(varargin) + % Extracts the second component from a given date/time input. + % + % Usage: + % s = second(d) + % s = second(d, formatDate) + % + % Input: + % d - Date/time input (can be a string, char, or numeric). + % formatDate - format date (can be a string, char). + % + % Output: + % s - Second component of the input date/time. + + narginchk(1, 2); + nargoutchk(0, 1); + + inputDate = varargin{1}; + inputDate = convertStringsToChars(inputDate); + + dateFormat = ''; + if nargin == 2 + dateFormat = convertStringsToChars(varargin{2}); + end + + if ischar(inputDate) + inputDate = datenum(inputDate, dateFormat); + inputSize = size(inputDate); + elseif iscell(inputDate) + inputSize = size(inputDate); + inputDate = datenum(inputDate, dateFormat); + elseif isnumeric(inputDate) + inputSize = size(inputDate); + else + error(_('Invalid date/time format. Input must be a string, char, or numeric.')); + end + + dateVector = datevec(inputDate(:)); + secondValues = dateVector(:, 6); + + secondValues = round(1000 .* secondValues) ./ 1000; + varargout{1} = reshape(secondValues, inputSize); +end +%============================================================================= diff --git a/modules/time/help/en_US/xml/hour.xml b/modules/time/help/en_US/xml/hour.xml new file mode 100644 index 0000000000..f983f9a862 --- /dev/null +++ b/modules/time/help/en_US/xml/hour.xml @@ -0,0 +1,76 @@ + + + SAME AS NELSON SOFTWARE + + en_US + hour + Hours part of the input date and time. + + + h = hour(t) + h = hour(t, formatIn) + + + + + t + serial date number or text inputs + + + formatIn + valid date format + + + + + + + h + a double: integer value. + + + + +

h = hour(t) extracts the hour component from each date and time specified in t.

+

The output h is a double array containing integer values ranging from 0 to 23.

+
+ + + + + + nelson + + + + + + + + + + minute + + + second + + + + + + + 1.10.0 + initial version + + + + + Allan CORNET + +
diff --git a/modules/time/help/en_US/xml/minute.xml b/modules/time/help/en_US/xml/minute.xml new file mode 100644 index 0000000000..920c28681d --- /dev/null +++ b/modules/time/help/en_US/xml/minute.xml @@ -0,0 +1,77 @@ + + + SAME AS NELSON SOFTWARE + + en_US + minute + Minutes part of the input date and time. + + + m = minute(t) + m = minute(t, formatIn) + + + + + t + serial date number or text inputs + + + formatIn + valid date format + + + + + + + m + a double: integer value. + + + + +

m = minute(t) extracts the minute component from each date and time specified in t.

+

The output m is a double array containing integer values ranging from 0 to 59.

+
+ + + + + + nelson + + + + + + + + + + hour + + + second + + + + + + + 1.10.0 + initial version + + + + + Allan CORNET + +
diff --git a/modules/time/help/en_US/xml/second.xml b/modules/time/help/en_US/xml/second.xml new file mode 100644 index 0000000000..e0bfb33c8c --- /dev/null +++ b/modules/time/help/en_US/xml/second.xml @@ -0,0 +1,77 @@ + + + SAME AS NELSON SOFTWARE + + en_US + second + Seconds part of the input date and time. + + + s = second(t) + s = second(t, formatIn) + + + + + t + serial date number or text inputs + + + formatIn + valid date format + + + + + + + s + a double: integer value. + + + + +

s = second(t) extracts the second component from each date and time specified in t.

+

The output s is a double array containing integer values ranging from 0 to 59.

+
+ + + + + + nelson + + + + + + + + + + minute + + + hour + + + + + + + 1.10.0 + initial version + + + + + Allan CORNET + +
diff --git a/modules/time/src/cpp/Calendar.cpp b/modules/time/src/cpp/Calendar.cpp index 0d4ae8d83e..d20d2cc24a 100644 --- a/modules/time/src/cpp/Calendar.cpp +++ b/modules/time/src/cpp/Calendar.cpp @@ -41,7 +41,7 @@ Calendar::Calendar(double dateserial) double mm = 0.; double s = 0.; double ms = 0.; - DateVector(dateserial, y, m, d, h, mm, s, ms); + DateVector(dateserial, y, m, d, h, mm, s, ms, false); this->m = static_cast(m); this->y = static_cast(y); } diff --git a/modules/time/src/include/DateVector.hpp b/modules/time/src/include/DateVector.hpp index 02b956420b..d99868ca46 100644 --- a/modules/time/src/include/DateVector.hpp +++ b/modules/time/src/include/DateVector.hpp @@ -14,6 +14,6 @@ namespace Nelson { NLSTIME_IMPEXP void DateVector(double dateSerial, double& Y, double& M, double& D, double& H, double& MN, double& S, - double& MS, bool rf = false); + double& MS, bool rf); } //============================================================================= diff --git a/modules/time/tests/test_hour.m b/modules/time/tests/test_hour.m new file mode 100644 index 0000000000..71edf0d30e --- /dev/null +++ b/modules/time/tests/test_hour.m @@ -0,0 +1,43 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +assert_isequal(hour(0), 0); +assert_isequal(hour(60000000000000), 0); +assert_isequal(hour(60000000000000.5), 12); +%============================================================================= +M = [datenum('2024-12-11 01:00:00'), datenum('2024-12-11 14:00:00')]; +R = hour(M); +REF = [1 14]; +assert_isequal(R, REF); +%============================================================================= +M = '2024-12-11 22:15:00'; +R = hour(M); +REF = 22; +assert_isequal(R, REF); +%============================================================================= +M = ["2024-12-11 01:00:00", "2024-12-11 14:00:00"]; +R = hour(M); +REF = [1 14]; +assert_isequal(R, REF); +%============================================================================= +M = datenum({'2024-12-11 03:00:00', '2024-12-11 23:45:00'}); +R = hour(M); +REF = [3, 23]; +assert_isequal(R, REF); +%============================================================================= +M = "2024-12-11 00:00:00"; +R = hour(M); +REF = 0; +assert_isequal(R, REF); +%============================================================================= +M = [6000000000.1, 60000000000.9]; +R = hour(M); +REF = [2, 21]; +assert_isequal(R, REF); +%============================================================================= diff --git a/modules/time/tests/test_minute.m b/modules/time/tests/test_minute.m new file mode 100644 index 0000000000..7e2670d138 --- /dev/null +++ b/modules/time/tests/test_minute.m @@ -0,0 +1,43 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +assert_isequal(minute(0), 0); +assert_isequal(minute(60000000), 0); +assert_isequal(minute(60000000.52), 28); +%============================================================================= +M = [datenum('2024-12-11 01:02:01'), datenum('2024-12-11 14:03:02')]; +R = minute(M); +REF = [2 3]; +assert_isequal(R, REF); +%============================================================================= +M = '2024-12-11 22:15:00'; +R = minute(M); +REF = 15; +assert_isequal(R, REF); +%============================================================================= +M = ["2024-12-11 01:03:00", "2024-12-11 14:02:00"]; +R = minute(M); +REF = [3 2]; +assert_isequal(R, REF); +%============================================================================= +M = datenum({'2024-12-11 03:23:00', '2024-12-11 23:45:00'}); +R = minute(M); +REF = [23, 45]; +assert_isequal(R, REF); +%============================================================================= +M = "2024-12-11 00:00:00"; +R = minute(M); +REF = 0; +assert_isequal(R, REF); +%============================================================================= +M = [6000000000.1, 60000000000.9]; +R = minute(M); +REF = [24, 36]; +assert_isequal(R, REF); +%============================================================================= diff --git a/modules/time/tests/test_second.m b/modules/time/tests/test_second.m new file mode 100644 index 0000000000..d29e7d1b6d --- /dev/null +++ b/modules/time/tests/test_second.m @@ -0,0 +1,43 @@ +%============================================================================= +% Copyright (c) 2016-present Allan CORNET (Nelson) +%============================================================================= +% This file is part of the Nelson. +%============================================================================= +% LICENCE_BLOCK_BEGIN +% SPDX-License-Identifier: LGPL-3.0-or-later +% LICENCE_BLOCK_END +%============================================================================= +assert_isequal(second(0), 0); +assert_isequal(second(60000000.12), 48); +assert_isequal(second(60000000.52), 48); +%============================================================================= +M = [datenum('2024-12-11 01:02:01'), datenum('2024-12-11 14:03:02')]; +R = second(M); +REF = [1 2]; +assert_isequal(R, REF); +%============================================================================= +M = '2024-12-11 22:15:59'; +R = second(M); +REF = 59; +assert_isequal(R, REF); +%============================================================================= +M = ["2024-12-11 01:03:33", "2024-12-11 14:02:19"]; +R = second(M); +REF = [33 19]; +assert_isequal(R, REF); +%============================================================================= +M = datenum({'2024-12-11 03:23:11', '2024-12-11 23:45:13'}); +R = second(M); +REF = [11, 13]; +assert_isequal(R, REF); +%============================================================================= +M = "2024-12-11 00:00:02"; +R = second(M); +REF = 2; +assert_isequal(R, REF); +%============================================================================= +M = [600000.12, 600000.923]; +R = second(M); +REF = [48, 7.2]; +assert_isapprox(R, REF, 1e-3); +%=============================================================================