Excel "dates" are doubles under the hood, and the XLOPER structure does not seem to have any concept of date, only xltypeNum (unlike Variants in VBA).
I have a simple XLL with a function that takes no arguments returns today's date as an LPXLOPER (type "P"). I have other functions that usually return dates but could also return errors which is why I am using "P" as the return value not "B".
DLLEXPORT LPXLOPER WINAPI epToday()
{
static XLOPER xResult;// Return value
xResult.xltype = xltypeNum;
xResult.val.num = ExcelDateForToday(); //A function that returns a double for today's date
return &xResult;
}
The return value appears in the calling spreadsheet as a number (eg 40303). I would like to be able to tell Excel to treat my returned double as a date: in the same way that the built-in function TODAY() does. Is there a way of doing this?
I see that =TODAY() changes the cell NumberFormat to "Date", should I be using a callback to change the format for the calling cell? This seems to be what Excel is doing: eg if I re-calc a cell with a =TODAY() function it again sets the NumberFormat to Date.
To my knowledge, following the TODAY()
approach of setting the date format via a callback is the only way to signal to Excel that the value you're returning from an XLL function is a date. However, it does have a few drawbacks:
=TODAY() - AnotherDate
, but so does Excel!Excel is able to magically avoid (4) by detecting whether it is being called as part of the calc cycle or not - I'm not sure how to achieve this without seeing the Excel source code. You could wrap you function in something to set the format which would give the user the choice (or to undo your format change as the callback would be queued second), e.g. =SetFormat(MyToday(), "yyyy-mm-dd")
The callback is fairly straightforward in xlOil (disclaimer: I wrote it):
XLO_FUNC_START(testToday())
{
CallerInfo caller;
if (!caller.fullSheetName().empty()) // Check caller is a worksheet
excelPost([=]
{
excelApp().Range[caller.writeAddress().c_str()]->NumberFormat = L"dd-mm-yyyy";
});
std::tm buf;
auto now = std::time(0);
localtime_s(&buf, &now); // Slightly labourious to get current date in C++
return returnValue(buf);
}
XLO_FUNC_END(testToday);