Marchwood Freight's out-of-hours desk answered 286 incidents in 2026, and the service review billed 23 of them as breaches of the 30-minute target. Four were real. The other nineteen were one hour, added by a subtraction that crossed a time zone.
The ticketing tool exports the moment an incident was raised in UTC: 2026-03-29T01:12:00Z, written as text, with a T in the middle and a Z on the end. The phone system exports the moment it was answered in London time.
Nobody noticed, because for five months of the year the two clocks agree. London is UTC+0 from late October to late March, so every winter row subtracts correctly and every summer row comes out an hour too long.
| Measure | As billed | As it happened |
|---|---|---|
| Incidents | 286 | 286 |
| Breaches over 30 minutes | 23 | 4 |
| Credit at £150 a breach | £3,450 | £600 |
| Longest response | 86 min | 34 min |
An hour is not a rounding error against a thirty-minute target. It is twice the target, applied to every incident raised between the last Sunday in March and the last Sunday in October.
01Two Clocks in One Subtraction
Eight Incidents, Two Clocks, Twenty-Three Breaches
Eight out-of-hours incidents from the 2026 log, taken from either side of the two clock changes. The rows sit in A2:H9. Column B is the UTC timestamp the ticketing tool exported as ISO 8601 text. Column C converts it to local time with a fixed =B2+1/24, and column D converts it with the rule the clocks actually follow: UTC+1 from 01:00 UTC on 29 March to 01:00 UTC on 25 October, UTC+0 the rest of the year. Column E is the moment the phone system recorded the answer, in London time. Column F is the billed response, =(E2-B2)*1440, which subtracts a UTC stamp from a local one, and column G is the same response measured from two UTC stamps. The full log holds 286 incidents, 23 of them invoiced as breaches of a 30-minute target at £150 each.
fxCells with formulas are highlighted in green
Hover over formula cells to see the formula and highlight referenced cells
Column B is what the ticketing tool wrote: an ISO 8601 timestamp in UTC, as text. Column E is what the phone system wrote, in London time. Column F subtracts one from the other and calls the answer a response time.
On the summer rows that subtraction is sixty minutes too generous, because the answer was recorded on a clock an hour ahead of the question. INC-2044 took 19 minutes and was billed as 79.
Columns C and D are the second half of the problem. C converts UTC to local with a fixed =B2+1/24, which is right in summer and an hour early on every winter night. D applies the rule the clocks actually follow.
Scenario: In your own log, subtract the raw timestamp column from the response column and look at the extremes with =MIN(F2:F9) and =MAX(F2:F9). A negative response, or a minimum that jumps by exactly 60 in one direction, means the two columns are not on the same clock.
02A UTC Stamp Is Text Until You Parse It
2026-03-29T01:12:00Z is not a time to Excel. It is twenty characters, left-aligned in the cell, and subtracting it from anything returns #VALUE! rather than a duration.
The stamp has a fixed shape, so the parse is two functions and nothing else:
=DATEVALUE(LEFT(A2,10))+TIMEVALUE(MID(A2,12,8))
LEFT takes 2026-03-29 and DATEVALUE turns it into a serial number. MID takes the eight characters after the T and TIMEVALUE turns them into a fraction of a day. Added together they are one real Excel timestamp.
The T and the Z have to go, however you do it. =VALUE(SUBSTITUTE(SUBSTITUTE(A2,"T"," "),"Z","")) does it in one cell, and TEXTBEFORE and TEXTAFTER do it where the seconds are sometimes missing and the fixed character positions cannot be trusted.
A parsed column is right-aligned and a text column is not, which is the fastest check on the screen. =COUNT(B2:B287) is the slower and better one: it counts numbers only, so anything under 286 is a stamp that never converted.
Scenario: Format the parsed column as yyyy-mm-dd hh:mm:ss and read three rows against the text beside them. Then put =COUNT(B2:B287) under the column and check it equals the number of rows before you trust a total built on it.
03An Offset Is a Fraction of a Day
Once the stamp is a number, converting it is arithmetic. A day is 1, an hour is 1/24, a minute is 1/1440, and an offset is added or subtracted like any other number.
| Zone | Offset | Conversion from UTC |
|---|---|---|
| London, winter | UTC+0 | =B2 |
| London, summer | UTC+1 | =B2+1/24 |
| Mumbai | UTC+5:30 | =B2+5.5/24 |
| Kathmandu | UTC+5:45 | =B2+TIME(5,45,0) |
| New York, winter | UTC−5 | =B2-5/24 |
TIME(5,45,0) is the safer way to write an awkward offset, because it saves anyone from deciding what 5:45 is in decimal hours. Half an hour is 0.5/24 and three quarters is 0.75/24; writing 5.45/24 is a 27-minute error nobody ever spots.
The display is a separate decision from the value. A converted cell keeps whatever number format it inherited, so a correct timestamp can still read as 46110.05, or as a time of day with the date hidden.
Scenario: Put a UTC stamp in one cell and =B2+5.5/24 beside it, then format both as dd/mm/yyyy hh:mm. Check that the date rolls over when the offset pushes a late-evening stamp past midnight.
04The Five Months a Fixed +1 Is Wrong
London is not UTC+1. It is UTC+0 for about five months of the year and UTC+1 for the other seven, so a column built on =B2+1/24 runs an hour ahead for every winter row in the file.
An hour is survivable in the middle of the afternoon. It is not survivable near midnight, which is exactly where an out-of-hours desk does its work.
INC-2041 was raised at 23:41 UTC on Saturday 28 March, while the clocks still said GMT. The fixed offset filed it at 00:41 on the 29th, so it left the Saturday night count and joined Sunday's.
| Row | Raised (UTC) | Fixed +1 | Actual local | Day moved |
|---|---|---|---|---|
| INC-2041 | 28/03 23:41 | 29/03 00:41 | 28/03 23:41 | Yes |
| INC-2042 | 29/03 00:20 | 29/03 01:20 | 29/03 00:20 | No |
| INC-2046 | 25/10 01:30 | 25/10 02:30 | 25/10 01:30 | No |
| INC-2047 | 03/11 02:14 | 03/11 03:14 | 03/11 02:14 | No |
Nine of the 286 incidents changed date that way, and two of the nights they moved on to crossed the five-incident threshold that triggers a review.
Scenario: Count the rows an offset can move with =SUMPRODUCT(--(MOD(B2:B287,1)>=23/24)), which finds every stamp in the last hour of a UTC day. Those are the rows whose local date is worth checking by hand.
05Where the Clocks Change, in a Formula
The United Kingdom and the European Union move on the same two instants: 01:00 UTC on the last Sunday in March, and 01:00 UTC on the last Sunday in October.
"Last Sunday in March" is a formula, not a date somebody retypes every year:
Starts =DATE(A2,3,31)-WEEKDAY(DATE(A2,3,31))+1
Ends =DATE(A2,10,31)-WEEKDAY(DATE(A2,10,31))+1
WEEKDAY with no second argument returns 1 for a Sunday and 7 for a Saturday. Subtracting it from the 31st and adding one lands on the last Sunday of that month: 29 March and 25 October in 2026.
Add 1/24 to each result and you have the two UTC instants the offset changes on. Put them in cells named BSTStart and BSTEnd, and every conversion in the workbook can read the same two numbers.
Scope. These two formulas are the European rule and nothing else. The United States changes on the second Sunday in March and the first Sunday in November, at 02:00 local, and the southern hemisphere runs the opposite way round: Sydney goes forward in October and back in April.
Japan, India, China and most of Africa never change at all, and Arizona and Saskatchewan sit out a change their neighbours make. Section 10 is what to do when one log holds several of them.
Scenario: Put =DATE(2027,3,31)-WEEKDAY(DATE(2027,3,31))+1 in a spare cell and confirm it returns 28 March 2027. Then name that cell BSTStart and point your conversion column at the name rather than the date.
06The Conversion That Knows Which Hour It Is
With the two boundaries in cells, the offset stops being a constant and becomes a test: =B2+(AND(B2>=BSTStart+1/24,B2<BSTEnd+1/24))/24.
The comparison returns TRUE or FALSE, Excel reads those as 1 and 0, and dividing by 24 turns the answer into one hour or nothing at all. One formula, both seasons, no list of dates to maintain.
LET says the same thing out loud when the boundaries are worked out in place rather than named:
=LET(utc, B2,
y, YEAR(utc),
starts, DATE(y,3,31)-WEEKDAY(DATE(y,3,31))+1+1/24,
ends, DATE(y,10,31)-WEEKDAY(DATE(y,10,31))+1+1/24,
utc + (AND(utc>=starts, utc<ends))/24)
=B2+IF(AND(B2>=BSTStart+1/24,B2<BSTEnd+1/24),1,0)/24 is the same test written out in full, for anyone who mistrusts a bare TRUE doing arithmetic. Excel reads the two identically.
Both comparisons are against UTC, never against the local column. Testing a local stamp against a local boundary is circular: the hour you are trying to work out is the hour you would need in order to run the test.
Scenario: Build the LET version beside your parsed column and check four rows by hand — one in January, one after 01:00 UTC on 29 March, one in July, one before 01:00 UTC on 25 October. If those four agree with a clock, the rest of the column does too.
07Never Subtract Across Two Zones
The £3,450 came out of one formula: =(E2-B2)*1440, the minutes between the raised stamp and the answered stamp. It is correct arithmetic on two numbers that are not measured from the same origin.
From late March to late October, E is an hour ahead of B before anybody does anything, so the subtraction hands back the real response plus sixty minutes. Nineteen incidents crossed the 30-minute line on that hour alone.
The fix is a rule rather than a formula: convert everything to UTC on the way in, do every subtraction there, and convert to local only to display it. A duration has no time zone, so the moment two zones meet inside one subtraction the answer is wrong by the difference between them.
| Row | Raised (UTC) | Answered (local) | Billed | Real |
|---|---|---|---|---|
| INC-2043 | 29/03 01:12 | 02:26 | 74 min | 14 min |
| INC-2044 | 11/06 09:05 | 10:24 | 79 min | 19 min |
| INC-2045 | 24/10 23:50 | 01:16 | 86 min | 26 min |
| INC-2047 | 03/11 02:14 | 02:48 | 34 min | 34 min |
INC-2047 is the row that makes the point. It is a real breach, in November, on a night when the two clocks agreed — and it spent the review buried in a list of twenty-three where nineteen were an artefact of arithmetic.
Scenario: Convert the answered column back to UTC with the same boundary test, then recalculate every duration from the two UTC columns. Check that the maximum falls by about 60 minutes and that no response comes out negative.
Try it in the grid08The Hour That Happens Twice
At 02:00 local on 25 October the clocks go back to 01:00, so 01:30 local happens twice that night: once at 00:30 UTC, and again an hour later at 01:30 UTC.
A local timestamp inside that hour is genuinely ambiguous. Nothing in the cell says which of the two it was and no formula can recover it, which is why a log kept in local time has already lost something a UTC log never had to store.
March does the opposite. Between 01:00 and 02:00 local on 29 March there is no time at all — the clocks jump from 00:59 to 02:00 — so a local stamp of 01:30 that night is not an error you can correct, only one you can flag.
Ambiguous =AND(D2>=BSTEnd+1/24, D2<BSTEnd+2/24)
Impossible =AND(D2>=BSTStart+1/24, D2<BSTStart+2/24)
INC-2046 is in the ambiguous hour: 01:30 local on 25 October. Its UTC stamp is the only reason we know which 01:30 it was. Run both tests over the local column and the rows that come back TRUE are the ones to read from the source, not from the sheet.
Scenario: Type 25/10/2026 01:30 into a cell as a local timestamp and try to say what it was in UTC. Then flag it with the ambiguity test instead, and go back to the exporting system for the answer.
Try it in the grid09Reporting by Local Day Without Moving Rows
The client reads a count per night in their own time, so the report has to be local even though the arithmetic is not. That is one more column, not a second version of the log.
=INT(D2) strips the time and leaves the local date. Counting on that column is honest; counting on INT of the UTC column files the last hour of every winter night under tomorrow.
Local day =INT(D2)
Hour of night =HOUR(D2)
Incidents =COUNTIFS($I$2:$I$287,J2)
Minutes =SUMIFS($G$2:$G$287,$I$2:$I$287,J2)
Average =ROUND(AVERAGE(FILTER($G$2:$G$287,$I$2:$I$287=J2)),1)
HOUR on the same column gives the hour-of-night profile the client asks for next, and it goes wrong the same way if you point it at the UTC column instead.
A date criterion has to reach COUNTIFS as a number, which is exactly what INT gives you. Typing "25/10/2026" into the criterion works on a machine whose regional settings agree with yours and returns a quiet zero on a colleague's.
Scenario: Build the local-day column, then total it two ways: =SUM(K2:K32) down the daily counts and =COUNTA(A2:A287) down the log. A difference is a stamp that failed to parse, not a day that was miscounted.
10When the Log Holds More Than One Country
One desk is one rule. Three desks are a table, because the hemispheres disagree about which months are summer and the Americas change on different Sundays from Europe.
| Site | Standard offset | Clocks change |
|---|---|---|
| London | UTC+0 | Last Sunday Mar and Oct |
| Frankfurt | UTC+1 | Last Sunday Mar and Oct |
| New York | UTC−5 | 2nd Sunday Mar, 1st Sunday Nov |
| Mumbai | UTC+5:30 | Never |
| Sydney | UTC+10 | 1st Sunday Oct, 1st Sunday Apr |
Hold the base offset and the two boundaries in a small table and read them per row: =XLOOKUP($C2,Sites[Site],Sites[Offset]) returns the base, and the same lookup against the boundary columns feeds the test. INDEX and MATCH do the job identically where XLOOKUP is not available.
Excel has no time zone database. No function takes a city and an instant and returns the offset in force, so anything you build is a table a person has to maintain — which is the honest argument for converting before the data reaches the sheet at all.
Scenario: Add a Site column to your log and a five-row offset table beside it, then write the base conversion with XLOOKUP. Check one row per site against a real clock rather than against your own formula.
11Writing a Timestamp Back Out
Anything leaving the sheet for another system goes out as UTC in ISO 8601, because it is the one spelling no reader has to guess at:
=TEXT(utc,"yyyy-mm-dd") & "T" & TEXT(utc,"hh:mm:ss") & "Z"
Building it from two TEXT calls keeps the literal T and Z outside the format string, where they would need escaping and would still catch out the next person to edit the cell.
NOW() is not UTC. It reads the machine's clock, so a "received at" stamp written by a London laptop in July sits an hour ahead of one written by the same formula in January, and neither cell says which. Convert it at the moment you capture it, with the same boundary test as every other row.
Scenario: Write the ISO formula beside three converted rows and check each result with =LEN(M2). Twenty characters is the shape the receiving system expects; nineteen means a single-digit hour lost its zero.
12Power Query, When the File Lands Every Week
A log that arrives weekly should be converted on import rather than in a column somebody remembers to fill down. Power Query reads ISO text into a datetimezone type and converts it in one step.
What you need.
DateTimeZone.FromTextreads the stamp with its offset,DateTimeZone.SwitchZonemoves it to a fixed offset, andDateTimeZone.ToLocalmoves it to the machine's own zone with daylight saving applied. That last one depends on the machine, which is fine on a desktop and a trap on a refresh running in the cloud.A query step also survives a column being inserted, because it refers to a column by name rather than by address. Three of the four bugs in this article are address-shaped.
The sheet-side formulas are still worth knowing. They are what you reach for when the file is already open, when a colleague mails you twenty rows, and when you have to show a client exactly where their number came from.
Scenario: Load one export through Power Query, set the raised column to datetimezone, add a step converting it to UTC, and compare the first ten rows against your formula column. They should agree to the second.
Try it in the grid13Eight Things That Bite
- A fixed
+1/24. Right for seven months and an hour early for five, and it does its worst damage on the rows nearest midnight. - Subtracting a local stamp from a UTC one. The result is out by the offset every time, and it looks exactly like a plausible duration.
- Leaving the stamp as text.
#VALUE!is the good outcome; a quiet zero inside aSUMIFSis the bad one. NOW()treated as a UTC clock. It is the machine's clock, and the machine moves with the local rules twice a year.- Testing the boundary against local time. Circular: the answer you need is the thing being tested.
- A date criterion typed as text.
"25/10/2026"reads differently on a machine set to US order; passINTof the timestamp or aDATEinstead. - Treating 01:30 on a clocks-back night as one instant. It is two, and only the exporting system knows which.
- An offset table nobody owns. Governments move these dates, and a table written once is wrong the first year one of them does.
14Mini Exercises
- Parse row 2 with
=DATEVALUE(LEFT(B2,10))+TIMEVALUE(MID(B2,12,8))and format itdd/mm/yyyy hh:mm. Expect 28/03/2026 23:41. - Write the two 2026 boundaries with the
WEEKDAYformulas from section 5. Expect 29 March and 25 October. - Convert all eight rows with the
ANDtest and compare against column D. Expect every row to match D, and four rows to disagree with C. - Recalculate the response times from two UTC stamps. Expect the 23 breaches to fall to 4 and the longest to fall from 86 minutes to 34.
- Flag the ambiguous hour with
=AND(D2>=BSTEnd+1/24,D2<BSTEnd+2/24). Expect TRUE on INC-2046 alone, because 01:30 local on 25 October happened twice.
What to Take Away
Nothing in this file was mistyped. Every stamp was accurate, every subtraction was arithmetic anybody would write, and the bill they produced between them was five times the one the contract called for.
The error was that two columns measured time from two different origins and nothing on the sheet said so. A timestamp with no zone attached is a number with no units, and Excel will subtract one from the other without a murmur.
So keep one rule and the rest follows: the log is UTC, the report is local, and the conversion happens in exactly one column. Nineteen of those twenty-three breaches would never have reached a page.