Precision issues with different DateTime types

It was 2:15 PM [ Friday afternoon], and the trading floor was humming with energy. Stocks were flying, orders were coming, and our systems were working overtime to keep up. Everything was smooth until it wasn’t. A confused trader called our client’s customer care, exclaiming, “Why are yesterday’s trades showing up as settled today?” as soon as we got this news we ran for a quick meeting…

By the time we uncovered the issue, it had already cost us $200K in delayed settlements and botched order executions..

The culprit? A simple but DateTime **bug **hidden in our code….

— Problem

If you think time is straightforward, you probably haven’t worked on a trading system. For most applications, a timestamp is just a way to mark when something happened. In trading, time isn’t just important — it’s everything. Every millisecond can mean the difference between profit and loss. Here’s what we learned the hard way about the dangers of mishandling time.

1. TimeZone Handling

Our first mistake was assuming everyone in the world was operating in the same time zone(lol he was junior and its about us who pushed it!). Orders were being timestamped using DateTime.Now, which records the local system time. Sounds harmless. Well, not when you’re dealing with traders in multiple time zones.

Imagine this: A trader in New York places an order at 4:00 PM EST. But our servers, running in UTC, recorded it as 9:00 PM. When the trades were reconciled, timestamps didn’t match, causing delays and failed settlements. Even worse, daylight savings time kicked in, throwing everything off by an hour. The result? Mismatched timestamps and unhappy clients.

What We Learned:

  • Always store timestamps in UTC.
  • Convert to local time only for display purposes.

2. Precision Issues: Milliseconds Matter

In high-frequency trading, precision isn’t optional. A trade executed at 12:34:56.789* *is very different from one at 12:34:56.790. Unfortunately, our system was rounding timestamps to the nearest second, and that tiny imprecision caused chaos in our order-matching algorithms.

Here’s a simplified example:

// Rounding issue
DateTime tradeTime = DateTime.Now;
var roundedTime = tradeTime.AddMilliseconds(-(tradeTime.Millisecond));

By rounding timestamps, we unintentionally caused trades to be executed out of order. A “buy” order that should have matched with a “sell” got delayed, resulting in missed opportunities and financial losses.

What We Learned:

  • Use DateTimeOffset for accurate timestamps.
  • Never truncate or round timestamps unless absolutely necessary.

3. UTC vs Local Time

One of our most painful lessons came from a naive assumption: that all systems would use the same time zone. During a critical trading window, a developer accidentally used DateTime.Now instead of DateTime.UtcNow.

**The result? **Orders placed during that window were marked with the wrong date entirely, causing settlements to be delayed by 24 hours.

Here’s an example of how we fixed it:

// Before: Using local time
var orderTime = DateTime.Now;

// After: Using UTC
var orderTime = DateTime.UtcNow;

What We Learned:

  • Always default to UTC for backend operations.
  • Clearly document when and where local time conversions should happen.

Here What I documented for YOU!:

Why Time Handling Is Hard:

  • Time Zones Are Complex:

  • Time zones change due to political decisions.

  • Systems relying on outdated time zone data can fail spectacularly.

2. Daylight Saving Time (DST):

  • Clocks “spring forward” or “fall back,” creating non-existent or duplicate timestamps.
  • Handling recurring events during DST transitions is tricky.

3. Leap Seconds:

  • Occasionally, an extra second is added to synchronize atomic time with Earth’s rotation.
  • Most systems are not designed to handle this, leading to errors in time-sensitive applications.

4. Cultural Differences:

  • Date and time formats vary globally (MM/DD/YYYY vs. DD/MM/YYYY).
  • Ambiguities can cause parsing errors.

Best Practices for Time Handling:

1. Use the Right Data Types:

  • DateTimeOffset: Best for storing timestamps with context (e.g., offset from UTC).
  • TimeZoneInfo: Useful for converting between time zones.

Noda Time: A third-party library that simplifies complex time calculations.

2. Store in UTC:

  • Always store timestamps in UTC.
  • Convert to local time only when displaying to users.

3. Validate and Test:

  • Simulate edge cases like DST transitions and leap seconds.
  • Test across different cultures and locales.

4. Keep Time Zone Data Updated:

  • Regularly update time zone databases to reflect political changes.

5. Avoid Assumptions:

  • Don’t assume timestamps are in UTC unless explicitly specified.
  • Document the expected behavior of all time-related methods.

How We Solved It

After identifying the issues, we overhauled our system’s approach to handling time. Here’s what worked for us:

1. Standardize Time Handling

We replaced all instances of DateTime with DateTimeOffset. Unlike DateTime, which can’t always differentiate between UTC and local time, DateTimeOffset includes the offset, ensuring no ambiguity.

2. Centralized Time Management

We created a TimeProvider utility class to ensure consistent time handling across the application:

public static class TimeProvider
{
    public static DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
    public static DateTimeOffset LocalNow => DateTimeOffset.Now;
}

3. Test for Edge Cases

We wrote tests to simulate real-world scenarios, like:

  • Daylight savings transitions.
  • Leap seconds.
  • Cross-timezone order matching.

4. Educate the Team

HAHA!! Clap guys! its important that every developer understands the importance of proper time handling.

It’s not just about writing code — it’s about understanding the impact of every timestamp.

NOTES FOR YOU!!!

Always Use UTC: Store all timestamps in UTC and convert to local time only when displaying to users.

Precision Matters: Don’t round or truncate timestamps unless you’re sure it won’t impact the system.

Use DateTimeOffset: Avoid using plain DateTime for anything critical.

Test for Edge Cases: Simulate real-world scenarios, including DST changes and leap seconds.

Educate Your Team: Ensure everyone understands the risks and best practices of handling time.

Those who wanna say Thanks | Buy me a Coffee🖤

Conclusion

Time is one of the most deceptively complex aspects of building trading systems. What seems simple on the surface can be a costly mistake if not handled correctly. For us, the $200K bug was a damm alarm! It taught us to respect the value of time and build systems that can handle its edge cases.

Have you ever faced a time-related bug in your systems? Share your story in the comments below — let’s learn from each other’s experiences!