.NET
DateTime
C#
programming
software development

How to truncate milliseconds off of a .NET DateTime

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Sometimes you want a DateTime rounded down to the nearest whole second for logging, grouping, or equality checks. In .NET, the safest way to do that is to create a new value whose ticks are trimmed to TimeSpan.TicksPerSecond.

That is an important distinction: formatting without milliseconds only changes how the value is displayed. Truncation changes the underlying value itself.

DateTime Stores More Than Milliseconds

DateTime is stored in ticks, where one tick is 100 nanoseconds. So a value like 12:30:15.789 is not really "stored to milliseconds"; it is stored with much finer precision.

If you want to remove the fractional second portion entirely, subtract the remainder after division by TimeSpan.TicksPerSecond.

A Good General Solution

This version truncates to the nearest whole second while preserving the original Kind:

csharp
1using System;
2
3DateTime original = DateTime.Now;
4DateTime truncated = new DateTime(
5    original.Ticks - (original.Ticks % TimeSpan.TicksPerSecond),
6    original.Kind);
7
8Console.WriteLine(original.ToString("O"));
9Console.WriteLine(truncated.ToString("O"));

Preserving Kind matters because a DateTime may represent local time, UTC, or an unspecified kind. If you rebuild the value with a constructor that ignores Kind, you can accidentally change later conversion behavior.

AddTicks Is a Nice Readable Alternative

The same logic can be expressed with AddTicks:

csharp
1using System;
2
3DateTime original = DateTime.UtcNow;
4DateTime truncated = original.AddTicks(
5    -(original.Ticks % TimeSpan.TicksPerSecond));
6
7Console.WriteLine(original);
8Console.WriteLine(truncated);

This is concise and works well when you already have the source value in hand.

Truncation Is Not the Same as Formatting

Many developers only want output without milliseconds. If so, formatting may be enough:

csharp
1using System;
2
3DateTime value = DateTime.Now;
4Console.WriteLine(value.ToString("yyyy-MM-dd HH:mm:ss"));

That prints no milliseconds, but the DateTime still contains them. If you later compare the value, serialize it in a different format, or write it to a database, the fractional seconds are still there.

Use formatting for display. Use tick trimming when the actual stored value must change.

Constructor-Based Rebuilds Work, but Be Careful

A constructor-based approach also works:

csharp
1using System;
2
3DateTime original = DateTime.Now;
4DateTime truncated = new DateTime(
5    original.Year,
6    original.Month,
7    original.Day,
8    original.Hour,
9    original.Minute,
10    original.Second,
11    original.Kind);

This is readable, but more verbose and easier to get wrong if you forget Kind or later want to truncate to a different precision.

DateTimeOffset May Be the Better Type

If your application crosses time zones, DateTimeOffset is often safer because it keeps the offset with the value. Truncation works the same idea:

csharp
1using System;
2
3DateTimeOffset original = DateTimeOffset.Now;
4DateTimeOffset truncated = original.AddTicks(
5    -(original.Ticks % TimeSpan.TicksPerSecond));
6
7Console.WriteLine(original.ToString("O"));
8Console.WriteLine(truncated.ToString("O"));

This does not change the offset. It only removes the fractional second portion.

Why Truncation Is Useful

Common cases include:

  • comparing timestamps at second precision
  • grouping log entries by whole second
  • generating stable string keys
  • normalizing values before storage or test assertions

If that is your real requirement, truncation is the correct tool. If you only want prettier output, formatting is simpler.

Common Pitfalls

The most common mistake is confusing formatting with truncation. A custom format string can hide milliseconds visually while leaving the value unchanged.

Another frequent issue is forgetting about Kind. Rebuilding a DateTime without preserving DateTimeKind.Utc or DateTimeKind.Local can create subtle bugs later when time-zone conversions happen.

Developers also mix truncation and rounding. Truncation always moves downward toward the start of the second. If you need nearest-second rounding, the logic is different.

Finally, do not normalize timestamps blindly when sub-second precision actually matters, such as event ordering or high-resolution telemetry.

Summary

  • To really remove milliseconds, change the underlying ticks, not just the display format.
  • Trimming with TimeSpan.TicksPerSecond is a clean and precise approach.
  • Preserve DateTime.Kind when constructing a new DateTime.
  • Use formatting only when you want cleaner output, not a changed value.
  • Consider DateTimeOffset when time-zone context matters.

Course illustration
Course illustration

All Rights Reserved.