**Nearly a third of our punches appear to have been created four hours before
they happened.** 42,141 of 140,775, to be exact. Nothing is broken. The number is
an artefact of comparing two clocks that mean different things, and the same
comparison quietly produces wrong pay in a lot of attendance systems.
What the data looks like
Every punch we hold carries two timestamps. punchTime is what the terminal
reported. createdAt is when our server wrote the row.
Subtract one from the other across 140,775 punches and the distribution is not
smooth. It has a spike:
A wall at minus four, a tail thinning towards minus two, and then almost nothing.
Minus four is the UAE offset. The terminal reports the wall time on its display,
which is UTC+4. The server records the instant of receipt in UTC. A punch
delivered the moment it happened therefore looks like it was received four hours
before it occurred.
The tail from -4.00 to -2.04 is not clock error, it is delivery latency: a punch
sitting at -2.04 took nearly two hours to reach us. So this one distribution
answers two questions at once. The wall tells you the timezone. The spread from
the wall tells you how fast the fleet is delivering.
The other 70 per cent
98,638 punches, 70.1 per cent, arrived more than a day after they happened. The
median gap for the whole set is around 51 days.
That is not a fault either. It is history: when a terminal is connected to a new
system, its onboard memory is read and every punch it has stored is pulled in.
Those records are real attendance from real days, ingested months later.
Two timestamps, two entirely different meanings, and a system that conflates them
will produce nonsense in both directions. Sort a report by createdAt and you get
the order things were imported. Sort by punchTime and you get the order things
happened. Only one of those is attendance.
Why four hours is not a small error
The instinct is that a few hours is a rounding issue. In attendance it is not,
because the money changes at boundaries and four hours moves shifts across them.
Midnight. A punch at 01:00 local, treated as UTC, lands on the previous day.
That single day shift changes which week the hours belong to, which rest day
applies, and whether a shift was one long day or two short ones.
22:00. Under Federal Decree-Law No. 33 of 2021, overtime between 22:00 and
04:00 attracts a 50 per cent uplift rather than 25. Shift an evening's records by
four hours in the wrong direction and hours that should be premium become
ordinary, or the reverse.
The rest day. A shift that crosses midnight into a rest day carries different
entitlements. Which day it belongs to is decided entirely by the timestamp
convention.
None of these produce an error message. They produce a payslip that is confidently
wrong, and an employee who is right to query it.
The rule we settled on
Store both, and never let one impersonate the other.
The local wall time is the event. It is what the employee experienced, what
the roster is written in, what the supervisor remembers, and what a payslip has
to agree with. An employee who started at 06:00 started at 06:00. No conversation
about that fact should ever involve a timezone.
The UTC instant is for the machine. Ordering, deduplication, replication,
anything that spans regions or has to agree with another system.
Never derive one from the other silently. The moment a system converts
without saying so, every question downstream inherits an assumption nobody can
see. The conversion is fine; the silence is the bug.
That sounds obvious written down. It is not how most systems that grew from a
single site behave, because at a single site both clocks agree and the
distinction costs nothing to ignore. The cost arrives with the second timezone,
or the first night shift, and by then the assumption is spread through years of
stored data.
The check worth running on your own data
Take a week of punches. For each, compute the difference between the timestamp
the device reported and the timestamp the record was created. Plot the
distribution.
A wall at a whole number of hours is a timezone offset. That is normal, and
the number tells you which convention each side is using.
A wall at a whole number that changes between devices means your fleet is not
configured consistently, and some sites are producing records on a different
convention from others. This is the one that hurts, because reports aggregate
across sites.
A gap that drifts steadily is a clock going wrong. Terminals drift, some by
minutes a month, and a device that is twenty minutes fast produces twenty minutes
of overtime that nobody worked.
A gap that jumps twice a year is a daylight saving rule applied somewhere it
should not be. The UAE does not observe daylight saving; plenty of software
assumes everywhere does.
The two bugs this always produces
Having been through it, the failures arrive in a predictable order.
The night shift that reports negative hours. Somebody starts at 22:00 and
finishes at 06:00. If the two timestamps get compared without agreeing which day
each belongs to, the finish precedes the start and the shift is minus sixteen
hours. This one is loud, which makes it the good kind: somebody notices
immediately and it gets fixed.
The shift that is off by exactly the offset. Every figure is plausible.
Nothing is negative, nothing is absurd, the report renders and the totals add up.
The hours are simply wrong by four, or the shift sits on the wrong side of 22:00,
and nobody notices until an employee compares a payslip against what they
remember doing.
The second is the expensive one, and it is expensive precisely because it does
not look like a bug. There is no error, no alert, no anomaly. There is just a
number that disagrees with a person, and the system says the number.
The tell is always the same: a discrepancy that is a constant, not a variable. If
several people are out by the same amount, that is a convention problem. If they
are out by different amounts, that is a data problem. Those two need completely
different investigations and the first question is worth asking before either
starts.
Why night shifts are the only test that matters
If you are evaluating attendance software, or checking your own, there is one
test worth running above all others.
Create a shift that starts at 22:00 and ends at 06:00 the following morning. Put
somebody on it. Then check four things: the hours worked, which day the shift is
attributed to, how much of it attracts the premium overtime rate, and what
happens when that shift lands on a rest day.
A system that is careless about time will pass every daytime test ever written
and fail this one. It is the shortest path to finding out whether the
distinctions in this post are actually being made, or whether they merely appear
to be because nothing so far has crossed a boundary.
In our own data, roughly one punch in seven happens after 22:00, so this is not a
corner case being tested for completeness. It is a seventh of the business.
What we do now
Every punch keeps the device's reported time exactly as sent, unmodified. The
server's receipt time is recorded separately and is never used to answer a
question about when somebody worked. Where the two are compared, the comparison
is explicit and the offset is a value, not an assumption.
The four-hour wall in our data is not a defect we are working around. It is the
evidence that the two clocks are being kept apart, which is the only state in
which either of them is trustworthy.
Put the workflow into practice.
Start with every Wurxa feature for 14 days. No credit card required.



