From c5ddc7788d66b8ef0203ad14989dc52fbc183712 Mon Sep 17 00:00:00 2001 From: Allan CORNET Date: Sun, 1 Sep 2024 21:30:37 +0200 Subject: [PATCH] Fix #36 - datenum compatibility extended. (in progress) --- CHANGELOG.md | 1 + modules/time/CMakeLists.txt | 2 +- modules/time/builtin/cpp/datenumBuiltin.cpp | 431 ++++++----- modules/time/builtin/cpp/datestrBuiltin.cpp | 14 +- modules/time/functions/@string/datestr.m | 12 +- modules/time/src/cpp/DateNumber.cpp | 720 +++++++++++++----- .../time/src/cpp/DateToFormattedString.cpp | 21 +- modules/time/src/include/DateNumber.hpp | 11 +- modules/time/tests/test_datenum.m | 69 -- modules/time/tests/test_datenum_string_1.m | 160 ++++ modules/time/tests/test_datenum_string_2.m | 211 +++++ modules/time/tests/test_datestr.m | 12 - modules/time/tests/test_datestr_string.m | 55 ++ 13 files changed, 1255 insertions(+), 464 deletions(-) create mode 100644 modules/time/tests/test_datenum_string_1.m create mode 100644 modules/time/tests/test_datenum_string_2.m create mode 100644 modules/time/tests/test_datestr_string.m diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d17306680..ce4191eece 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `Resize` - Resize figure property. +- [#36](http://github.com/nelson-lang/nelson/issues/36) `datenum` format compatibility extended. - [#37](http://github.com/nelson-lang/nelson/issues/37) `datestr` Convert date and time to string format. ### Changed diff --git a/modules/time/CMakeLists.txt b/modules/time/CMakeLists.txt index c08e17ed50..1f3be99487 100644 --- a/modules/time/CMakeLists.txt +++ b/modules/time/CMakeLists.txt @@ -41,7 +41,7 @@ target_include_directories( target_link_libraries( ${module_library_name} nlsCommons - nlsError + nlsError_manager nlsElementary_mathematics nlsI18n nlsInterpreter diff --git a/modules/time/builtin/cpp/datenumBuiltin.cpp b/modules/time/builtin/cpp/datenumBuiltin.cpp index aaa3706726..dd61598ca6 100644 --- a/modules/time/builtin/cpp/datenumBuiltin.cpp +++ b/modules/time/builtin/cpp/datenumBuiltin.cpp @@ -19,222 +19,181 @@ //============================================================================= using namespace Nelson; //============================================================================= -static ArrayOfVector -datanumBuiltinNoRhs(int nLhs); +static Dimensions +findCommonDimensions(const Dimensions& dims1, const Dimensions& dims2, bool& isValid); +static Dimensions +findCommonDimensions( + const Dimensions& dims1, const Dimensions& dims2, const Dimensions& dims3, bool& isValid); +static Dimensions +findCommonDimensions(const Dimensions& dims1, const Dimensions& dims2, const Dimensions& dims3, + const Dimensions& dims4, const Dimensions& dims5, const Dimensions& dims6, bool& isValid); //============================================================================= static ArrayOfVector -datanumBuiltinOneRhs(int nLhs, const ArrayOf& param1); +datenumOneRhsBuiltin(int nLhs, const ArrayOfVector& argIn); //============================================================================= static ArrayOfVector -datanumBuiltinTwoRhs(int nLhs, const ArrayOf& param1, const ArrayOf& param2); +datenumOneNumericRhsBuiltin(int nLhs, const ArrayOfVector& argIn); //============================================================================= static ArrayOfVector -datanumBuiltinThreeRhs( - int nLhs, const ArrayOf& param1, const ArrayOf& param2, const ArrayOf& param3); +datenumOneStringRhsBuiltin(int nLhs, const ArrayOfVector& argIn); //============================================================================= static ArrayOfVector -datanumBuiltinSixRhs(int nLhs, const ArrayOf& param1, const ArrayOf& param2, const ArrayOf& param3, - const ArrayOf& param4, const ArrayOf& param5, const ArrayOf& param6); +datenumTwoRhsBuiltin(int nLhs, const ArrayOfVector& argIn); //============================================================================= -static Dimensions -findCommonDimensions(const Dimensions& dims1, const Dimensions& dims2, bool& isValid); -//============================================================================= -static Dimensions -findCommonDimensions( - const Dimensions& dims1, const Dimensions& dims2, const Dimensions& dims3, bool& isValid); +static ArrayOfVector +datenumThreeRhsBuiltin(int nLhs, const ArrayOfVector& argIn); //============================================================================= -static Dimensions -findCommonDimensions(const Dimensions& dims1, const Dimensions& dims2, const Dimensions& dims3, - const Dimensions& dims4, const Dimensions& dims5, const Dimensions& dims6, bool& isValid); +static ArrayOfVector +datenumSixRhsBuiltin(int nLhs, const ArrayOfVector& argIn); //============================================================================= ArrayOfVector Nelson::TimeGateway::datenumBuiltin(int nLhs, const ArrayOfVector& argIn) { - // DateNumber = datenum() - // DateNumber = datenum(t) - // DateNumber = datenum(DateString) - // DateNumber = datenum(DateVector) - // DateNumber = datenum(DateString, formatIn) - // DateNumber = datenum(Y, M, D) - // DateNumber = datenum(Y, M, D, H, MN, S) - ArrayOfVector retval; + // DateNumber = datenum(t) (by overload later) + + nargincheck(argIn, 1, 6); nargoutcheck(nLhs, 0, 1); switch (argIn.size()) { - case 0: { - retval = datanumBuiltinNoRhs(nLhs); - } break; case 1: { - retval = datanumBuiltinOneRhs(nLhs, argIn[0]); + // DateNumber = datenum(DateString) + // DateNumber = datenum(DateVector) + return datenumOneRhsBuiltin(nLhs, argIn); } break; case 2: { - retval = datanumBuiltinTwoRhs(nLhs, argIn[0], argIn[1]); + // DateNumber = datenum(DateString, formatIn) + // DateNumber = datenum(DateString, pivotYear) + return datenumTwoRhsBuiltin(nLhs, argIn); } break; case 3: { - retval = datanumBuiltinThreeRhs(nLhs, argIn[0], argIn[1], argIn[2]); + // DateNumber = datenum(DateString, formatIn, pivotYear) + // DateNumber = datenum(Y, M, D) + return datenumThreeRhsBuiltin(nLhs, argIn); } break; case 6: { - retval = datanumBuiltinSixRhs( - nLhs, argIn[0], argIn[1], argIn[2], argIn[3], argIn[4], argIn[5]); + // DateNumber = datenum(Y, M, D, H, MN, S) + return datenumSixRhsBuiltin(nLhs, argIn); } break; default: { Error(ERROR_WRONG_NUMBERS_OUTPUT_ARGS); } break; } - return retval; + return {}; } //============================================================================= ArrayOfVector -datanumBuiltinNoRhs(int nLhs) +datenumOneRhsBuiltin(int nLhs, const ArrayOfVector& argIn) { + int pivotYear = 50; ArrayOfVector retval; - retval << ArrayOf::doubleConstructor(Now()); - return retval; + if (argIn[0].isNumeric()) { + return datenumOneNumericRhsBuiltin(nLhs, argIn); + } + return datenumOneStringRhsBuiltin(nLhs, argIn); } //============================================================================= -static ArrayOfVector -datanumBuiltinOneRhs(int nLhs, const ArrayOf& param1) +ArrayOfVector +datenumOneNumericRhsBuiltin(int nLhs, const ArrayOfVector& argIn) { ArrayOfVector retval; - if (param1.isNumeric()) { - if (param1.getElementCount() < 2) { - retval << param1; - } else if (param1.getElementCount() == 3 && param1.isRowVector()) { - ArrayOf asDoubleArrayOf(param1); - asDoubleArrayOf.promoteType(NLS_DOUBLE); - double* v = (double*)asDoubleArrayOf.getDataPointer(); - double year = v[0]; - double month = v[1]; - double day = v[2]; - double hour = 0.; - double min = 0.; - double sec = 0.; - retval << ArrayOf::doubleConstructor(DateNumber(year, month, day, hour, min, sec)); - } else if (param1.getElementCount() == 6 && param1.isRowVector()) { - ArrayOf asDoubleArrayOf(param1); - asDoubleArrayOf.promoteType(NLS_DOUBLE); - double* v = (double*)asDoubleArrayOf.getDataPointer(); - double year = v[0]; - double month = v[1]; - double day = v[2]; - double hour = v[3]; - double min = v[4]; - double sec = v[5]; - retval << ArrayOf::doubleConstructor(DateNumber(year, month, day, hour, min, sec)); - } else if (param1.is2D() && param1.getColumns() == 3) { - ArrayOf asDoubleArrayOf(param1); - asDoubleArrayOf.promoteType(NLS_DOUBLE); - double* v = (double*)asDoubleArrayOf.getDataPointer(); - indexType lenghResultVector = param1.getElementCount() / 3; - Dimensions dimsRes(1, lenghResultVector); - double* pRes = (double*)ArrayOf::allocateArrayOf(NLS_DOUBLE, lenghResultVector); - ArrayOf res = ArrayOf(NLS_DOUBLE, dimsRes, pRes); + if (argIn[0].getElementCount() < 2) { + retval << argIn[0]; + } else if (argIn[0].getElementCount() == 3 && argIn[0].isRowVector()) { + ArrayOf asDoubleArrayOf(argIn[0]); + asDoubleArrayOf.promoteType(NLS_DOUBLE); + double* v = (double*)asDoubleArrayOf.getDataPointer(); + double year = v[0]; + double month = v[1]; + double day = v[2]; + double hour = 0.; + double min = 0.; + double sec = 0.; + retval << ArrayOf::doubleConstructor(DateNumber(year, month, day, hour, min, sec)); + } else if (argIn[0].getElementCount() == 6 && argIn[0].isRowVector()) { + ArrayOf asDoubleArrayOf(argIn[0]); + asDoubleArrayOf.promoteType(NLS_DOUBLE); + double* v = (double*)asDoubleArrayOf.getDataPointer(); + double year = v[0]; + double month = v[1]; + double day = v[2]; + double hour = v[3]; + double min = v[4]; + double sec = v[5]; + retval << ArrayOf::doubleConstructor(DateNumber(year, month, day, hour, min, sec)); + } else if (argIn[0].is2D() && argIn[0].getColumns() == 3) { + ArrayOf asDoubleArrayOf(argIn[0]); + asDoubleArrayOf.promoteType(NLS_DOUBLE); + double* v = (double*)asDoubleArrayOf.getDataPointer(); + indexType lenghResultVector = argIn[0].getElementCount() / 3; + Dimensions dimsRes(1, lenghResultVector); + double* pRes = (double*)ArrayOf::allocateArrayOf(NLS_DOUBLE, lenghResultVector); + ArrayOf res = ArrayOf(NLS_DOUBLE, dimsRes, pRes); #if WITH_OPENMP #pragma omp parallel for #endif - for (ompIndexType k = 0; k < (ompIndexType)lenghResultVector; ++k) { - double year = v[lenghResultVector * k]; - double month = v[(lenghResultVector * k) + 1]; - double day = v[(lenghResultVector * k) + 2]; - double hour = 0; - double min = 0; - double sec = 0; - pRes[k] = DateNumber(year, month, day, hour, min, sec); - } - retval << res; + for (ompIndexType k = 0; k < (ompIndexType)lenghResultVector; ++k) { + double year = v[lenghResultVector * k]; + double month = v[(lenghResultVector * k) + 1]; + double day = v[(lenghResultVector * k) + 2]; + double hour = 0; + double min = 0; + double sec = 0; + pRes[k] = DateNumber(year, month, day, hour, min, sec); + } + retval << res; - } else if (param1.is2D() && param1.getColumns() == 6) { - ArrayOf asDoubleArrayOf(param1); - asDoubleArrayOf.promoteType(NLS_DOUBLE); - double* v = (double*)asDoubleArrayOf.getDataPointer(); - indexType lenghResultVector = param1.getElementCount() / 6; - Dimensions dimsRes(1, lenghResultVector); - double* pRes = (double*)ArrayOf::allocateArrayOf(NLS_DOUBLE, lenghResultVector); - ArrayOf res = ArrayOf(NLS_DOUBLE, dimsRes, pRes); + } else if (argIn[0].is2D() && argIn[0].getColumns() == 6) { + ArrayOf asDoubleArrayOf(argIn[0]); + asDoubleArrayOf.promoteType(NLS_DOUBLE); + double* v = (double*)asDoubleArrayOf.getDataPointer(); + indexType lenghResultVector = argIn[0].getElementCount() / 6; + Dimensions dimsRes(1, lenghResultVector); + double* pRes = (double*)ArrayOf::allocateArrayOf(NLS_DOUBLE, lenghResultVector); + ArrayOf res = ArrayOf(NLS_DOUBLE, dimsRes, pRes); #if WITH_OPENMP #pragma omp parallel for #endif - for (ompIndexType k = 0; k < (ompIndexType)lenghResultVector; ++k) { - double year = v[lenghResultVector * k]; - double month = v[(lenghResultVector * k) + 1]; - double day = v[(lenghResultVector * k) + 2]; - double hour = v[(lenghResultVector * k) + 3]; - double min = v[(lenghResultVector * k) + 4]; - double sec = v[(lenghResultVector * k) + 5]; - pRes[k] = DateNumber(year, month, day, hour, min, sec); - } - retval << res; - - } else { - retval << param1; + for (ompIndexType k = 0; k < (ompIndexType)lenghResultVector; ++k) { + double year = v[lenghResultVector * k]; + double month = v[(lenghResultVector * k) + 1]; + double day = v[(lenghResultVector * k) + 2]; + double hour = v[(lenghResultVector * k) + 3]; + double min = v[(lenghResultVector * k) + 4]; + double sec = v[(lenghResultVector * k) + 5]; + pRes[k] = DateNumber(year, month, day, hour, min, sec); } + retval << res; + } else { - if (param1.isRowVectorCharacterArray()) { - std::wstring strdate = param1.getContentAsWideString(); - bool bParsed; - double res = DateNumber(strdate, bParsed); - if (!bParsed) { - Error(L"None of the standard formats match the DATE string."); - } - retval << ArrayOf::doubleConstructor(res); - } else if (param1.isStringArray()) { - ArrayOf* pArrayStr = (ArrayOf*)param1.getDataPointer(); - Dimensions dimsRes = param1.getDimensions(); - double* pRes = (double*)ArrayOf::allocateArrayOf(NLS_DOUBLE, dimsRes.getElementCount()); - ArrayOf res = ArrayOf(NLS_DOUBLE, dimsRes, pRes); - for (indexType k = 0; k < dimsRes.getElementCount(); ++k) { - if (pArrayStr[k].isRowVectorCharacterArray()) { - std::wstring strdate = pArrayStr[k].getContentAsWideString(); - bool bParsed; - pRes[k] = DateNumber(strdate, bParsed); - if (!bParsed) { - Error(L"None of the standard formats match the DATE string."); - } - } else { - Error(_W("Failed to convert text to date number.")); - } - } - retval << res; - } else if (param1.isCellArrayOfCharacterVectors()) { - ArrayOf* pArrayStr = (ArrayOf*)param1.getDataPointer(); - Dimensions dimsRes = param1.getDimensions(); - double* pRes = (double*)ArrayOf::allocateArrayOf(NLS_DOUBLE, dimsRes.getElementCount()); - ArrayOf res = ArrayOf(NLS_DOUBLE, dimsRes, pRes); - for (indexType k = 0; k < dimsRes.getElementCount(); ++k) { - std::wstring strdate = pArrayStr[k].getContentAsWideString(); - bool bParsed; - pRes[k] = DateNumber(strdate, bParsed); - if (!bParsed) { - Error(L"None of the standard formats match the DATE string."); - } - } - retval << res; - } else { - Error(_W("vector double, character vector or string array expected.")); - } + retval << argIn[0]; } return retval; } //============================================================================= ArrayOfVector -datanumBuiltinTwoRhs(int nLhs, const ArrayOf& param1, const ArrayOf& param2) +datenumOneStringRhsBuiltin(int nLhs, const ArrayOfVector& argIn) { ArrayOfVector retval; - if (param1.isNumeric()) { - Warning(_W("a date vector or string expected, all subsequent arguments are ignored.")); - retval << param1; - } else if (param1.isStringArray()) { - std::wstring dateformat = param2.getContentAsWideString(); - ArrayOf* pArrayStr = (ArrayOf*)param1.getDataPointer(); - Dimensions dimsRes = param1.getDimensions(); + int pivotYear = 50; + if (argIn[0].isRowVectorCharacterArray()) { + std::wstring strdate = argIn[0].getContentAsWideString(); + bool bParsed; + double res = DateNumber(strdate, false, pivotYear, bParsed); + if (!bParsed) { + Error(L"None of the standard formats match the DATE string."); + } + retval << ArrayOf::doubleConstructor(res); + } else if (argIn[0].isStringArray()) { + ArrayOf* pArrayStr = (ArrayOf*)argIn[0].getDataPointer(); + Dimensions dimsRes = argIn[0].getDimensions(); double* pRes = (double*)ArrayOf::allocateArrayOf(NLS_DOUBLE, dimsRes.getElementCount()); ArrayOf res = ArrayOf(NLS_DOUBLE, dimsRes, pRes); for (indexType k = 0; k < dimsRes.getElementCount(); ++k) { if (pArrayStr[k].isRowVectorCharacterArray()) { std::wstring strdate = pArrayStr[k].getContentAsWideString(); bool bParsed; - pRes[k] = DateNumber(strdate, dateformat, bParsed); + pRes[k] = DateNumber(strdate, false, pivotYear, bParsed); if (!bParsed) { Error(L"None of the standard formats match the DATE string."); } @@ -243,16 +202,15 @@ datanumBuiltinTwoRhs(int nLhs, const ArrayOf& param1, const ArrayOf& param2) } } retval << res; - } else if (param1.isCellArrayOfCharacterVectors()) { - std::wstring dateformat = param2.getContentAsWideString(); - ArrayOf* pArrayStr = (ArrayOf*)param1.getDataPointer(); - Dimensions dimsRes = param1.getDimensions(); + } else if (argIn[0].isCellArrayOfCharacterVectors()) { + ArrayOf* pArrayStr = (ArrayOf*)argIn[0].getDataPointer(); + Dimensions dimsRes = argIn[0].getDimensions(); double* pRes = (double*)ArrayOf::allocateArrayOf(NLS_DOUBLE, dimsRes.getElementCount()); ArrayOf res = ArrayOf(NLS_DOUBLE, dimsRes, pRes); for (indexType k = 0; k < dimsRes.getElementCount(); ++k) { std::wstring strdate = pArrayStr[k].getContentAsWideString(); bool bParsed; - pRes[k] = DateNumber(strdate, dateformat, bParsed); + pRes[k] = DateNumber(strdate, false, pivotYear, bParsed); if (!bParsed) { Error(L"None of the standard formats match the DATE string."); } @@ -265,55 +223,144 @@ datanumBuiltinTwoRhs(int nLhs, const ArrayOf& param1, const ArrayOf& param2) } //============================================================================= ArrayOfVector -datanumBuiltinThreeRhs( - int nLhs, const ArrayOf& param1, const ArrayOf& param2, const ArrayOf& param3) +datenumTwoRhsBuiltin(int nLhs, const ArrayOfVector& argIn) { + // DateNumber = datenum(DateString, formatIn) + bool isCompatibleType = argIn[0].isCellArrayOfCharacterVectors() + || argIn[0].isRowVectorCharacterArray() || argIn[0].isStringArray(); + + if (!isCompatibleType) { + Error(_W("First input argument must be a date character vector or a string.")); + } + int pivotYear = 50; + bool withPivotYear = false; + bool withFormatIn = false; + std::wstring formatIn; + if (argIn[1].isRowVectorCharacterArray() || argIn[1].isScalarStringArray()) { + formatIn = argIn[1].getContentAsWideString(); + withFormatIn = true; + } else if (argIn[1].isNumeric() && argIn[1].isScalar()) { + pivotYear = (int)argIn[1].getContentAsDoubleScalar(); + withPivotYear = true; + } else { + Error(_W("Second argument must be a character vector, a string or scalar numerica value.")); + } + + wstringVector dateAsString = argIn[0].getContentAsWideStringVector(false); + Dimensions dimsRes(1, dateAsString.size()); + double* pRes = (double*)ArrayOf::allocateArrayOf(NLS_DOUBLE, dateAsString.size()); + ArrayOf res = ArrayOf(NLS_DOUBLE, dimsRes, pRes); + ArrayOfVector retval; - bool isValid; - Dimensions dimsRes = findCommonDimensions( - param1.getDimensions(), param2.getDimensions(), param3.getDimensions(), isValid); - if (!isValid) { - Error(_W("Invalid vector size must be compatible")); + if (withFormatIn) { + for (size_t k = 0; k < dateAsString.size(); ++k) { + bool bParsed; + pRes[k] = DateNumber(dateAsString[k], formatIn, withPivotYear, pivotYear, bParsed); + if (!bParsed) { + Error(_W("Failed to convert text to date number.")); + } + } + } else { + for (size_t k = 0; k < dateAsString.size(); ++k) { + bool bParsed; + pRes[k] = DateNumber(dateAsString[k], withPivotYear, pivotYear, bParsed); + if (!bParsed) { + Error(L"None of the standard formats match the DATE string."); + } + } } - ArrayOf param1AsDouble(param1); - param1AsDouble.promoteType(NLS_DOUBLE); - ArrayOf param2AsDouble(param2); - param2AsDouble.promoteType(NLS_DOUBLE); - ArrayOf param3AsDouble(param3); - param3AsDouble.promoteType(NLS_DOUBLE); + retval << res; + return retval; +} +//============================================================================= +ArrayOfVector +datenumThreeRhsBuiltin(int nLhs, const ArrayOfVector& argIn) +{ + // DateNumber = datenum(DateString, formatIn, pivotYear) + // DateNumber = datenum(Y, M, D) + ArrayOfVector retval; + bool withPivotYear = false; + bool isDateString = argIn[0].isRowVectorCharacterArray() + || argIn[0].isCellArrayOfCharacterVectors() || argIn[0].isStringArray(); + if (isDateString) { + wstringVector dateString = argIn[0].getContentAsWideStringVector(false); + if (argIn[1].isScalarStringArray() || argIn[1].isRowVectorCharacterArray()) { + std::wstring formatIn = argIn[1].getContentAsWideString(); + 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 (!bParsed) { + Error(_W("Failed to convert text to date number.")); + } + } + retval << res; + } + } else { + bool isAllNumeric = argIn[0].isNumeric() && argIn[1].isNumeric() && argIn[2].isNumeric(); + if (!isAllNumeric) { + Error(_W("vector double, character vector or string array expected.")); + } + ArrayOf param1(argIn[0]); + ArrayOf param2(argIn[1]); + ArrayOf param3(argIn[2]); + bool isValid; + Dimensions dimsRes = findCommonDimensions( + param1.getDimensions(), param2.getDimensions(), param3.getDimensions(), isValid); + if (!isValid) { + Error(_W("Invalid vector size must be compatible")); + } + ArrayOf param1AsDouble(param1); + param1AsDouble.promoteType(NLS_DOUBLE); + ArrayOf param2AsDouble(param2); + param2AsDouble.promoteType(NLS_DOUBLE); + ArrayOf param3AsDouble(param3); + param3AsDouble.promoteType(NLS_DOUBLE); - double* ptrParam1 = (double*)param1AsDouble.getDataPointer(); - double* ptrParam2 = (double*)param2AsDouble.getDataPointer(); - double* ptrParam3 = (double*)param3AsDouble.getDataPointer(); - double hour = 0.; - double min = 0.; - double sec = 0.; + double* ptrParam1 = (double*)param1AsDouble.getDataPointer(); + double* ptrParam2 = (double*)param2AsDouble.getDataPointer(); + double* ptrParam3 = (double*)param3AsDouble.getDataPointer(); + double hour = 0.; + double min = 0.; + double sec = 0.; - bool param1IsScalar = param1AsDouble.isScalar(); - bool param2IsScalar = param2AsDouble.isScalar(); - bool param3IsScalar = param3AsDouble.isScalar(); + bool param1IsScalar = param1AsDouble.isScalar(); + bool param2IsScalar = param2AsDouble.isScalar(); + bool param3IsScalar = param3AsDouble.isScalar(); - double* pRes = (double*)ArrayOf::allocateArrayOf(NLS_DOUBLE, dimsRes.getElementCount()); - ArrayOf res = ArrayOf(NLS_DOUBLE, dimsRes, pRes); + double* pRes = (double*)ArrayOf::allocateArrayOf(NLS_DOUBLE, dimsRes.getElementCount()); + ArrayOf res = ArrayOf(NLS_DOUBLE, dimsRes, pRes); #if WITH_OPENMP #pragma omp parallel for #endif - for (ompIndexType k = 0; k < (ompIndexType)dimsRes.getElementCount(); ++k) { - double year = param1IsScalar ? ptrParam1[0] : ptrParam1[k]; - double month = param2IsScalar ? ptrParam2[0] : ptrParam2[k]; - double day = param3IsScalar ? ptrParam3[0] : ptrParam3[k]; - pRes[k] = DateNumber(year, month, day, hour, min, sec); + for (ompIndexType k = 0; k < (ompIndexType)dimsRes.getElementCount(); ++k) { + double year = param1IsScalar ? ptrParam1[0] : ptrParam1[k]; + double month = param2IsScalar ? ptrParam2[0] : ptrParam2[k]; + double day = param3IsScalar ? ptrParam3[0] : ptrParam3[k]; + pRes[k] = DateNumber(year, month, day, hour, min, sec); + } + retval << res; } - retval << res; return retval; } //============================================================================= ArrayOfVector -datanumBuiltinSixRhs(int nLhs, const ArrayOf& param1, const ArrayOf& param2, const ArrayOf& param3, - const ArrayOf& param4, const ArrayOf& param5, const ArrayOf& param6) +datenumSixRhsBuiltin(int nLhs, const ArrayOfVector& argIn) { + // DateNumber = datenum(Y, M, D, H, MN, S) ArrayOfVector retval; + ArrayOf param1(argIn[0]); + ArrayOf param2(argIn[1]); + ArrayOf param3(argIn[2]); + ArrayOf param4(argIn[3]); + ArrayOf param5(argIn[4]); + ArrayOf param6(argIn[5]); + bool isValid; Dimensions dimsRes = findCommonDimensions(param1.getDimensions(), param2.getDimensions(), param3.getDimensions(), param4.getDimensions(), param5.getDimensions(), diff --git a/modules/time/builtin/cpp/datestrBuiltin.cpp b/modules/time/builtin/cpp/datestrBuiltin.cpp index d007e3dc9b..4bba7f1378 100644 --- a/modules/time/builtin/cpp/datestrBuiltin.cpp +++ b/modules/time/builtin/cpp/datestrBuiltin.cpp @@ -19,12 +19,18 @@ Nelson::TimeGateway::datestrBuiltin(int nLhs, const ArrayOfVector& argIn) { nargincheck(argIn, 1, 3); nargoutcheck(nLhs, 0, 1); - ArrayOfVector retval(nLhs); + ArrayOfVector retval; + + bool isValid = argIn[0].isDoubleType(true) && !argIn[0].isSparse() && argIn[0].is2D() + && argIn[0].isPositive(); - bool isValid = argIn[0].isDoubleType(true) && !argIn[0].isSparse() && argIn[0].isPositive() - && argIn[0].is2D(); if (!isValid) { - Error(_W("Numeric input data must be real.")); + if (argIn[0].isStringArray() || argIn[0].isRowVectorCharacterArray()) { + std::wstring v = argIn[0].getContentAsWideString(); + Error(v); + } else { + Error(_W("Numeric input data must be real.")); + } } bool isLocalized = false; diff --git a/modules/time/functions/@string/datestr.m b/modules/time/functions/@string/datestr.m index 54fa91d447..cd14e80b7a 100644 --- a/modules/time/functions/@string/datestr.m +++ b/modules/time/functions/@string/datestr.m @@ -31,15 +31,17 @@ varargout{1} = datestr(epochs, 'local'); else % DateString = datestr(DateStringIn, formatOut) - epochs = datenum(dateStringIn, formatOutOrLocal); - varargout{1} = datestr(epochs); + try + epochs = datenum(dateStringIn); + catch + epochs = datenum(dateStringIn, formatOutOrLocal); + end + varargout{1} = datestr(epochs, formatOutOrLocal); end return end if nargin == 3 - mustBeTextScalar(varargin{2}, 2); formatOut = convertStringsToChars(varargin{2}); - formatOutOrLocal = convertStringsToChars(varargin{3}); if ischar(formatOutOrLocal) if ~strcmp(formatOutOrLocal, 'local') @@ -50,7 +52,7 @@ varargout{1} = datestr(epochs, formatOut, 'local'); elseif (isnumeric(formatOutOrLocal) && isscalar(formatOutOrLocal)) % DateString = datestr(DateStringIn, formatOut, PivotYear) - epochs = datenum(dateStringIn, formatOut, formatOutOrLocal); + epochs = datenum(dateStringIn, formatOutOrLocal); varargout{1} = datestr(epochs, formatOut); else error(_('Third argument must be a numeric scalar.')); diff --git a/modules/time/src/cpp/DateNumber.cpp b/modules/time/src/cpp/DateNumber.cpp index 29fa05d825..4dadf61870 100644 --- a/modules/time/src/cpp/DateNumber.cpp +++ b/modules/time/src/cpp/DateNumber.cpp @@ -7,26 +7,35 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // LICENCE_BLOCK_END //============================================================================= +#ifdef _MSC_VER +#define _CRT_SECURE_NO_WARNINGS +#endif +//============================================================================= #include "DateNumber.hpp" #include "IEEEFP.hpp" #include "IsLeapYear.hpp" #include "characters_encoding.hpp" -#include #include "StringHelpers.hpp" -#include -#include -#include -#include +#include "Types.hpp" +#include +#include +#include #include +#include #include +#include +#include +#include +#include //============================================================================= namespace Nelson { //============================================================================= double -DateNumber(double year, double month, double day, double hour, double minutes, double secondes) +DateNumber(double year, double month, double day, double hour, double minutes, double seconds, + double milliseconds) { if (!IsFinite(year) || !IsFinite(month) || !IsFinite(day) || !IsFinite(hour) - || !IsFinite(minutes) || !IsFinite(secondes)) { + || !IsFinite(minutes) || !IsFinite(seconds) || !IsFinite(milliseconds)) { return nan(""); } if (month < 1) { @@ -36,9 +45,15 @@ DateNumber(double year, double month, double day, double hour, double minutes, d year += (month - 1) / 12; month = (static_cast(month - 1) % 12) + 1; } - double decimal_part = (secondes * (1.0 / (24.0 * 3600.0))) + (minutes * (1.0 / (24.0 * 60.0))) - + (hour * (1.0 / 24.0)); - // convert of month and day + + // Compute the decimal part from time (hour, minutes, seconds, and milliseconds) + double decimal_part + = (milliseconds * (1.0 / (24.0 * 3600.0 * 1000.0))) // milliseconds to day fraction + + (seconds * (1.0 / (24.0 * 3600.0))) // seconds to day fraction + + (minutes * (1.0 / (24.0 * 60.0))) // minutes to day fraction + + (hour * (1.0 / 24.0)); // hours to day fraction + + // Calculate the integer part based on days int integer_part = static_cast(day + floor((month * 3057 - 3007) / 100)); // we remove 1 if mont is more than february integer_part = integer_part + (static_cast(month < 3) - 1); @@ -57,195 +72,554 @@ DateNumber(double year, double month, double day, double hour, double minutes, d return (integer_part + decimal_part); } //============================================================================= -double -DateNumber(const std::wstring& datestring, const std::wstring& formatIn, bool& bParsed) +static int +monthStringToNumber(const std::wstring& month) { - std::wstring _formatIn(formatIn); - StringHelpers::replace_all(_formatIn, L"yyyy", L"%Y"); - StringHelpers::replace_all(_formatIn, L"yy", L"%y"); - StringHelpers::replace_all(_formatIn, L"mmm", L"%b"); - StringHelpers::replace_all(_formatIn, L"dd", L"%d"); - StringHelpers::replace_all(_formatIn, L"HH", L"%H"); - StringHelpers::replace_all(_formatIn, L"MM", L"%M"); - StringHelpers::replace_all(_formatIn, L"SS", L"%ls"); - double res = nan(""); - bParsed = false; - boost::posix_time::ptime pt(boost::posix_time::not_a_date_time); - boost::posix_time::wtime_input_facet* ptrFacet - = new boost::posix_time::wtime_input_facet(_formatIn); - std::locale format = std::locale(std::locale::classic(), ptrFacet); - std::wistringstream is(datestring); - is.imbue(format); - is.exceptions(std::ios_base::failbit); - try { - is >> pt; - } catch (const std::ios_base::failure&) { - return res; - } - if (pt != boost::posix_time::ptime()) { - boost::gregorian::date d = pt.date(); - if (!d.is_not_a_date()) { - double month = (double)d.month().as_number(); - double year = (double)d.year(); - double day = (double)d.day().as_number(); - double hours = (double)pt.time_of_day().hours(); - double minutes = (double)pt.time_of_day().minutes(); - double secondes = (double)pt.time_of_day().seconds(); - res = DateNumber(year, month, day, hours, minutes, secondes); - bParsed = true; + static std::map monthMap = { { L"jan", 1 }, { L"feb", 2 }, { L"mar", 3 }, + { L"apr", 4 }, { L"may", 5 }, { L"jun", 6 }, { L"jul", 7 }, { L"aug", 8 }, { L"sep", 9 }, + { L"oct", 10 }, { L"nov", 11 }, { L"dec", 12 } }; + std::wstring lowerMonth = month; + std::transform(lowerMonth.begin(), lowerMonth.end(), lowerMonth.begin(), ::tolower); + return monthMap[lowerMonth]; +} +//============================================================================= +static int +parseYearWithPivot(int year, int pivotYear) +{ + if (year < 100) { + int century = (pivotYear / 100) * 100; + int pivot = pivotYear % 100; + if (year <= pivot) { + year += century; + } else { + year += century - 100; } } - return res; + return year; } //============================================================================= -double -DateNumber(const std::wstring& datestring, bool& bParsed) +static bool +isValidDate(int year, int month, int day) { - const std::locale formats_without_date[] = { - std::locale(std::locale::classic(), new boost::posix_time::wtime_input_facet(L"%H:%M:%S")), - std::locale(std::locale::classic(), new boost::posix_time::wtime_input_facet(L"%H:%M")), - }; - const std::locale formats_without_time[] = { - std::locale(std::locale::classic(), new boost::posix_time::wtime_input_facet(L"%m/%d/%Y")), - std::locale(std::locale::classic(), new boost::posix_time::wtime_input_facet(L"%m/%d/%y")), - std::locale(std::locale::classic(), new boost::posix_time::wtime_input_facet(L"%Y/%m/%d")), - std::locale(std::locale::classic(), new boost::posix_time::wtime_input_facet(L"%d-%b-%Y")), - std::locale(std::locale::classic(), new boost::posix_time::wtime_input_facet(L"%Y-%m-%d")), - }; - const std::locale formats_date_time[] = { - std::locale( - std::locale::classic(), new boost::posix_time::wtime_input_facet(L"%d-%b-%Y %H:%M:%S")), - std::locale( - std::locale::classic(), new boost::posix_time::wtime_input_facet(L"%b.%m.%Y %H:%M:%S")), - std::locale( - std::locale::classic(), new boost::posix_time::wtime_input_facet(L"%Y-%m-%d %H:%M:%S")), - }; - // bool haveAMPM = StringHelpers::contains(datestring, L" AM") || - // StringHelpers::contains(datestring, L" PM"); - bool haveAM = StringHelpers::contains(datestring, L" AM"); - bool havePM = StringHelpers::contains(datestring, L" PM"); - std::wstring _datestring(datestring); - if (haveAM) { - boost::replace_all(_datestring, L" AM", ""); + if (month < 1 || month > 12) + return false; + if (day < 1 || day > 31) + return false; + if ((month == 4 || month == 6 || month == 9 || month == 11) && day > 30) + return false; + if (month == 2) { + bool isLeapYear = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); + if (day > (isLeapYear ? 29 : 28)) + return false; } - if (havePM) { - boost::replace_all(_datestring, L" PM", ""); - } - size_t count_time_separator = std::count(datestring.begin(), datestring.end(), L':'); - size_t count_date_separator_0 = std::count(datestring.begin(), datestring.end(), L','); - size_t count_date_separator_1 = std::count(datestring.begin(), datestring.end(), L'.'); - size_t count_date_separator_2 = std::count(datestring.begin(), datestring.end(), L'/'); - size_t count_date_separator_3 = std::count(datestring.begin(), datestring.end(), L'-'); - bParsed = false; - if (count_date_separator_0 == 1 && count_date_separator_1 == 1) { - bParsed = true; - return nan(""); + return true; +} +//============================================================================= +std::vector +splitString(const std::wstring& str, const std::wstring& delims) +{ + std::vector tokens; + std::wstring::size_type beg = 0; + for (auto end = str.find_first_of(delims); end != std::wstring::npos; + end = str.find_first_of(delims, beg)) { + if (end > beg) + tokens.push_back(str.substr(beg, end - beg)); + beg = end + 1; } - bool is_without_date = (count_time_separator > 0) - && ((count_date_separator_1 != 3) || (count_date_separator_2 != 3) - || (count_date_separator_3 != 3)); - bool is_without_time = (count_time_separator == 0); - bool is_with_date_time = count_time_separator > 0; - boost::posix_time::ptime pt(boost::posix_time::not_a_date_time); - if (is_without_date) { - boost::posix_time::ptime t(boost::posix_time::second_clock::local_time()); - boost::gregorian::date currentdate = t.date(); - for (const auto& i : formats_without_date) { - std::wistringstream is(datestring); - is.imbue(i); - is >> pt; - if (pt != boost::posix_time::ptime()) { - break; + if (beg < str.length()) + tokens.push_back(str.substr(beg)); + return tokens; +} +//============================================================================= + +struct DateFormatInfo +{ + std::wregex regex; + std::wstring format; + std::function parser; +}; +//============================================================================= +static std::vector +getDateFormatInfoList(bool withPivot) +{ + enum class DateFormat + { + DMY, // Day-Month-Year + MDY // Month-Day-Year + }; + + std::time_t t = std::time(nullptr); + std::tm* now = std::localtime(&t); + + // Extract year + int currentYear = now->tm_year + 1900; + int century = (currentYear / 100) * 100; + + std::vector formats = { + { std::wregex(L"(\\d{1,2})[-\\s](\\w{3})[-\\s](\\d{4})\\s(\\d{1,2}):(\\d{2}):(\\d{2})"), + L"dd-mmm-yyyy HH:MM:SS", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[3]), pivotYear) : std::stoi(m[3]); + return DateNumber(year, monthStringToNumber(m[2]), std::stoi(m[1]), std::stoi(m[4]), + std::stoi(m[5]), std::stoi(m[6])); + } }, + { std::wregex(L"(\\d{1,2})[-\\s](\\w{3})[-\\s](\\d{4})"), L"dd-mmm-yyyy", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[3]), pivotYear) : std::stoi(m[3]); + return DateNumber(year, monthStringToNumber(m[2]), std::stoi(m[1]), 0, 0, 0); + } }, + { std::wregex(L"(\\d{1,2})/(\\d{1,2})/(\\d{2})"), L"mm/dd/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[2]); + int second = std::stoi(m[1]); + if (isValidDate(year, second, first)) { + return DateNumber(year, second, first, 0, 0, 0); + } else if (isValidDate(year, first, second)) { + return DateNumber(year, first, second, 0, 0, 0); + } + return std::nan(""); + } }, + { 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 day = std::stoi(m[1]); + int month = std::stoi(m[2]); + int year = std::stoi(m[3]); + if (withPivot) { + year = pivotYear + year; + } else { + year = century + year; + } + return DateNumber(year, month, day, 0, 0, 0); + } }, + { std::wregex(L"(\\d{1,2})/(\\d{1,2})"), L"mm/dd", + [withPivot, currentYear](const std::wsmatch& m, int pivotYear) -> double { + int year = withPivot ? parseYearWithPivot(currentYear, pivotYear) : currentYear; + return DateNumber(year, std::stoi(m[1]), std::stoi(m[2]), 0, 0, 0); + } }, + { std::wregex(L"(\\d{1,2}):(\\d{2}):(\\d{2})(?:\\s(AM|PM))?", std::regex_constants::icase), + L"HH:MM:SS PM", + [withPivot, currentYear](const std::wsmatch& m, int pivotYear) -> double { + if (!m[4].matched) { + return std::nan(""); + } + int hour = std::stoi(m[1]); + int days = 1; + if (m[4] == L"PM" || m[4] == L"pm") { + if (hour > 12) { + hour -= 12; + days += 1; + } else { + hour += 12; + } + } + if ((m[4] == L"AM" || m[4] == L"am") && hour == 12) + hour = 0; + int year = withPivot ? parseYearWithPivot(currentYear, pivotYear) : currentYear; + return DateNumber(year, 1, days, hour, std::stoi(m[2]), std::stoi(m[3])); + } }, + + { std::wregex(L"(\\d{1,2}):(\\d{2})(?:\\s(AM|PM))?", std::regex_constants::icase), + L"HH:MM PM", + [withPivot, currentYear](const std::wsmatch& m, int pivotYear) -> double { + if (!m[3].matched) { + return std::nan(""); + } + int hour = std::stoi(m[1]); + int days = 1; + if (m[3] == L"PM" || m[3] == L"pm") { + if (hour > 12) { + hour -= 12; + days += 1; + } else { + hour += 12; + } + } + if ((m[3] == L"AM" || m[3] == L"am") && hour == 12) { + hour = 0; + } + int year = withPivot ? parseYearWithPivot(currentYear, pivotYear) : currentYear; + return DateNumber(year, 1, days, hour, std::stoi(m[2]), 0); + } }, + { std::wregex(L"(\\d{1,2})\\/(\\d{1,2})\\/(\\d{4})"), L"mm/dd/yyyy", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[3]), pivotYear) : std::stoi(m[3]); + return DateNumber(year, std::stoi(m[1]), std::stoi(m[2]), 0, 0, 0); + } }, + { std::wregex(L"(\\d{1,2}):(\\d{2}):(\\d{2})?", std::regex_constants::icase), L"HH:MM:SS", + [withPivot, currentYear](const std::wsmatch& m, int pivotYear) -> double { + int hour = std::stoi(m[1]); + int year = withPivot ? parseYearWithPivot(currentYear, pivotYear) : currentYear; + return DateNumber(year, 1, 1, hour, std::stoi(m[2]), std::stoi(m[3])); + } }, + { std::wregex(L"(\\d{1,2}):(\\d{2})?", std::regex_constants::icase), L"HH:MM", + [withPivot, currentYear](const std::wsmatch& m, int pivotYear) -> double { + int hour = std::stoi(m[1]); + int year = withPivot ? parseYearWithPivot(currentYear, pivotYear) : currentYear; + return DateNumber(year, 1, 1, hour, std::stoi(m[2]), 0); + } }, + { std::wregex(L"(\\d{1,2})/(\\d{1,2})/(\\d{2})"), L"dd/mm/yy or mm/dd/yy", + [withPivot, century](const std::wsmatch& m, int pivotYear) -> double { + 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)) { + return DateNumber(year, first, second, 0, 0, 0); + } + return std::nan(""); // Invalid date + } }, + { std::wregex(L"(\\d{2})(\\d{2}|\\d{4})"), L"ddmmyy", + [withPivot, century](const std::wsmatch& m, int pivotYear) -> double { + if (m.size() == 4) { + int year = withPivot ? parseYearWithPivot(std::stoi(m[3]), pivotYear) + : century + std::stoi(m[3]); + return DateNumber(year, monthStringToNumber(m[2]), std::stoi(m[1]), 0, 0, 0); + } + return std::nan(""); + } }, + { std::wregex(L"(\\w{3})(\\d{2}|\\d{4})"), L"mmmyy", + [withPivot, century](const std::wsmatch& m, int pivotYear) -> double { + int year = withPivot ? std::stoi(m[2]) + pivotYear : century + std::stoi(m[2]); + return DateNumber(year, monthStringToNumber(m[1]), 1, 0, 0, 0); + } }, + { std::wregex(L"(\\w{3})[.]?(\\d{1,2})[,.]?(\\d{4})\\s(\\d{1,2}):(\\d{2}):(\\d{2})"), + L"mmm.dd,yyyy HH:MM:SS", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[3]), pivotYear) : std::stoi(m[3]); + return DateNumber(year, monthStringToNumber(m[1]), std::stoi(m[2]), std::stoi(m[4]), + std::stoi(m[5]), std::stoi(m[6])); + } }, + { std::wregex(L"(\\w{3})[.]?(\\d{1,2})[,.]?(\\d{4})"), L"mmm.dd,yyyy", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year = std::stoi(m[3]); + return DateNumber(year, monthStringToNumber(m[1]), std::stoi(m[2]), 0, 0, 0); + } }, + { std::wregex(L"(\\d{4})/(\\d{1,2})/(\\d{1,2})"), L"yyyy/mm/dd", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[1]), pivotYear) : std::stoi(m[1]); + return DateNumber(year, std::stoi(m[2]), std::stoi(m[3]), 0, 0, 0); + } }, + { std::wregex(L"(\\d{4})-(\\d{1,2})-(\\d{1,2})"), L"yyyy-mm-dd", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[1]), pivotYear) : std::stoi(m[1]); + return DateNumber(year, std::stoi(m[2]), std::stoi(m[3]), 0, 0, 0); + } }, + { std::wregex(L"(\\d{4})-(\\d{1,2})-(\\d{1,2})\\s(\\d{1,2}):(\\d{2}):(\\d{2})"), + L"yyyy-mm-dd HH:MM:SS", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[1]), pivotYear) : std::stoi(m[1]); + return DateNumber(year, std::stoi(m[2]), std::stoi(m[3]), std::stoi(m[4]), + std::stoi(m[5]), std::stoi(m[6])); + } }, + { std::wregex(L"(\\w{3})-(\\d{1,2})-(\\d{4})\\s(\\d{1,2}):(\\d{2}):(\\d{2})"), + L"mmm-dd-yyyy HH:MM:SS", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[3]), pivotYear) : std::stoi(m[3]); + return DateNumber(year, monthStringToNumber(m[1]), std::stoi(m[2]), std::stoi(m[4]), + std::stoi(m[5]), std::stoi(m[6])); + } }, + { std::wregex(L"(\\w{3})-(\\d{1,2})-(\\d{4})"), L"mmm-dd-yyyy", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[3]), pivotYear) : std::stoi(m[3]); + return DateNumber(year, monthStringToNumber(m[1]), std::stoi(m[2]), 0, 0, 0); + } }, + { std::wregex(L"(\\d{1,2})\\s(\\w{3})\\s(\\d{4})\\s(\\d{1,2}):(\\d{2}):(\\d{2})"), + L"dd mmm yyyy HH:MM:SS", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[3]), pivotYear) : std::stoi(m[3]); + return DateNumber(year, monthStringToNumber(m[2]), std::stoi(m[1]), std::stoi(m[4]), + std::stoi(m[5]), std::stoi(m[6])); + } }, + { std::wregex(L"(\\d{1,2})\\s(\\w{3})\\s(\\d{4})"), L"dd mmm yyyy", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[3]), pivotYear) : std::stoi(m[3]); + return DateNumber(year, monthStringToNumber(m[2]), std::stoi(m[1]), 0, 0, 0); + } }, + { std::wregex(L"(\\w{3})\\s(\\d{1,2})\\s(\\d{4})\\s(\\d{1,2}):(\\d{2}):(\\d{2})"), + L"mmm dd yyyy HH:MM:SS", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[3]), pivotYear) : std::stoi(m[3]); + return DateNumber(year, monthStringToNumber(m[1]), std::stoi(m[2]), std::stoi(m[4]), + std::stoi(m[5]), std::stoi(m[6])); + } }, + { std::wregex(L"(\\w{3})\\s(\\d{1,2})\\s(\\d{4})"), L"mmm dd yyyy", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year = parseYearWithPivot(std::stoi(m[3]), pivotYear); + return DateNumber(year, monthStringToNumber(m[1]), std::stoi(m[2]), 0, 0, 0); + } }, + { std::wregex(L"(\\d{1,2})\\.(\\w{3})\\.(\\d{4})\\s(\\d{1,2}):(\\d{2}):(\\d{2})"), + L"dd.mmm.yyyy HH:MM:SS", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[3]), pivotYear) : std::stoi(m[3]); + return DateNumber(year, monthStringToNumber(m[2]), std::stoi(m[1]), std::stoi(m[4]), + std::stoi(m[5]), std::stoi(m[6])); + } }, + { std::wregex(L"(\\d{1,2})\\.(\\w{3})\\.(\\d{4})"), L"dd.mmm.yyyy", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year = parseYearWithPivot(std::stoi(m[3]), pivotYear); + return DateNumber(year, monthStringToNumber(m[2]), std::stoi(m[1]), 0, 0, 0); + } }, + { std::wregex(L"(\\w{3})\\.(\\d{1,2})\\.(\\d{4})\\s(\\d{1,2}):(\\d{2}):(\\d{2})"), + L"mmm.dd.yyyy HH:MM:SS", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year = std::stoi(m[3]); + return DateNumber(year, monthStringToNumber(m[1]), std::stoi(m[2]), std::stoi(m[4]), + std::stoi(m[5]), std::stoi(m[6])); + } }, + { std::wregex(L"(\\w{3})\\.(\\d{1,2})\\.(\\d{4})"), L"mmm.dd.yyyy", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[3]), pivotYear) : std::stoi(m[3]); + return DateNumber(year, monthStringToNumber(m[1]), std::stoi(m[2]), 0, 0, 0); + } }, + { std::wregex(L"(\\d{1,2})/(\\d{1,2})/(\\d{4})\\s(\\d{1,2}):(\\d{2})"), L"mm/dd/yyyy HH:MM", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[3]), pivotYear) : std::stoi(m[3]); + return DateNumber( + year, std::stoi(m[1]), std::stoi(m[2]), std::stoi(m[4]), std::stoi(m[5]), 0); + } }, + { std::wregex(L"(\\d{4})"), L"yyyy", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[1]), pivotYear) : std::stoi(m[1]); + return DateNumber(year, 1, 1, 0, 0, 0); + } }, + { std::wregex(L"(\\d{4})-(\\d{2})"), L"yyyy-mm", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[1]), pivotYear) : std::stoi(m[1]); + return DateNumber(year, std::stoi(m[2]), 1, 0, 0, 0); + } }, + { std::wregex(L"(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})Z"), + L"yyyy-mm-ddTHH:MM:SSZ", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[1]), pivotYear) : std::stoi(m[1]); + return DateNumber(year, std::stoi(m[2]), std::stoi(m[3]), std::stoi(m[4]), + std::stoi(m[5]), std::stoi(m[6])); + } }, + { std::wregex(L"(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})\\.(\\d{3})Z"), + L"yyyy-mm-ddTHH:MM:SS.FFFZ", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[1]), pivotYear) : std::stoi(m[1]); + return DateNumber(year, std::stoi(m[2]), std::stoi(m[3]), std::stoi(m[4]), + std::stoi(m[5]), std::stoi(m[6]), std::stoi(m[7])); + } }, + { std::wregex(L"(\\w{3})[.]?(\\d{1,2})(\\d{4})"), L"mmm.ddyyyy", + [withPivot](const std::wsmatch& m, int pivotYear) -> double { + int year + = withPivot ? parseYearWithPivot(std::stoi(m[3]), pivotYear) : std::stoi(m[3]); + return DateNumber(year, monthStringToNumber(m[1]), std::stoi(m[2]), 0, 0, 0); + } }, + { std::wregex(L"(\\d{1,2})-(\\d{1,2}),\\s*(\\d{4})"), L"mm-dd, yyyy", + [](const std::wsmatch& m, int pivotYear) -> double { + int year = std::stoi(m[3]); + int month = std::stoi(m[1]); + int day = std::stoi(m[2]); + return DateNumber(year, month, day, 0, 0, 0); + } }, + { std::wregex(L"(\\d{2})(\\d{2})(\\d{4})"), L"ddmmyyyy", + [](const std::wsmatch& m, int pivotYear) -> double { + int day = std::stoi(m[1]); + int month = std::stoi(m[2]); + int year = std::stoi(m[3]); + return DateNumber(year, month, day, 0, 0, 0); + } }, + { std::wregex(L"(\\w{3})\\.(\\d{1,2})(\\d{4})\\s(\\d{1,2}):(\\d{2}):(\\d{2})"), + L"mmm.ddyyyy HH:MM:SS", + [](const std::wsmatch& m, int pivotYear) -> double { + int year = std::stoi(m[3]); + return DateNumber(year, monthStringToNumber(m[1]), std::stoi(m[2]), std::stoi(m[4]), + std::stoi(m[5]), std::stoi(m[6])); + } }, + }; + return formats; +} +//============================================================================= +double +DateNumber(const std::wstring& datestring, const std::wstring& formatIn, bool withPivot, + int pivotYear, bool& bParsed) +{ + std::vector formats = getDateFormatInfoList(withPivot); + + for (const auto& format : formats) { + if (format.format == formatIn) { + std::wsmatch match; + if (std::regex_match(datestring, match, format.regex)) { + double value = format.parser(match, pivotYear); + if (std::isfinite(value)) { + bParsed = true; + return value; + } } } - if (pt != boost::posix_time::ptime()) { - boost::gregorian::date d = pt.date(); - double year = currentdate.year(); - double month = 1; - double day = 1; - double hours = (double)pt.time_of_day().hours(); - if (havePM) { - if (hours < 12) { - hours = hours + 12; + } + + std::tm timeinfo = {}; + std::vector dateTokens = splitString(datestring, L" -/:."); + std::vector formatTokens = splitString(formatIn, L" -/:."); + + std::time_t t = std::time(nullptr); + std::tm* now = std::localtime(&t); + + // Extract year + int currentYear = now->tm_year + 1900; + int century = (currentYear / 100) * 100; + + int year = currentYear; + int month = 1; + int day = 1; + int hours = 0; + int minutes = 0; + int secondes = 0; + size_t tokenIndex = 0; + bool timeFound = false; + + for (const auto& fmt : formatTokens) { + if (fmt == L"AM" || fmt == L"PM") { + bool findAmOrPm = false; + for (auto s : dateTokens) { + if (s == L"AM" || s == L"PM") { + findAmOrPm = true; } } - double minutes = (double)pt.time_of_day().minutes(); - double secondes = (double)pt.time_of_day().seconds(); - double res = DateNumber(year, month, day, hours, minutes, secondes); - bParsed = true; - return res; + if (!findAmOrPm) { + bParsed = false; + return std::nan(""); + } } } - if (is_without_time) { - if (count_date_separator_2 == 1) { - std::locale format = std::locale( - std::locale::classic(), new boost::posix_time::wtime_input_facet(L"%m/%d")); - std::wistringstream is(datestring); - is.imbue(format); - is >> pt; - if (pt != boost::posix_time::ptime()) { - boost::posix_time::ptime t(boost::posix_time::second_clock::local_time()); - boost::gregorian::date currentdate = t.date(); - boost::gregorian::date d = pt.date(); - double year = currentdate.year(); - double month = d.month().as_number(); - double day = d.day().as_number(); - double hours = 0; - double minutes = 0; - double secondes = 0; - double res = DateNumber(year, month, day, hours, minutes, secondes); + + for (const auto& fmt : formatTokens) { + if (tokenIndex >= dateTokens.size()) { + break; + } + if (fmt == L"mmmyy") { + try { + // Parse the month and year + std::wstring monthStr = datestring.substr(0, 3); + std::wstring yearStr = datestring.substr(3, 2); + + int month = monthStringToNumber(monthStr); + if (withPivot) { + year = parseYearWithPivot(std::stoi(yearStr), pivotYear); + } else { + year = century + std::stoi(yearStr); + } + int day = 1; // Default to the first day of the month bParsed = true; - return res; + return DateNumber(year, month, day); + } catch (...) { + bParsed = false; + return std::nan(""); + } + } else if (fmt == L"dd") { + day = std::stoi(dateTokens[tokenIndex++]); + } else if (fmt == L"mm") { + month = std::stoi(dateTokens[tokenIndex++]); + } else if (fmt == L"mmm") { + month = monthStringToNumber(dateTokens[tokenIndex++]); + } else if (fmt == L"yyyy") { + year = std::stoi(dateTokens[tokenIndex++]); + } else if (fmt == L"yy") { + year = std::stoi(dateTokens[tokenIndex++]); + if (withPivot) { + year = (year < pivotYear ? year + 100 : year) - 1900; + } else { + year = century + year; } + } else if (fmt == L"HH") { + hours = std::stoi(dateTokens[tokenIndex++]); + timeFound = true; + } else if (fmt == L"MM") { + minutes = std::stoi(dateTokens[tokenIndex++]); + timeFound = true; + } else if (fmt == L"SS") { + secondes = std::stoi(dateTokens[tokenIndex++]); + timeFound = true; } else { - for (const auto& i : formats_without_time) { - std::wistringstream is(datestring); - is.imbue(i); - is >> pt; - if (pt != boost::posix_time::ptime()) { - break; + tokenIndex++; // Skip unrecognized format tokens + } + } + + // If there are more tokens in the date string, try to parse them as time + if (tokenIndex < dateTokens.size() && !timeFound) { + std::wregex timeRegex(L"(\\d{1,2})(?::(\\d{1,2})(?::(\\d{1,2}))?)?"); + std::wstring remainingString = dateTokens[tokenIndex]; + std::wsmatch matches; + if (std::regex_match(remainingString, matches, timeRegex)) { + if (matches[1].matched) { + int h = std::stoi(matches[1]); + if (h < 24 && h > 0) { + hours = h; } } + if (matches[2].matched) { + int m = std::stoi(matches[2]); + if (m < 60 && m > 0) { + minutes = m; + } + } + if (matches[3].matched) { + int s = std::stoi(matches[3]); + if (s < 60 && s > 0) { + secondes = s; + } + } + // timeFound = true; } - if (pt != boost::posix_time::ptime()) { - boost::gregorian::date d = pt.date(); - double year = d.year(); - double month = d.month().as_number(); - double day = d.day().as_number(); - double hours = 0; - double minutes = 0; - double secondes = 0; - double res = DateNumber(year, month, day, hours, minutes, secondes); - bParsed = true; - return res; - } } - if (is_with_date_time) { - for (const auto& i : formats_date_time) { - std::wistringstream is(datestring); - is.imbue(i); - is >> pt; - if (pt != boost::posix_time::ptime()) { - break; + + // Validate date and time + if (!isValidDate(year, month, day) || hours > 23 || minutes > 59 || secondes > 59) { + bParsed = false; + return std::numeric_limits::quiet_NaN(); + } + + bParsed = true; + + return DateNumber( + year, month, day, timeFound ? hours : 0, timeFound ? minutes : 0, timeFound ? secondes : 0); +} +//============================================================================= + +double +DateNumber(const std::wstring& datestring, bool withPivot, int pivotYear, bool& bParsed) +{ + std::vector formats = getDateFormatInfoList(withPivot); + + for (const auto& format : formats) { + std::wsmatch match; + if (std::regex_match(datestring, match, format.regex)) { + double value = format.parser(match, pivotYear); + if (std::isfinite(value)) { + bParsed = true; + return value; } } - if (pt != boost::posix_time::ptime()) { - boost::gregorian::date d = pt.date(); - double year = (double)d.year(); - double month = (double)d.month().as_number(); - double day = (double)d.day().as_number(); - double hours = (double)pt.time_of_day().hours(); - double minutes = (double)pt.time_of_day().minutes(); - double secondes = (double)pt.time_of_day().seconds(); - double res = DateNumber(year, month, day, hours, minutes, secondes); - bParsed = true; - return res; - } } - return nan(""); + bParsed = false; + return std::nan(""); } //============================================================================= - } // namespace Nelson //============================================================================= diff --git a/modules/time/src/cpp/DateToFormattedString.cpp b/modules/time/src/cpp/DateToFormattedString.cpp index eb027ae9a3..f64288220d 100644 --- a/modules/time/src/cpp/DateToFormattedString.cpp +++ b/modules/time/src/cpp/DateToFormattedString.cpp @@ -123,6 +123,7 @@ formatDateTime(const std::wstring& format, const std::tm& dateTime, bool isLocal bool isAM = false; bool isPM = false; bool replaceAmByPM = false; + bool replacePmByAm = false; for (const auto& key : keys) { size_t pos = result.find(key); @@ -142,7 +143,10 @@ formatDateTime(const std::wstring& format, const std::tm& dateTime, bool isLocal replacement << std::setw(2) << std::setfill(L'0') << dateTime.tm_mday; } else if (key == L"HH") { int hour = dateTime.tm_hour; - if (isAM && dateTime.tm_hour > 12) { + if (isPM && dateTime.tm_hour < 12) { + replacement << std::setw(2) << std::setfill(L' ') << hour; + replacePmByAm = true; + } else if (isAM && dateTime.tm_hour > 12) { hour -= 12; replacement << std::setw(2) << std::setfill(L' ') << hour; replaceAmByPM = true; @@ -216,10 +220,17 @@ formatDateTime(const std::wstring& format, const std::tm& dateTime, bool isLocal } } } - if (replaceAmByPM) { - size_t pos = result.find(L"AM"); - if (pos != std::wstring::npos) { - result.replace(pos, 2, L"PM"); + if (replaceAmByPM || replacePmByAm) { + if (replaceAmByPM) { + size_t pos = result.find(L"AM"); + if (pos != std::wstring::npos) { + result.replace(pos, 2, L"PM"); + } + } else { + size_t pos = result.find(L"PM"); + if (pos != std::wstring::npos) { + result.replace(pos, 2, L"AM"); + } } } diff --git a/modules/time/src/include/DateNumber.hpp b/modules/time/src/include/DateNumber.hpp index b5b8c582fd..872399a5f6 100644 --- a/modules/time/src/include/DateNumber.hpp +++ b/modules/time/src/include/DateNumber.hpp @@ -13,12 +13,17 @@ #include "nlsTime_exports.h" //============================================================================= namespace Nelson { +//============================================================================= NLSTIME_IMPEXP double DateNumber(double year, double month, double day, double hour = 0, double minutes = 0, - double secondes = 0); + double secondes = 0, double milliseconds = 0); +//============================================================================= NLSTIME_IMPEXP double -DateNumber(const std::wstring& datestring, const std::wstring& formatIn, bool& bParsed); +DateNumber(const std::wstring& datestring, const std::wstring& formatIn, bool withPivot, + int pivotYear, bool& bParsed); +//============================================================================= NLSTIME_IMPEXP double -DateNumber(const std::wstring& datestring, bool& bParsed); +DateNumber(const std::wstring& datestring, bool withPivot, int pivotYear, bool& bParsed); +//============================================================================= } // namespace Nelson //============================================================================= diff --git a/modules/time/tests/test_datenum.m b/modules/time/tests/test_datenum.m index b778b92e35..fba4cf685f 100644 --- a/modules/time/tests/test_datenum.m +++ b/modules/time/tests/test_datenum.m @@ -18,82 +18,13 @@ REF = [1973, 8, 4, 12, 1, 18]; assert_isequal(R, REF); %============================================================================= -d = datenum('04-Aug-1973 12:01:18'); -REF = 720840.500902777770534; -assert_isequal(d, REF); -%============================================================================= assert_isequal(datenum(1973, 8, 4), 720840); assert_isequal(datenum(1970, 1, 1), 719529); %============================================================================= -dv = datevec(datenum('10-Mar-2010 16:48:17')); -REF = [2010 3 10 16 48 17]; -assert_isequal(fix(dv), REF); -%============================================================================= -dv = datevec(datenum('12-Mar-2010')); -REF = [2010 3 12 0 0 0]; -assert_isequal(fix(dv), REF); -%============================================================================= -dv = datevec(datenum('03/10/2010')); -REF = [2010 3 10 0 0 0]; -assert_isequal(dv, REF); -%============================================================================= -dv = datevec(datenum('03/10/00')); -REF = [2000 3 10 0 0 0]; -assert_isequal(dv, REF); -%============================================================================= -dv = datevec(datenum('03/10')); -REF = [2018 3 10 0 0 0]; -assert_isequal(dv(2:end), REF(2:end)); -%============================================================================= -dv = datevec(datenum('2010-03-10 16:48:17')); -REF = [2010 3 10 16 48 17]; -assert_isequal(fix(dv), REF); -%============================================================================= -dv = datevec(datenum('2010-03-10')); -REF = [2010 3 10 0 0 0]; -assert_isequal(dv, REF); -%============================================================================= -dv = datevec(datenum('2000/03/10')); -REF = [2000 3 10 0 0 0]; -assert_isequal(fix(dv), REF); -%============================================================================= -dv = datevec(datenum('16:48:17')); -r = datevec(now()); -REF = [r(1), 1, 1, 16, 48, 17]; -assert_isequal(fix(dv), REF); -%============================================================================= -dv = datevec(datenum('03:48:17 PM')); -REF = [r(1), 1, 1, 15, 48, 17]; -assert_isequal(dv, REF); -%============================================================================= -dv = datevec(datenum('03:48:17 AM')); -REF = [r(1), 1, 1, 3, 48, 17]; -assert_isequal(dv, REF); -%============================================================================= -dn = datenum('16:48'); -REF = datenum([r(1), 1, 1, 16, 48, 0]); -assert_isequal(dn, REF); -%============================================================================= -dn = datenum('03:35 PM'); -REF = datenum([r(1), 1, 1, 15, 35, 0]); -assert_isequal(dn, REF); -%============================================================================= -dn = datenum('03:35 AM'); -REF = datenum([r(1), 1, 1, 3, 35, 0]); -assert_isequal(dn, REF); -%============================================================================= R = datenum([10000, 200000]); REF = [10000, 200000]; assert_isequal(R, REF); %============================================================================= -R = datenum(["2000/03/10";"2000/03/11"]); -REF = [730555; 730556]; -assert_isequal(R, REF); -%============================================================================= -R = datenum({'2000/03/10';'2000/03/11'}); -REF = [730555; 730556]; -assert_isequal(R, REF); -%============================================================================= Y = [1973; 1974]; M = [8; 7]; D = [15; 4]; diff --git a/modules/time/tests/test_datenum_string_1.m b/modules/time/tests/test_datenum_string_1.m new file mode 100644 index 0000000000..523fe2e715 --- /dev/null +++ b/modules/time/tests/test_datenum_string_1.m @@ -0,0 +1,160 @@ +%============================================================================= +% 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 +%============================================================================= +d = datenum('04-Aug-1973 12:01:18'); +REF = 720840.500902777770534; +assert_isequal(d, REF); +%============================================================================= +dv = datevec(datenum('10-Mar-2010 16:48:17')); +REF = [2010 3 10 16 48 17]; +assert_isequal(fix(dv), REF); +%============================================================================= +dv = datevec(datenum('12-Mar-2010')); +REF = [2010 3 12 0 0 0]; +assert_isequal(fix(dv), REF); +%============================================================================= +dv = datevec(datenum('03/10/2010')); +REF = [2010 3 10 0 0 0]; +assert_isequal(dv, REF); +%============================================================================= +dv = datevec(datenum('03/10/00')); +REF = [2000 3 10 0 0 0]; +assert_isequal(dv, REF); +%============================================================================= +dv = datevec(datenum('03/10')); +REF = [2024 3 10 0 0 0]; +assert_isequal(dv(2:end), REF(2:end)); +%============================================================================= +dv = datevec(datenum('2010-03-10 16:48:17')); +REF = [2010 3 10 16 48 17]; +assert_isequal(fix(dv), REF); +%============================================================================= +dv = datevec(datenum('2010-03-10')); +REF = [2010 3 10 0 0 0]; +assert_isequal(dv, REF); +%============================================================================= +dv = datevec(datenum('2000/03/10')); +REF = [2000 3 10 0 0 0]; +assert_isequal(fix(dv), REF); +%============================================================================= +dv = datevec(datenum('16:48:17')); +r = datevec(now()); +REF = [r(1), 1, 1, 16, 48, 17]; +assert_isequal(fix(dv), REF); +%============================================================================= +dv = datevec(datenum('03:48:17 PM')); +REF = [r(1), 1, 1, 15, 48, 17]; +assert_isequal(dv, REF); +%============================================================================= +dv = datevec(datenum('03:48:17 AM')); +REF = [r(1), 1, 1, 3, 48, 17]; +assert_isequal(dv, REF); +%============================================================================= +dn = datenum('16:48'); +REF = datenum([r(1), 1, 1, 16, 48, 0]); +assert_isequal(dn, REF); +%============================================================================= +dn = datenum('03:35 PM'); +REF = datenum([r(1), 1, 1, 15, 35, 0]); +assert_isequal(dn, REF); +%============================================================================= +dn = datenum('03:35 AM'); +REF = datenum([r(1), 1, 1, 3, 35, 0]); +assert_isequal(dn, REF); +%============================================================================= +R = datenum(["2000/03/10";"2000/03/11"]); +REF = [730555; 730556]; +assert_isequal(R, REF); +%============================================================================= +R = datenum({'2000/03/10';'2000/03/11'}); +REF = [730555; 730556]; +assert_isequal(R, REF); +%============================================================================= +R = datenum('19-May-2001', 'dd-mmm-yyyy'); +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')); +REF = [2009 1 31 13 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('31-Jan-2009 13:56:23', 'dd-mmm-yyyy HH:MM:SS', 100)); +REF = [2009 1 31 13 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('31-Jan-2009 13:56:23', 'dd-mmm-yyyy')); +REF = [ 2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('31-Jan-2009', 'dd-mmm-yyyy')); +REF = [ 2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('31-Jan-2009', 'dd-mmm-yyyy', 100)); +REF = [2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('01/31/09', 'mm/dd/yy')); +REF = [2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('01/31/09', 'mm/dd/yy', 100)); +REF = [109 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('01/31','mm/dd')); +REF = [ 2024 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('01/31','mm/dd', 100)); +REF = [2024 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('01/31/2009', 'mm/dd/yyyy')); +REF = [2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('01/31/2009', 'mm/dd/yyyy', 100)); +REF = [2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('31/01/09', 'dd/mm/yy')); +REF = [ 2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('31/01/09', 'dd/mm/yy',100)); +REF = [ 109 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('01/31/09', 'mm/dd/yy')); +REF = [ 2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('01/31/09', 'mm/dd/yy', 100)); +REF = [109 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('Jan09', 'mmmyy')); +REF = [2009 1 1 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('Jan09', 'mmmyy', 100)); +REF = [ 109 1 1 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('2009/01/31', 'yyyy/mm/dd')); +REF = [ 2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('2009/01/31', 'yyyy/mm/dd', 100)); +REF = [ 2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= diff --git a/modules/time/tests/test_datenum_string_2.m b/modules/time/tests/test_datenum_string_2.m new file mode 100644 index 0000000000..f81dd3ff1b --- /dev/null +++ b/modules/time/tests/test_datenum_string_2.m @@ -0,0 +1,211 @@ +%============================================================================= +% 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 +%============================================================================= +R = datevec(datenum('2009-01-31', 'yyyy-mm-dd')); +REF = [ 2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('2009-01-31', 'yyyy-mm-dd', 100)); +REF = [ 2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('2009-01-31 13:56:23', 'yyyy-mm-dd HH:MM:SS')); +REF = [ 2009 1 31 13 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('2009-01-31 13:56:23', 'yyyy-mm-dd HH:MM:SS', 100)); +REF = [ 2009 1 31 13 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('JAN-31-2009 13:56:23', 'mmm-dd-yyyy HH:MM:SS')); +REF = [ 2009 1 31 13 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('JAN-31-2009 13:56:23', 'mmm-dd-yyyy HH:MM:SS', 100)); +REF = [ 2009 1 31 13 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('Jan-31-2009', 'mmm-dd-yyyy')); +REF = [ 2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('Jan-31-2009', 'mmm-dd-yyyy', 100)); +REF = [ 2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('31 Jan 2009 13:56:23', 'dd mmm yyyy HH:MM:SS')); +REF = [ 2009 1 31 13 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('31 Jan 2009 13:56:23', 'dd mmm yyyy HH:MM:SS', 100)); +REF = [ 2009 1 31 13 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('31 Jan 2009', 'dd mmm yyyy')); +REF = [ 2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('31 Jan 2009', 'dd mmm yyyy', 100)); +REF = [ 2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('Jan 31 2009 13:56:23', 'mmm dd yyyy HH:MM:SS')); +REF = [ 2009 1 31 13 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('Jan 31 2009 13:56:23', 'mmm dd yyyy HH:MM:SS', 100)); +REF = [ 2009 1 31 13 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('Jan 31 2009', 'mmm dd yyyy')); +REF = [ 2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('Jan 31 2009', 'mmm dd yyyy', 100)); +REF = [ 2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('31.Jan.2009 13:56:23', 'dd.mmm.yyyy HH:MM:SS')); +REF = [ 2009 1 31 13 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('31.Jan.2009 13:56:23', 'dd.mmm.yyyy HH:MM:SS', 100)); +REF = [ 2009 1 31 13 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('31.Jan.2009', 'dd.mmm.yyyy')); +REF = [ 2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('31.Jan.2009', 'dd.mmm.yyyy', 100)); +REF = [ 2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('Jan.31.2009 13:56:23', 'mmm.dd.yyyy HH:MM:SS')); +REF = [ 2009 1 31 13 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('Jan.31.2009 13:56:23', 'mmm.dd.yyyy HH:MM:SS', 100)); +REF = [ 2009 1 31 13 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('Jan.31.2009', 'mmm.dd.yyyy')); +REF = [ 2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('Jan.31.2009', 'mmm.dd.yyyy', 100)); +REF = [ 2009 1 31 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('01/31/2009 13:56', 'mm/dd/yyyy HH:MM')); +REF = [ 2009 1 31 13 56 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('01/31/2009 13:56', 'mm/dd/yyyy HH:MM', 100)); +REF = [ 2009 1 31 13 56 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('01/31/2009', 'yyyy')); +REF = [ 1 1 1 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('2009', 'yyyy')); +REF = [ 2009 1 1 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('2009', 'yyyy', 100)); +REF = [ 2009 1 1 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('2009-01', 'yyyy-mm')); +REF = [ 2009 1 1 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('2009-01', 'yyyy-mm', 100)); +REF = [ 2009 1 1 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('2009-01-31T13:56:23Z', 'yyyy-mm-ddTHH:MM:SSZ')); +REF = [ 2009 1 31 13 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('2009-01-31T13:56:23Z', 'yyyy-mm-ddTHH:MM:SSZ',100)); +REF = [ 2009 1 31 13 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('2009-01-31T13:56:23.145Z', 'yyyy-mm-ddTHH:MM:SS.FFFZ')); +REF = [2.009000000000000 0.001000000000000 0.031000000000000 0.013000000000000 0.056000000000000 0.0231] * 1e3; +assert_isapprox(R, REF, 1-3); +%============================================================================= +R = datevec(datenum('2009-01-31T13:56:23.145Z', 'yyyy-mm-ddTHH:MM:SS.FFFZ', 100)); +REF = [ 2.009000000000000 0.001000000000000 0.031000000000000 0.013000000000000 0.056000000000000 0.02314] * 1e3; +assert_isapprox(R, REF, 1-3); +%============================================================================= +R = datevec(datenum('13:56:23', 'HH:MM:SS')); +REF = [ 2024 1 1 13 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('13:56:23', 'HH:MM:SS', 100)); +REF = [ 2024 1 1 13 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('13:56', 'HH:MM')); +REF = [ 2024 1 1 13 56 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('13:56', 'HH:MM', 100)); +REF = [ 2024 1 1 13 56 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum("13:56:23 PM", "HH:MM:SS PM")); +REF = [2024 1 2 1 56 23]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum("13:56:23 PM", "HH:MM:SS PM", 100)); +REF = [2024 1 2 1 56 23]; +assert_isequal(R, REF); +%============================================================================= +assert_checkerror('datenum("13:56:23", "HH:MM:SS PM")', _('Failed to convert text to date number.')); +%============================================================================= +cmd = "R = datevec(datenum('13:56', 'HH:MM PM'))"; +msg = _('Failed to convert text to date number.'); +assert_checkerror(cmd, msg); +%============================================================================= +R = datevec(datenum('13:56 AM', 'HH:MM AM')); +REF = [ 2024 1 1 13 56 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('13:56 PM', 'HH:MM PM')); +REF = [ 2024 1 2 1 56 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('13:56 PM', 'HH:MM PM', 100)); +REF = [ 2024 1 2 1 56 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('jan.012009', 'mmm.ddyyyy')); +REF = [ 2009 1 1 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('jan.012009', 'mmm.ddyyyy', 100)); +REF = [2009 1 1 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datenum ("5-19, 2001", "mm-dd, yyyy"); +REF = 730990; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('25122015','ddmmyyyy')); +REF = [2015 12 25 0 0 0]; +assert_isequal(R, REF); +%============================================================================= +R = datevec(datenum('Jan.012009 13:56:23', 'mmm.ddyyyy HH:MM:SS')); +REF = [ 2009 1 1 13 56 23]; +assert_isequal(R, REF); +%============================================================================= diff --git a/modules/time/tests/test_datestr.m b/modules/time/tests/test_datestr.m index 6e94851ac9..c0fddf1a8c 100644 --- a/modules/time/tests/test_datestr.m +++ b/modules/time/tests/test_datestr.m @@ -309,15 +309,3 @@ msg = [_('Invalid date format (duplicated field): '), 'dd']; assert_checkerror(cmd, msg) %============================================================================= -R = datestr('05:32'); -REF = '01-Jan-2024 05:32:00'; -assert_isequal(R, REF); -%============================================================================= -R = datestr(["05:32","05:35"]); -REF = ['01-Jan-2024 05:32:00';'01-Jan-2024 05:35:00']; -assert_isequal(R, REF); -%============================================================================= -R = datestr({'05:32', '05:35'}); -REF = ['01-Jan-2024 05:32:00';'01-Jan-2024 05:35:00']; -assert_isequal(R, REF); -%============================================================================= diff --git a/modules/time/tests/test_datestr_string.m b/modules/time/tests/test_datestr_string.m new file mode 100644 index 0000000000..8d57a6aaad --- /dev/null +++ b/modules/time/tests/test_datestr_string.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 +%============================================================================= +R = datestr('05:32'); +REF = '01-Jan-2024 05:32:00'; +assert_isequal(R, REF); +%============================================================================= +R = datestr(["05:32","05:35"]); +REF = ['01-Jan-2024 05:32:00';'01-Jan-2024 05:35:00']; +assert_isequal(R, REF); +%============================================================================= +R = datestr({'05:32', '05:35'}); +REF = ['01-Jan-2024 05:32:00';'01-Jan-2024 05:35:00']; +assert_isequal(R, REF); +%============================================================================= +R = datestr('05:32 PM','HH:MM'); +REF = '17:32'; +assert_isequal(R, REF); +%============================================================================= +formatOut = 'dd mmm yyyy'; +R = datestr(datenum('16-04-55','dd-mm-yy',1900),formatOut); +REF = '16 Apr 1955'; +assert_isequal(R, REF); +%============================================================================= +R = datestr(datenum({'09/16/2007';'05/14/1996';'11/29/2010'}, 'mm/dd/yyyy')); +REF = [ '16-Sep-2007' +'14-May-1996' +'29-Nov-2010']; +assert_isequal(R, REF); +%============================================================================= +cmd = "R = datestr(datenum('13/24/88','mm/dd/yy'));"; +msg = _('Failed to convert text to date number.'); +assert_checkerror(cmd, msg); +%============================================================================= +DateStringIn = '4/16/55'; +formatOut = 1; +PivotYear = 1900; +R = datestr(DateStringIn,formatOut,PivotYear); +REF = '16-Apr-1955'; % Happy birthday! Jack ;) +assert_isequal(R, REF); +%============================================================================= +PivotYear = 2000; +R = datestr(DateStringIn,formatOut,PivotYear); +REF = '16-Apr-2055'; +%============================================================================= +R = datestr('05:32','HH:MM PM'); +REF = ' 5:32 AM'; +assert_isequal(R, REF); +%=============================================================================