Wireshark-dev: [Wireshark-dev] Re: Time and time scales in Wireshark

From: Guy Harris <gharris@xxxxxxxxx>
Date: Tue, 28 Jul 2026 02:43:31 -0700
On Jul 27, 2026, at 6:46 AM, Martin Mayer via Wireshark-dev <wireshark-dev@xxxxxxxxxxxxx> wrote:

> Let's start with the most important time scale which is the basis for our ticking clocks - the International Atomic Time (TAI). Distributed atomic clocks around the world are used to measure the SI-second. This scale only cares about the second. It does not care about day and night, the earth rotation or something else.
> 
> What brings us to the next: Universal Time 1 (UT1). This scale is aligned with astronomical observation, which means midnight must be aligned with astronomical midnight. Because the earth rotation is slowing down, in average over the past decades, an UT1 second's duration is a) longer than a SI-second and b) not constant.
> 
> An important one I want to describe is UTC. The Coordinated Universal Time which we are all used to. It combines the SI-second with astronomical observations - and this is the inherent problem because astronomical midnight is bound to the earth rotation. The UTC second's duration is the same as the SI-second's duration which leads to the fact that, over time, midnight gets coming earlier (it could also come later when earth is accelerating). To overcome this issue, UTC is aligned with astronomical observations from time to time. This happens when the difference between UT1 and UTC (called DUT1) reaches the bounds of -0.9 < DUT1 < +0.9 seconds. This is when leap seconds are added or removed to UTC.
> 
> ## Time scale arithmetic
> 
> Wireshark's internal time scale is UNIX time. This time scale counts the seconds since `1970-01-01 00:00:00 UTC` which is not totally exact. The downside is that UNIX time cannot handle leap seconds. A UNIX day strictly has 86400 seconds. When leap seconds are added or deleted, UNIX time simply pauses, repeats or jumps (there are different approaches).
> The result of this behavior is that time arithmetic is not possible because precision is lost.

It's possible in that you can subtract two time_t values.

However, what you get is *not* guaranteed to give the number of SI seconds that have elapsed between the two instants in time.

A time scale of "SI seconds that have elapsed since some epoch" *does* guarantee (modulo clock error) that a difference between time stamps will give the number of SI seconds that have elapsed between the two instants (by definition).

> Example:
> 
> UNIX time `1483225200` equals `2017-01-01 00:00:00 UTC`

Presumably you mean "UNIX time 1483228800".

> ## Calendar and user date representation
> 
> A calendar is one way to represent a certain coordinate at a time scale in an alternative format. We are all used to the Gregorian calendar which follows our personal astronomical observations and divides years into months, and months into days, and so on. It follows a proleptic scheme but has in fact nothing to do with the time scale. Gregorian dates can be applied to any time scale.
> 
> Why do I note that? Because it is important for our date and time representation in Wireshark.
> 
> If we introduce multiple time scales, we must indicate it when displaying a time. That means for example, `2026-07-26 23:59:50 UTC` is the exact same point in time as `2027-07-27 00:00:27 TAI`.
> 
> The display value is different because we express the same time in different time scales.

Time stamps are represented in the native UNIX and Windows formats as counts of time units based on the SI second since some point in the past, not as anything involving year, month, day, hour, minute, and second/fraction. Both UNIXes and Windows have routines to convert between them.

In UNIX, the count is *not* supposed to count positive leap seconds, and it would "count" negative leap seconds, so that a one-second jump from 23:59:58 UTCE to 00:00:00 UTC the next day, on a day with a negative leap second, would count as *two* seconds.

In Windows, well, there's a paper, "Implementation of UTC-compliant Leap Seconds in Microsoft Windows", which was presented at the 50th Annual Precise Time and Time Interval Systems and Applications Meeting. See

	https://www.ion.org/publications/abstract.cfm?articleID=16762

It *is* purchasable, but you need to sign up for an account (which it turned out I'd already done, a while ago) and pay USD 35 unless you're a member of the organization.

So I Downloaded It So You Don't Have To(TM), and will give you a summary so as not to infringe copyright:

The internal lowest-level Windows clock, as I understand it, supplies FILETIME values, which are "count of time units based on the SI second since some time in the past" values, the tine unit being 100 ns and the time base being "1601-01-01 00:00L00 UTC" (scare quotes because this is based on the "proleptic Gregorian calendar", per Microsoft, and presumably also "proleptic UTC", as per thinking "what the heck else *could* it be, as they didn't exactly have atomic clocks in 1601).

It appears that the clock in question has *not* counted any leap seconds that have occurred so far.

As I understand from what various documents say, my reading-between-the-lines interpretation of what they say, and some poking around on my Windows 11 machine, it appears to be the case that:

In Windows versions prior to the Microsoft Windows 10 October 2018 Update and Windows Server, , and routines such as FileTimeToSystemTime() will never return 60 as the seconds value of the time.

In those updates and all versions after it:

	If the process has *not* set the PROCESS_LEAP_SECOND_INFO_FLAG_ENABLE_SIXTY_SECOND flag, routines such as FileTimeToSystemTime() will never return 60 as the seconds value of the time.

	If the process *has* set the PROCESS_LEAP_SECOND_INFO_FLAG_ENABLE_SIXTY_SECOND flag, routines such as FileTimeToSystemTime() *will* return 60 as the seconds value of the time if the time refers to a positive leap second (and it may be that it will handle negative leap seconds as well).

See https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-process_leap_second_info for information on the PROCESS_LEAP_SECOND_INFO_FLAG_ENABLE_SIXTY_SECOND flag.

I am *not* certain what happens with the value returned by GetSystemTiemAsFileTime() and GetSystemTimePreciseAsFileTime() - does it always count leap seconds, and routines such as FileTimeToSystemTime() hide leap seconds from the application unless PROCESS_LEAP_SECOND_INFO_FLAG_ENABLE_SIXTY_SECOND has been set, or is the behavior of GetSystemTiemAsFileTime() and GetSystemTimePreciseAsFileTime() modified by PROCESS_LEAP_SECOND_INFO_FLAG_ENABLE_SIXTY_SECOND as well? I had a bit of a long conversation with the Google search AI, which claimed that if PROCESS_LEAP_SECOND_INFO_FLAG_ENABLE_SIXTY_SECOND isn't enabled, it slows the clock down around the leap second so that it counts 1 second for both the pre-leap second and the leap second, and that if it *is* enabled it counts 2 seconds, but somehow the two clocks magically sync up after that, which apparently means that FileTimeToSystemTime() will report the current FILETIME as 23:59:60.xxx during the leap second but would somehow *not* do so after the leap second. This sounds... fishy, so this may have been, to use the phrase Gerald used in the 2026 SharkFest US keynote, "answer-shaped".

I have some *vague* memory of seeing some document from Microsoft saying how KeQuerySystemTimePrecise() handles leap seconds; I suspect GetSystemTimeAsFileTime() sits atop KeQuerySystemTimePrecise(). I'll have to try to find that

Note that leap second handling can also be disabled system-wide, in which case PROCESS_LEAP_SECOND_INFO_FLAG_ENABLE_SIXTY_SECOND has no effect.


Calendar/clock representation uses year, month, day, hour, minute, second/fraction.

Calendar/clock representation of TAI always goes from 23:59:59 to 00:00:00 the next day; calendar/clock representation of UTC *alsmost* always does that, except when a leap second is inserted (23:59:59 -> 23:59:60 -> 00:00:00 the next day) or removed (23:59:58 -> 00:00:00 the next day).

> 
> This is also important for display filters.
> 
> ## Which protocols make use of it
> 
> Currently, I'm focusing on NTPv5 which introduces TAI and UT1 as new time scales. But PTP is also affected. PTP (depending on version) can transport timestamps in UTC, arbitrary or UTC-relative timescales (which could be TAI, identified by offsetUtc).

So part of the goal here is to be able to display those TAI and UT1 times?

This would mean having different types of nstime_t, indicating what scale they use. TAI and *real* UTC don't really differ, when represented as counts of time intervals since some epoch; they could both be represented as counts of SI seconds since the POSIX epoch.

To display a "counts every SI second since the POSIX epoch" nstime_t as UTC date/time for, use the IANA tzdb sample code and "right" files, which include leap second information, for 1972-01-01 00:00:00 UTC and later, and do whatever compensation for pre-1972 UTC adjustments for earlier dates (or not, and hope nobody cares, which I suspect few people will do).

To displaya "counts every SI second since the POSIX epoch" nstime_t as TAI date/time, use the tzdb code, with the "posix" files (that means files in which no leap seconds are added or subtracted, and all days are 86400 seconds long), with an appropriate epoch shift.

For UT1, it's not so simple, as UT1-UTC shifts all over the place. If we wanted to store a UT1 value in an nstime_t as we currently have it, we'd have to apply the UT1-UTC delta in effect at that time.

The other alternative would be to have some other internal representation for UT1 time stamps, and, if somebody decides to do arithmetic between a UTC value and a UT1 value, adjust one or the other - or just say "I'm sorry, Dave, I can't do that". We might also need new code to convert them to a calendar+clock display time.

> It may also affect dissectors for GNSS, because each, GPS, Galileo and BeiDou, have their own time scale.

If those just count SI second-based time units since an Epoch, that's just a "shift the epoch" problem.

If not, e.g. if the delta between them and UTC isn't just a leap-second difference, we're dealing with the same problem as UT1.

> ## Possible solutions
> 
> ### Pragmatic way
> 
> The easiest solution to implement additional time scales is to stay with the internal storage of time as UNIX timestamps. With having leap second information available, we can convert one time scale into another. What we cannot do is calculate exact differences or display UTC dates where leap-seconds were added.

...or properly show the value of the last second of December 31, 2016, i.e. "December 2, 2016, 23:59:60 UTC".

> ### Correct way
> 
> The correct way in my opinion is to implement a new basis time scale for storing timestamps which is continuous and based on SI-seconds. It can even be TAI.

My choice would be "count of SI seconds since 1970-01-01 00:00:00 UTC, plus nanoseconds", as *there's already code and data that can display that as UTC*.

That could also easily be displayed as TAI.

However, especially with modern UN*X systems providing POSIX time, in practice a lot of time stamps in packets, as well as the capture tie stamp, are in PT - POSIX Time - not UTC. Some UTC values have *no* corresponding PT values, so mapping PT values to UTC values gets tricky around the end of the days with green boxes in the table at https://en.wikipedia.org/wiki/Leap_second#Procedure.

If we're going to use nstime_t for times with different flavors of seconds, e.g. for SI-second-based time (TAI, UTC), POSIX time, UT1, possibly others, we'd have to have the nstime_t include a designation of which flavor of second is being used.