Set up a test you can repeat
Use a manually triggered test flow with Compose actions only. Nothing here sends email or updates a business record. Paste formulas into the expression editor without an outer @{...}. Rename each action before using its name in a formula. The names must match your flow.
Create RunUtc as a Compose containing the text 2026-09-25T00:30:00Z. A fixed input helps you repeat the same test. After testing, replace that text with utcNow() once at the start, then reuse its output. Repeated calls around midnight can produce different dates.
A trailing Z identifies UTC. An offset such as -05:00 also identifies a specific moment in time. A value like 2026-09-25 may instead be a calendar date. Read the connector output and the field definition before choosing a conversion. Microsoft explains timestamp conversion and connector differences.
Example 1: display a UTC event in local time
Beacon Delivery receives a work request at the fixed RunUtc time. The operations team uses US Central Time. Add a Compose named LocalDisplay:
convertTimeZone(outputs('RunUtc'), 'UTC', 'Central Standard Time', 'yyyy-MM-dd HH:mm')Expected output: 2026-09-24 19:30. The event belongs to September 24 locally even though its UTC date is September 25. Label this display as Central Time. Keep the original UTC value alongside it for sorting or passing to another system.
formatDateTime() alone changes how the date looks; it does not choose the business time zone. Do not append a literal Z to local display text. That would label a local clock reading as UTC. Microsoft documents date formatting.
Edge test: change RunUtc to 2026-01-25T00:30:00Z. Expect 2026-01-24 18:30. The one-hour winter difference is intentional. Central Standard Time is a Windows time zone ID, not an instruction to subtract six hours all year.
Example 2: calculate yesterday in the business time zone
The September 24 evening run should report September 23, the previous local calendar day. Create LocalToday and then ReportDate:
convertTimeZone(outputs('RunUtc'), 'UTC', 'Central Standard Time', 'yyyy-MM-dd')addDays(concat(outputs('LocalToday'), 'T00:00:00'), -1, 'yyyy-MM-dd')Expected outputs: LocalToday is 2026-09-24; ReportDate is 2026-09-23. These are calendar dates, not specific moments in time. Subtracting a day from the UTC date would select September 24, which is the wrong reporting day for this scenario.
Edge tests: at 2026-09-25T05:30:00Z, expect ReportDate 2026-09-24. With RunUtc 2026-01-01T06:30:00Z, expect 2025-12-31. The formula handles changes of month and year. The Microsoft expression cookbook covers adding days and local formatting.
Agree whether the report means yesterday, the last business day, or the last 24 hours. Weekends and a holiday calendar are separate rules. This example uses the previous calendar day.
Example 3: keep a date-only deadline unchanged
A delivery form says a task is due on September 25, regardless of where a reviewer opens it. The data it sends is {"DueDate":"2026-09-25"}. In a Compose expression, format that calendar value:
formatDateTime('2026-09-25', 'MM/dd/yyyy')Expected output: 09/25/2026. No time-zone conversion is needed. Turning this into midnight UTC and converting to Central would move the display to the previous evening, changing the deadline.
Edge tests: 2028-02-29 should display 02/29/2028. A missing value or 2026-02-30 needs a check and a way to handle the error. Do not automatically replace it with today. If a connector represents a date-only field as a timestamp, confirm that connector and field behavior before removing the time; do not apply this shortcut to actual appointment times.
Write the requirement beside the field: “due on September 25” or “due at 5 pm Central on September 25.” Those statements require different stored values. Date-format documentation also distinguishes month MM, minute mm and day dd tokens.
Example 4: build a daily UTC range across daylight saving
For a report covering a local day, convert the start and end midnight separately. Do not calculate the end by adding 24 hours to the UTC start. Create a new test Compose DayToReport with literal 2026-03-08, then these actions in order:
StartUtc:
convertToUtc(concat(outputs('DayToReport'), 'T00:00:00'), 'Central Standard Time', 'yyyy-MM-ddTHH:mm:ssZ')
NextLocalDate:
addDays(concat(outputs('DayToReport'), 'T00:00:00'), 1, 'yyyy-MM-dd')
EndUtc:
convertToUtc(concat(outputs('NextLocalDate'), 'T00:00:00'), 'Central Standard Time', 'yyyy-MM-ddTHH:mm:ssZ')The labels above identify three Compose actions; paste only the expression under each label. Microsoft defines convertToUtc and its time-zone parameter.
| Local day | StartUtc inclusive | EndUtc exclusive | Elapsed hours |
|---|---|---|---|
| 2026-03-08 | 2026-03-08T06:00:00Z | 2026-03-09T05:00:00Z | 23 |
| 2026-11-01 | 2026-11-01T05:00:00Z | 2026-11-02T06:00:00Z | 25 |
| 2026-09-24 | 2026-09-24T05:00:00Z | 2026-09-25T05:00:00Z | 24 |
Use the range timestamp greater than or equal to StartUtc, and less than EndUtc. On the March example, 2026-03-08T05:59:59Z is excluded, 2026-03-08T06:00:00Z is included, and 2026-03-09T05:00:00Z is excluded. The next report starts where this one ends, without counting that record twice.
Write that rule using the filter syntax your connector supports, then check the number of results. You still need to check pagination and whether all rows were returned. These examples use Central’s 2026 rules. If a region changes its clock at midnight, test what happens when that local time occurs twice or does not occur at all.
To test the comparison without a connector, add Filter array with this expression in From:
json('[{"Id":"A","Created":"2026-03-08T05:59:59Z"},{"Id":"B","Created":"2026-03-08T06:00:00Z"},{"Id":"C","Created":"2026-03-09T05:00:00Z"}]')In advanced mode, use the following condition. This mode includes the leading @; ordinary expression fields do not.
@and(greaterOrEquals(ticks(item()?['Created']), ticks(outputs('StartUtc'))), less(ticks(item()?['Created']), ticks(outputs('EndUtc'))))Expected result: only row B remains. All three Created values are valid UTC timestamps. Reject or route null and malformed timestamps before this filter; ticks() does not validate the input for you. In a live flow, check that the source query returned every row you need before trusting the filtered count.
Example 5: read a date using a known format
An imported text value is 04/05/2026. Before processing it, check whether the supplier means April 5 or May 4. For a documented day/month/year source, use:
formatDateTime(parseDateTime('04/05/2026', 'en-GB', 'dd/MM/yyyy'), 'yyyy-MM-dd')Expected output: 2026-05-04. For a documented US month/day/year source, use en-US and MM/dd/yyyy; expect 2026-04-05. This sample alone cannot tell you which format the supplier intended.
Edge test: 13/05/2026 is valid for the first format and invalid for the second. Keep the original value and row ID so someone can correct it. Reading a calendar date from text does not assign a business time zone to it. Microsoft documents parseDateTime locale and format parameters.
Check the whole report
Before replacing the fixed input with live data, test midnight, winter, summer, month-end, year-end and invalid values. Compare the record IDs you expect, not just a total that looks right. Keep a made-up record at the start or end of the day in your tests so you can spot problems after changing a filter.
For an actual scheduled flow, set its Recurrence time zone and intended hours separately from these data expressions. Microsoft describes the scheduling controls. Setting the trigger schedule does not automatically convert timestamps in later actions.
We based these examples on the documentation and checked the date calculations locally. We have not run the flows in a Power Automate environment. Test how your connector handles them in an approved development flow before using them for a client report.