Skip to content

Commit

Permalink
Fix #1303 - normalize datevec results for consistent date handling (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Nelson-numerical-software authored Dec 12, 2024
1 parent 2ea23b8 commit c648d20
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 6 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- i18n: migration PO files to JSON.
- `dlmwrite`: rework the function to be more fast and robust.
- `strjust`: rework the function to be more fast and robust.
- `datenum`: support '' as format for compatibility.

### Fixed

- [#1276](http://github.com/nelson-lang/nelson/issues/1276) micromamba macos build.
- [#1303](http://github.com/nelson-lang/nelson/issues/1303) `datevec` result must be normalized.
- [#1297](http://github.com/nelson-lang/nelson/issues/1297) some features have no help files.
- [#1276](http://github.com/nelson-lang/nelson/issues/1276) micromamba macos build.

## 1.9.0 (2024-10-26)

Expand Down
10 changes: 8 additions & 2 deletions modules/time/builtin/cpp/datenumBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ datenumTwoRhsBuiltin(int nLhs, const ArrayOfVector& argIn)
std::wstring formatIn;
if (argIn[1].isRowVectorCharacterArray() || argIn[1].isScalarStringArray()) {
formatIn = argIn[1].getContentAsWideString();
withFormatIn = true;
withFormatIn = !formatIn.empty();
} else if (argIn[1].isNumeric() && argIn[1].isScalar()) {
pivotYear = (int)argIn[1].getContentAsDoubleScalar();
withPivotYear = true;
Expand Down Expand Up @@ -285,14 +285,20 @@ datenumThreeRhsBuiltin(int nLhs, const ArrayOfVector& argIn)
wstringVector dateString = argIn[0].getContentAsWideStringVector(false);
if (argIn[1].isScalarStringArray() || argIn[1].isRowVectorCharacterArray()) {
std::wstring formatIn = argIn[1].getContentAsWideString();
bool withFormatIn = !formatIn.empty();
int pivotYear = (int)argIn[2].getContentAsDoubleScalar();
withPivotYear = true;
Dimensions dimsRes(1, dateString.size());
double* pRes = (double*)ArrayOf::allocateArrayOf(NLS_DOUBLE, dimsRes.getElementCount());
ArrayOf res = ArrayOf(NLS_DOUBLE, dimsRes, pRes);
for (size_t k = 0; k < dimsRes.getElementCount(); ++k) {
bool bParsed = false;
pRes[k] = DateNumber(dateString[k], withPivotYear, pivotYear, bParsed);
if (withFormatIn) {
pRes[k]
= DateNumber(dateString[k], formatIn, withPivotYear, pivotYear, bParsed);
} else {
pRes[k] = DateNumber(dateString[k], withPivotYear, pivotYear, bParsed);
}
if (!bParsed) {
Error(_W("Failed to convert text to date number."));
}
Expand Down
9 changes: 8 additions & 1 deletion modules/time/help/en_US/xml/datenum.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@

<param_input_item>
<param_name>format</param_name>
<param_description>a string: date format.</param_description>
<param_description
>a string specifying the date format, or leave it empty ('') for automatic format detection.</param_description>
</param_input_item>

<param_input_item>
Expand Down Expand Up @@ -214,6 +215,12 @@ d = datenum(["04–Aug-1973 12:01:18"; "04–Aug-1974 11:01:18"])
<history_version>1.8.0</history_version>
<history_description>date string parsing extended.</history_description>
</history_item>
<history_item>
<history_version>1.10.0</history_version>
<history_description
>added: format '' means try to detect.</history_description>
</history_item>

</history>

<authors>
Expand Down
3 changes: 1 addition & 2 deletions modules/time/src/cpp/DateNumber.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,10 +302,9 @@ getDateFormatInfoList(bool withPivot)
} },
{ std::wregex(L"(\\d{1,2})/(\\d{1,2})/(\\d{2})"), L"dd/mm/yy",
[withPivot, century](const std::wsmatch& m, int pivotYear) -> double {
int year = withPivot ? std::stoi(m[3]) + pivotYear : century + std::stoi(m[3]);
int first = std::stoi(m[1]);
int second = std::stoi(m[2]);
int year = withPivot ? parseYearWithPivot(std::stoi(m[3]), pivotYear)
: century + std::stoi(m[3]);
if (isValidDate(year, second, first)) {
return DateNumber(year, second, first, 0, 0, 0);
} else if (isValidDate(year, first, second)) {
Expand Down
47 changes: 47 additions & 0 deletions modules/time/src/cpp/DateVector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ namespace Nelson {
static double common_year[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 };
static double leap_year[] = { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 };
//=============================================================================
static void
normalizeDate(double& Y, double& M, double& D, double& H, double& MN, double& S, double& MS);
//=============================================================================
void
DateVector(double dateSerial, double& Y, double& M, double& D, double& H, double& MN, double& S,
double& MS, bool rf)
Expand Down Expand Up @@ -81,6 +84,50 @@ DateVector(double dateSerial, double& Y, double& M, double& D, double& H, double
dateSerial = dateSerial - cdm[mon];
D = dateSerial;
}
normalizeDate(Y, M, D, H, MN, S, MS);
}
//=============================================================================
void
normalizeDate(double& Y, double& M, double& D, double& H, double& MN, double& S, double& MS)
{
if (MS > 500) {
S += 1;
MS = 0;
}
if (S > 59) {
MN += 1;
S = 0;
}
if (MN > 59) {
H += 1;
MN = 0;
}
if (H > 23) {
D += 1;
H = 0;
}

// Define days in each month for common and leap years
bool isLeapYear = (static_cast<int>(Y) % 4 == 0 && static_cast<int>(Y) % 100 != 0)
|| (static_cast<int>(Y) % 400 == 0);
int daysInMonth[] = { 0, 31, isLeapYear ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

// Adjust day overflow for the current month
while (D > daysInMonth[static_cast<int>(M)]) {
D -= daysInMonth[static_cast<int>(M)];
M += 1;
}

// Adjust month overflow to increment the year
while (M > 12) {
M -= 12;
Y += 1;

// Recompute leap year status for the new year
isLeapYear = (static_cast<int>(Y) % 4 == 0 && static_cast<int>(Y) % 100 != 0)
|| (static_cast<int>(Y) % 400 == 0);
daysInMonth[2] = isLeapYear ? 29 : 28;
}
}
//=============================================================================
} // namespace Nelson
Expand Down
8 changes: 8 additions & 0 deletions modules/time/tests/test_datenum_string.m
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@
REF = 730990;
assert_isequal(R, REF);
%=============================================================================
R = datenum('19-May-2001', '');
REF = 730990;
assert_isequal(R, REF);
%=============================================================================
R = datenum('19-May-2001');
REF = 730990;
assert_isequal(R, REF);
%=============================================================================
% REF date 2009/01/31 13:56:23
%=============================================================================
R = datevec(datenum('31-Jan-2009 13:56:23', 'dd-mmm-yyyy HH:MM:SS'));
Expand Down
4 changes: 4 additions & 0 deletions modules/time/tests/test_datevec.m
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,7 @@
assert_isequal(Y, Y_REF);
assert_isequal(M, M_REF);
%=============================================================================
R = datevec(datenum('2024-12-11 01:03:00'))
REF = [2024, 12, 11, 1, 3, 0];
assert_isequal(R, REF);
%=============================================================================

0 comments on commit c648d20

Please sign in to comment.