Power Automate - check if valid date - excel

I have a Power Automate flow that calls on an Office Script to read a specific cell in an Excel workbook. That cell is supposed to have a properly formatted date. If it is properly formatted, in order for Power Automate to read that, I set a variable with this expression:
addDays('1899-12-30', int(outputs('Run_script_2')?['body/result/DeliveryDate']), 'MM/dd/yy')
However, if the output of that script isn't an expected value (i.e. 8.9.2022 instead of 8/9/2022), the flow breaks when trying to run that expression. How can I write an expression that doesn't fail if there isn't the expected Excel-type date? I'd like the expression to equate to null if it's not able to calculate an actual date value.

You can check the NumberFormatCategory, or the specific NumberFormat of the cell to check if it is formatted as a date.
if (selectedSheet.getRange("A1").getNumberFormatCategory() == ExcelScript.NumberFormatCategory.date)
...
or
if (selectedSheet.getRange("A1").getNumberFormatLocal() == "m/d/yyyy")
...
You can return this boolean to Power Automate as well and build your expression accordingly.

Here is how I solved this issue, because my users use the excel date field for more than a date, such as entering "NA", "Not Known", "TBD", etc.
Use the INT() function on the date value. Configure the next step using the "Configure run after" setting to be "Failed" only. Configure the step after that to be "is skipped".
In my case, in the failed case, I simply copy the excel value into a string I'll use for my output file. If the INT() succeeds, I run a condition statement because I want to blank out future dates but leave past dates

You can try using JavaScript's Date class to convert the date in the cell. When you create an instance of the date object, you provide it with the date value from the cell in its constructor. You can then call the toLocalDateString() method of the date object and return that. You can see an example of how to do that below:
function main(workbook: ExcelScript.Workbook) {
let selectedSheet: ExcelScript.Worksheet = workbook.getActiveWorksheet();
let rang: ExcelScript.Range = selectedSheet.getRange("A1") //cell contains the value 8.9.2022
rang.setNumberFormat("mm/dd/yyyy")
let date: Date = new Date(rang.getValue() as string);
return date.toLocaleDateString();
}

Related

Excel Online Workbook Links - Linking full row range

I am using Excel Online in the browser, have setup a workbook link to my main file from a source. In my main file I have table headers and additional columns with formula. I just need from A2 to AC down. The issue is that the source file changes daily. There might be more rows the next day or fewer. I need to be able to reference set columns and then detect how many rows are in the data source and update the main file
So far, I have something like this
='https://sharepoint.com/personal/myFolder/Documents/[data_source.xlsx]in'!A2
Which on columns B2 and C2 load the first row. I can select a range from the source data so it loads all of it, but if the next day there is more rows, it wont load those, or if there are fewer, it will display as blanks.
How can I tell the formula to select Columns A2 to C2 and extend down, or refresh the data like it does in Excel desktop when using data connections?
As you can see Source data, Day 2 has extra rows that wont be loaded in my main file.
You can use PowerAutomate and two Office Scripts to link the two workbooks together.
You'd start by using a recurrence. So you'd pick how often you'd like the flow to run (weekly, daily, etc.)
After you set the recurrence, you have to write an office script that work with the table data. You can work with the dataBodyRange of the table by using the table's GetRangeBetweenHeaderAndTotal() method. And once you have that, you can resize the range to get the data you need. Next, you need to get the values which you can use with the GetValues method. GetValues returns a 2d array which you can't return from a PowerAutomate RunScript. Since you can't do that, but you can return a string, you get around that by converting the 2d array to a json string. You can see the code below:
function main(workbook: ExcelScript.Workbook): string {
let sh: ExcelScript.Worksheet = workbook.getActiveWorksheet();
//get table
let tbl: ExcelScript.Table = sh.getTable("Table1");
//get table's column count
let tblColumnCount: number = tbl.getColumns().length;
//set number of columns to keep
let columnsToKeep: number = 3;
//set the number of rows to remove
let rowsToRemove: number = 0;
//resize the table range
let tblRange: ExcelScript.Range = tbl.getRangeBetweenHeaderAndTotal().getResizedRange(rowsToRemove,columnsToKeep - tblColumnCount);
//get the table values
let tblRangeValues: string[][] = tblRange.getValues() as string[][];
//create a JSON string
let result: string = JSON.stringify(tblRangeValues);
//return JSON string
return result;
}
Once you created your script, consider naming it something you'll remember when you call it in PowerAutomate (I called mine getTableValues). Next, after the recurrence in PowerAutomate, add a Run Script step. Fill out the values and select the script like so:
Next, you have to create the script which takes the input returned from the previous script and completes the final steps. So the script has to have a parameter that takes the string returned from the previous script (I called it tableValues in mine). In the script, you have to parse the json string array to create a 2d array, resize the initial range, and then set the values of the resized range. You can see a script that does that below:
function main(workbook: ExcelScript.Workbook, tableValues: string)
{
let sh: ExcelScript.Worksheet = workbook.getWorksheet("Sheet1")
//parses the JSON string to create array
let tableValuesArray: string[][] = JSON.parse(tableValues);
//gets row count from the array
let valuesRowCount: number = tableValuesArray.length - 1
//gets column count from the array
let valuesColumnCount: number = tableValuesArray[0].length - 1
//resizes the range
let rang: ExcelScript.Range = sh.getRange("A1").getResizedRange(valuesRowCount,valuesColumnCount)
//sets the value of the resized range to the array
rang.setValues(tableValuesArray)
}
In PowerAutomate, you have to create a second run script step. In the second step, you should be prompted with a value to enter after you've selected the script (the value is called tableValues in my step.) In the table values input, you have to enter the dynamic content Result value. Once this is done, you can save the script and test.
One thing to note is that the second script doesn't delete old range values from previous runs. This can be done in a number of different ways. But the preferred way may depend on how the workbook is structured. So I'd recommend writing code to clear the range in the second script somewhere in the beginning. Or better yet, add the output of the first script into an Excel table. And just empty out the table every time you run the second script.
If you'd like to see how you might do that, you can take a look at this post here

MinIfs syntax with conditional time/date

I'm trying to use the MinIfs method of the WorksheetFunction object to determine a conditional minimum.
Adding another condition makes my code fail, resulting in 0, which suggests that the last condition is not satisfied in the used range. But in fact it is.
What I need help with is the correct syntax of the last condition.
What works:
WorksheetFunction.MinIfs(wsSource.Columns(34), wsSource.Columns(36), False, wsSource.Columns(14), False, wsSource.Columns(3), wsL.Cells(j, 13).Value)
What doesn't:
WorksheetFunction.MinIfs(wsSource.Columns(34), wsSource.Columns(36), False, wsSource.Columns(14), False, wsSource.Columns(3), wsL.Cells(j, 13).Value, wsSource.Columns(34), """>""&" & startTime)
The variable startTime is defined as a date, and contains date and time information. Column 34 contains date and time information in custom format that looks like this: 2021.01.04 22:20:07.
What the code is trying to do is to find the earliest time and date that fits the other criteria after a certain time of that specific day (stored in startTime).
Changing format, however, shouldn't matter, and in fact it doesn't: I changed the source's format to number and it hadn't worked either. I tried to replace startTime with CDbl(startTime) as well.

Exceljs package not retrieving some cell values

I am using ExcelJS package when I retrieve some cells value, it doesn't return the values inside instead it returns some sort of format that I think is a date format sort of.
const workbook = new Excel.Workbook ();
workbook.csv.readFile(path)
.then(worksheet => {
const seenCell = worksheet.getCell('A3').value;
console.log(seenCell);
}
When I run this code try to get cell A4 it returns the content which is a string, but trying to get cell A3 returns
2027-02-11T23:00:00.000Z
I will like to know which format this is, it looks like a date to me and my data is not date.
Since CSV files don't contain any information about data types, ExcelJS tries to guess: anything that even remotely looks like a date is converted to a Date. But the test isn't perfect, and something like 123-456-7890 gets converted to 7891-01-13T22:00:00.000Z.
You can disable date detection by passing empty dateFormats list,
e.g. workbook.csv.readFile('foo.csv', {dateFormats:[]}).

Changing format of TODAY() in excel

I'm using today to aquire todays date and then adding a static value to the end of it using the following:
=TODAY()&"T23:00:00"
Which Returns 43202T23:00:00
I really need it in the format 2018-04-12T23:00:00
Any help on this would be great!
There are a couple ways to accomplish this, depending on whether your goal is a formatted String (to display) or a numeric value (such as data type Date) for storing or using with calculations.
If you want a formatted date/time result (to display to the user)...
Use the TEXT worksheet function:
=TEXT(TODAY(),"yyyy-mm-dd")&"T23:00:00"
...the reason this works is because TODAY() returns a Date data type, which is basically just a number representing the date/time, (where 1 = midnight on January 1, 1900, 2 = midnight on January 2, 1900, 2.5 = noon on January 2, 1900,etc).
You can convert the date type to a String (text) with the TEXT function, in whatever format you like. The example above will display today's date as 2018-04-12.
If, for example, you wanted the date portion of the string displayed asApril 12, 2018 then you would instead use:
TEXT(TODAY(),"mmmm d, yyyy")
Note that the TEXT worksheet function (and VBA's Format function) always return Strings, ready to be concatenated with the rest of the String that you're trying to add ("T23:00:00").
If you want to use the result in calculations...
If you instead want the result to be in a Date type, then instead of concatenating a string (produced by the TEXT function) to a string (from "T23:00:00"), you could instead add a date to a date:
=TODAY()+TIME(23,0,0)
or
=TODAY()+TIMEVALUE("23:00")
..and then you can format it as you like to show or hide Y/M/D/H/M/S as necessary with Number Formats (shortcut: Ctrl+1).
More Information:
MSDN : TEXT Function (Excel)
MSDN : TIMEVALUE Function (Excel)
MSDN : TIME Function (Excel)

How to convert and compare a date string to a date in Excel

= "7/29/2011 12:58:00 PM" > NOW()
I'd like this expression to return FALSE and yet it returns TRUE.
I know I can break apart my datetime into a date and a time and add them together as follows:
= DateValue("7/29/2011") + TimeValue("12:58:00 PM") > NOW()
But, this seems inelegant to me. I want a simple function or approach that looks nice and I feel certain that it's out there but I just can't find it.
I also know there is a VBA function called CDate which can typecast the string into a datetime and that would be perfect. But, I don't see how to call a VBA function in an excel cell.
Multiply the string by one and the comparison function will work:
= 1*"7/29/2011 12:58:00 PM" > NOW()
The answer to your question is tightly related to #Jean-François's comment: Why is the date being interpreted by Excel as a Text and not by a date?
Once you find it out, you'll be able to do the comparison.
If that's because the string is being retrieved as a text, you can simply multiply it by one and the comparison function will work, then. But it applies only in case the string format is a valid date/time format in your regional settings.
You could wrap the VBA call in a custom function:
Function ReturnDate(ByVal datestr As String) As Date
ReturnDate = CDate(datestr)
End Function
which you can use just like a formula in your sheet.
I'm upgrading the following from a comment to an answer:
Unless you have a very specific reason to do so (and right now I can't think of any), dates (and other values) really shouldn't be "hard-coded" in cells as strings like you show. Hard-coding the string like that makes it invisible and inflexible. The user will just see TRUE or FALSE with no indication of what this means.
I would just put your date 7/29/2011 12:58:00 PM in a cell on its own e.g. A1, and set the cell's format to some date format. Then you can say = A1 > NOW().
Contrary to #jonsca's and #Tiago Cardoso's answers, this answer doesn't address your specific question, but then again, what you are asking seems like really bad practice to me!
The simplest way to do this is to make a VBA function that uses CDATE and return your comparison. Then, call the function from an excel cell.
The VBA Function
Public Function compareDate(ByVal inputDate As String) As Boolean
compareDate = CDate(inputDate) > Now()
End Function
Then in your spreadsheet, just do
=compareDate("YOUR DATE")
The function will return "FALSE" if it is older and "TRUE" if it is newer than Now()

Resources