diff --git a/AlteryxAbacus.dll b/AlteryxAbacus.dll index 2b230ba..b86f597 100644 Binary files a/AlteryxAbacus.dll and b/AlteryxAbacus.dll differ diff --git a/AlteryxAbacus/AlteryxAbacus.aps b/AlteryxAbacus/AlteryxAbacus.aps index 02f0eb9..0542e69 100644 Binary files a/AlteryxAbacus/AlteryxAbacus.aps and b/AlteryxAbacus/AlteryxAbacus.aps differ diff --git a/AlteryxAbacus/AlteryxAbacus.cpp b/AlteryxAbacus/AlteryxAbacus.cpp index 51e8dfd..35f2244 100644 --- a/AlteryxAbacus/AlteryxAbacus.cpp +++ b/AlteryxAbacus/AlteryxAbacus.cpp @@ -395,5 +395,43 @@ extern "C" long _declspec(dllexport) _stdcall Split(int nNumArgs, FormulaAddInDa pReturnValue->isNull = 0; } + return AlteryxAbacusUtils::ReturnSuccess(nNumArgs, pArgs); +} + +// Need String, String (char), Integer +extern "C" long _declspec(dllexport) _stdcall RangeJoin(int nNumArgs, FormulaAddInData *pArgs, FormulaAddInData *pReturnValue) +{ + pReturnValue->nVarType = 1; + + // Check Input Parameters + if (nNumArgs != 2 || + pArgs[0].nVarType != 1 || + pArgs[1].nVarType != 2) { + return AlteryxAbacusUtils::ReturnError(L"Syntax: Number, RangeList", pReturnValue, nNumArgs, pArgs); + } + + // Check for Nulls + if (pArgs[0].isNull || pArgs[2].isNull) { + pReturnValue->isNull = 1; + return AlteryxAbacusUtils::ReturnSuccess(nNumArgs, pArgs); + } + + // Read Target Value to Long Long + const auto target = static_cast(pArgs[0].dVal); + + // Read String + const wchar_t* start = pArgs[1].pVal - 1; + while (*start != L'\0') { + wchar_t* end; + const auto value = _wcstoi64(start + 1, &end, 10); + start = end; + if (value > target) + { + pReturnValue->dVal = value; + return AlteryxAbacusUtils::ReturnSuccess(nNumArgs, pArgs); + } + } + + pReturnValue->isNull = 1; return AlteryxAbacusUtils::ReturnSuccess(nNumArgs, pArgs); } \ No newline at end of file diff --git a/AlteryxAbacus/AlteryxAbacus.rc b/AlteryxAbacus/AlteryxAbacus.rc index 1d88fcc..1e52409 100644 Binary files a/AlteryxAbacus/AlteryxAbacus.rc and b/AlteryxAbacus/AlteryxAbacus.rc differ diff --git a/AlteryxAbacus/DateTimeFunctions.cpp b/AlteryxAbacus/DateTimeFunctions.cpp index 9f46194..8543188 100644 --- a/AlteryxAbacus/DateTimeFunctions.cpp +++ b/AlteryxAbacus/DateTimeFunctions.cpp @@ -1,10 +1,11 @@ #include #include "AlteryxAbacus.h" #include "AlteryxAbacusUtils.h" +#include const int daysInMonthArray[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; -int daysInMonth(int year, int month) +int days_in_month(int year, int month) { if (month < 1 || month > 12) { @@ -28,9 +29,9 @@ extern "C" long __declspec(dllexport) _stdcall MakeDate(int nNumArgs, FormulaAdd return AlteryxAbacusUtils::ReturnError(L"MakeDate: Requires between 1 and 3 numerical arguments", pReturnValue, nNumArgs, pArgs); } - int year = static_cast(pArgs[0].dVal); - int month = nNumArgs > 1 && !pArgs[1].isNull ? static_cast(pArgs[1].dVal) : 1; - int day = nNumArgs > 2 && !pArgs[2].isNull ? static_cast(pArgs[2].dVal) : 1; + const auto year = static_cast(pArgs[0].dVal); + const auto month = nNumArgs > 1 && !pArgs[1].isNull ? static_cast(pArgs[1].dVal) : 1; + const auto day = nNumArgs > 2 && !pArgs[2].isNull ? static_cast(pArgs[2].dVal) : 1; if (year == 0 && month == 0 && day == 0) { @@ -49,17 +50,24 @@ extern "C" long __declspec(dllexport) _stdcall MakeDate(int nNumArgs, FormulaAdd return AlteryxAbacusUtils::ReturnError(L"MakeDate: Month must be between 1 and 12", pReturnValue, nNumArgs, pArgs); } - int maxDay = daysInMonth(year, month); - if (day < 1 || day > maxDay) - { - std::wstring msg(L"MakeDate: Day must be between 1 and "); - return AlteryxAbacusUtils::ReturnError((msg + std::to_wstring(maxDay)).c_str(), pReturnValue, nNumArgs, pArgs); + if (day > 28 || day < 1) { + const int max_day = days_in_month(year, month); + if (day < 1 || day > max_day) + { + const std::wstring msg(L"MakeDate: Day must be between 1 and "); + return AlteryxAbacusUtils::ReturnError((msg + std::to_wstring(max_day)).c_str(), pReturnValue, nNumArgs, pArgs); + } } - std::wstring output = std::to_wstring(year); - output += (month < 10 ? L"-0" : L"-") + std::to_wstring(month); - output += (day < 10 ? L"-0" : L"-") + std::to_wstring(day); - AlteryxAbacusUtils::SetString(pReturnValue, output.c_str()); + const long long dt = year * 1000000LL + month * 1000L + day; + //std::wstring output = std::to_wstring(dt); + //AlteryxAbacusUtils::SetString(pReturnValue, output.c_str()); + auto *p_string_ret = static_cast(GlobalAlloc(GMEM_FIXED, 11 * sizeof(wchar_t))); + _i64tow_s(dt, p_string_ret, 11 * sizeof(wchar_t), 10); + p_string_ret[4] = L'-'; + p_string_ret[7] = L'-'; + p_string_ret[10] = L'\0'; + pReturnValue->pVal = p_string_ret; pReturnValue->isNull = 0; return AlteryxAbacusUtils::ReturnSuccess(nNumArgs, pArgs); } @@ -73,9 +81,9 @@ extern "C" long __declspec(dllexport) _stdcall MakeTime(int nNumArgs, FormulaAdd return AlteryxAbacusUtils::ReturnError(L"MakeTime: Requires between 0 and 3 numerical arguments", pReturnValue, nNumArgs, pArgs); } - int hour = nNumArgs > 0 && !pArgs[0].isNull ? static_cast(pArgs[0].dVal) : 0; - int minute = nNumArgs > 1 && !pArgs[1].isNull ? static_cast(pArgs[1].dVal) : 0; - int second = nNumArgs > 2 && !pArgs[2].isNull ? static_cast(pArgs[2].dVal) : 0; + const auto hour = nNumArgs > 0 && !pArgs[0].isNull ? static_cast(pArgs[0].dVal) : 0; + const auto minute = nNumArgs > 1 && !pArgs[1].isNull ? static_cast(pArgs[1].dVal) : 0; + const auto second = nNumArgs > 2 && !pArgs[2].isNull ? static_cast(pArgs[2].dVal) : 0; if (hour < 0 || hour > 23) { @@ -92,10 +100,17 @@ extern "C" long __declspec(dllexport) _stdcall MakeTime(int nNumArgs, FormulaAdd return AlteryxAbacusUtils::ReturnError(L"MakeTime: Second must be between 0 and 59", pReturnValue, nNumArgs, pArgs); } - std::wstring output = (hour < 10 ? L"0" : L"") + std::to_wstring(hour); - output += (minute < 10 ? L":0" : L":") + std::to_wstring(minute); - output += (second < 10 ? L":0" : L":") + std::to_wstring(second); - AlteryxAbacusUtils::SetString(pReturnValue, output.c_str()); + auto *p_string_ret = static_cast(GlobalAlloc(GMEM_FIXED, 9 * sizeof(wchar_t))); + _itow_s((hour+30) * 10000 + minute * 100 + second, p_string_ret, 8 * sizeof(wchar_t), 10); + *(p_string_ret + 8) = L'\0'; + *(p_string_ret + 7) = *(p_string_ret + 5); + *(p_string_ret + 6) = *(p_string_ret + 4); + *(p_string_ret + 5) = L':'; + *(p_string_ret + 4) = *(p_string_ret + 3); + *(p_string_ret + 3) = *(p_string_ret + 2); + *(p_string_ret + 2) = L':'; + *p_string_ret = *p_string_ret - 3; + pReturnValue->pVal = p_string_ret; pReturnValue->isNull = 0; return AlteryxAbacusUtils::ReturnSuccess(nNumArgs, pArgs); } @@ -103,7 +118,7 @@ extern "C" long __declspec(dllexport) _stdcall MakeTime(int nNumArgs, FormulaAdd extern "C" long __declspec(dllexport) _stdcall MakeDateTime(int nNumArgs, FormulaAddInData *pArgs, FormulaAddInData *pReturnValue) { // The Date Part - long dateResult = MakeDate(nNumArgs > 3 ? 3 : nNumArgs, pArgs, pReturnValue); + const long dateResult = MakeDate(nNumArgs > 3 ? 3 : nNumArgs, pArgs, pReturnValue); if (dateResult != 0) { std::wstring msg(pReturnValue->pVal); @@ -123,7 +138,7 @@ extern "C" long __declspec(dllexport) _stdcall MakeDateTime(int nNumArgs, Formul } // The Time Part - long timeResult = MakeTime(nNumArgs - 3, pArgs + 3, pReturnValue); + const long timeResult = MakeTime(nNumArgs - 3, pArgs + 3, pReturnValue); if (timeResult != 0) { std::wstring msg(pReturnValue->pVal); diff --git a/DateUtils.Test/CenturyTest.yxmd b/DateUtils.Test/CenturyTest.yxmd new file mode 100644 index 0000000..02b5bc9 --- /dev/null +++ b/DateUtils.Test/CenturyTest.yxmd @@ -0,0 +1,332 @@ + + + + + + + + + + + .\DateReference.xlsx + + False + + + + + DateReference.xlsx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [Actual] != [Expected] + Custom + + + + [Actual] != [Expected] + + + + + + + + + + + + + + Century + #1 + RecCountValue + 0 + + + + + + + Month + Century + + + + + + + + + + + + + .\DateTimeReference.xlsx + + False + + + + + DateTimeReference.xlsx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + [Actual] != [Expected] + Custom + + + + [Actual] != [Expected] + + + + + + + + + + + + + + Century DateTime + #1 + RecCountValue + 0 + + + + + + + Century DateTime + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Horizontal + + + CenturyTest + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DateUtils.Test/DatePartTest.yxmd b/DateUtils.Test/DatePartTest.yxmd index 3b6eeee..7dc9aa6 100644 --- a/DateUtils.Test/DatePartTest.yxmd +++ b/DateUtils.Test/DatePartTest.yxmd @@ -3,7 +3,7 @@ - + @@ -49,7 +49,7 @@ Table=`Sheet1$` - + @@ -207,7 +207,7 @@ Table=`Sheet1$` - + @@ -249,7 +249,7 @@ Table=`Sheet1$` - + @@ -283,58 +283,26 @@ Table=`Sheet1$` - + - - - + - P1 = [DateText] -P2 = [Part] -Actual = Switch(P2,NULL(), - "year",Year(P1)... + - - - - - - - C:\Users\JDUNKE~1.SCO\AppData\Local\Temp\Engine_25912_c16d6cb3dd0d44a69081cdbc79f5b9b4_\Engine_22056_e54e7c4beb9e4a8482124bf5b892debb_.yxdb - - - Single - - - Profile - - - - - - - - - - - - - - - - + @@ -365,6 +333,134 @@ Actual = Switch(P2,NULL(), + + + + + + + + + DatePartTest DateTime Join + #1 + RecCountValue + 0 + + + + + + + DatePartTest DateTime Join + + + + + + + + + + + + + + DatePartTest DateTime Result + #1 + RecCountValue + 0 + + + + + + + DatePartTest DateTime Result + + + + + + + + + + + + Switch(P2,NULL(), + "year",Year(P1), + "yyyy",Year(P1), + "yy",Year(P1), + "%Y",Year(P1), + "%y",Year(P1), + "cc",Century(P1), + "c",Century(P1), + "%c",Century(P1), + "quarter",Quarter(P1), + "qq",Quarter(P1), + "q",Quarter(P1), + "month",Month(P1), + "mm",Month(P1), + "m",Month(P1), + "dayofyear",OrdinalDay(P1), + "dy",OrdinalDay(P1), + "y",OrdinalDay(P1), + "%j",OrdinalDay(P1), + "day",DAY(P1), + "dd",DAY(P1), + "d",DAY(P1), + "%d",DAY(P1), + "%e",DAY(P1), + "hour",HOUR(P1), + "hh",HOUR(P1), + "h",HOUR(P1), + "%H",HOUR(P1), + "%k",HOUR(P1), + "mi",MINUTE(P1), + "n",MINUTE(P1), + "ss",SECOND(P1), + "s",SECOND(P1), + "%s",SECOND(P1), + "%m",IIF(CharToInt(Right(P2,1))=77, MINUTE(P1),MONTH(P1)) +) + + + + + + + + + + + + + + + + + + + + WEEK (need new function) +weekday, dw ==> WEEKDAY +%w ==> lowercase WEEKDAY, uppercase WEEK +%i, %l ==> 12 HOUR CLOCK +]]> + + + + + + + + + + + + + @@ -383,13 +479,17 @@ Actual = Switch(P2,NULL(), + + + + - + - + diff --git a/DateUtils.Test/MakeDatePerfTest.yxmd b/DateUtils.Test/MakeDatePerfTest.yxmd new file mode 100644 index 0000000..aa1da82 --- /dev/null +++ b/DateUtils.Test/MakeDatePerfTest.yxmd @@ -0,0 +1,151 @@ + + + + + + + + + + + + RowCount + Int32 + 4 + 1 + RowCount <= 250000 + RowCount + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + C:\Users\JDUNKE~1.SCO\AppData\Local\Temp\Engine_6640_0ed895ca79b54832a4438e6890311e6b_\Engine_32512_e3a46a9352b14c9d9b7ff279830ee39b_.yxdb + + + Single + + + Profile + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Horizontal + + + MakeDatePerfTest + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DateUtils.xml b/DateUtils.xml index 3b8e3cd..f6696fb 100644 --- a/DateUtils.xml +++ b/DateUtils.xml @@ -235,4 +235,12 @@ Parse a D/M/Y date string into Alteryx format. Copes with or without leading 0 and with various separators. DATETIMEPARSE(REGEX_Replace(Trim(REGEX_Replace(" " + P1,"(\D)(\d)(?=\D)","${1}0$2")), "\D", "/"), "%d/%m/" + IIF(REGEX_Match(P1,".*\D\d{1,2}$"), "%y", "%Y")) + + DATEPART + 2 + DateTime + DATEPART(DateTime, Part) + Reproduces SQL Server's DatePart function. Support SQL servers standard parts as well as the numeric Alteryx date formatters. + Switch(LOWERCASE(P2),NULL(),"year",Year(P1),"yyyy",Year(P1),"yy",Year(P1),"%y",Year(P1),"cc",Century(P1),"c",Century(P1),"%c",Century(P1),"quarter",Quarter(P1),"qq",Quarter(P1),"q",Quarter(P1),"month",Month(P1),"mm",Month(P1),"m",Month(P1),"dayofyear",OrdinalDay(P1),"dy",OrdinalDay(P1),"y",OrdinalDay(P1),"%j",OrdinalDay(P1),"day",DAY(P1),"dd",DAY(P1),"d",DAY(P1),"%d",DAY(P1),"%e",DAY(P1),"hour",HOUR(P1),"hh",HOUR(P1),"h",HOUR(P1),"%h",HOUR(P1),"%k",HOUR(P1),"mi",MINUTE(P1),"n",MINUTE(P1),"ss",SECOND(P1),"s",SECOND(P1),"%s",SECOND(P1),"%m",IIF(CharToInt(Right(P2,1))=77, MINUTE(P1),MONTH(P1)),"weekday",WEEKDAY(P1),"dw",WEEKDAY(P1)) + diff --git a/MiscUtils.Test/RangeJoinTest.yxmd b/MiscUtils.Test/RangeJoinTest.yxmd new file mode 100644 index 0000000..7c5dd46 --- /dev/null +++ b/MiscUtils.Test/RangeJoinTest.yxmd @@ -0,0 +1,351 @@ + + + + + + + + + + + + + + + + 1 + + + 100 + + + 250 + + + 500 + + + 750 + + + 900 + + + 1234 + + + + + + + + + + + + + + + + + + + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 4239 + + + + + + + + + + + + + + + + + + Error + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + C:\Users\JDUNKE~1.SCO\AppData\Local\Temp\Engine_33160_af6c18f1bfd14626a8ec03afa689f92d_\Engine_32512_bd9f678ee6df451185e4ed4cb135c04c_.yxdb + + + Single + + + Profile + + + + + + + + + + + + + + + + + + + + C:\Users\JDUNKE~1.SCO\AppData\Local\Temp\Engine_33160_af6c18f1bfd14626a8ec03afa689f92d_\Engine_32512_401bed2a687e41409d111dbf6bc3f6a4_.yxdb + + + Single + + + Profile + +
+ + + + + + + + + + + + + + + + + + + + C:\Users\JDUNKE~1.SCO\AppData\Local\Temp\Engine_33160_af6c18f1bfd14626a8ec03afa689f92d_\Engine_32512_b88d5ae2a8624d42b2c4fe98f6024e3e_.yxdb + + + Single + + + Profile + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Horizontal + + + RangeJoinTest + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MiscUtils.xml b/MiscUtils.xml index 1263f9b..eb79824 100644 --- a/MiscUtils.xml +++ b/MiscUtils.xml @@ -101,13 +101,24 @@ LOG 3 Specialized - LOG(ReturnValue, Message, FileName) + LOG(ReturnValue, FileName, Message) Write to a log file AlteryxAbacus.dll Log + + RangeJoin + 2 + Specialized + RangeJoin(Value, RangeCSV) + Find value in list above value + + AlteryxAbacus.dll + RangeJoin + + REPORTERROR 0