adjtimex(2)
๐ธ adjtimex(2) โ System Calls Manual ๐ธ
okay so bestie, welcome to the manual page for the Linux clock-tuning system calls. This one lives in section 2 of the manual (that's the system calls section, iykyk). Grab a snack, it's a long one. ๐
โจ NAME โจ
adjtimex, clock_adjtime, ntp_adjtime โ tune kernel clock ๐ฐ๏ธโจ
๐ LIBRARY ๐
Standard C library (libc, -lc)
๐ SYNOPSIS ๐
#include <sys/timex.h>
int adjtimex(struct timex *buf);
int clock_adjtime(clockid_t clk_id, struct timex *buf);
int ntp_adjtime(struct timex *buf);
๐ DESCRIPTION ๐
So Linux runs David L. Mills' clock adjustment algorithm โ yes, the legend himself โ and if you wanna see the receipts, that's RFC 5905. ๐
The system call adjtimex() reads and optionally sets adjustment parameters for that algorithm. It takes a pointer to a timex structure, updates kernel parameters from (selected) field values, and then hands you back that same structure, all filled in with the current kernel values. It's giving read-then-write-then-read-back. ๐ฆ
That structure is declared like this, and every line matters:
struct timex {
int modes; /* Mode selector */
long offset; /* Time offset; nanoseconds, if STA_NANO
status flag is set, otherwise
microseconds */
long freq; /* Frequency offset; see NOTES for units */
long maxerror; /* Maximum error (microseconds) */
long esterror; /* Estimated error (microseconds) */
int status; /* Clock command/status */
long constant; /* PLL (phase-locked loop) time constant */
long precision; /* Clock precision
(microseconds, read-only) */
long tolerance; /* Clock frequency tolerance (read-only);
see NOTES for units */
struct timeval time;
/* Current time (read-only, except for
ADJ_SETOFFSET); upon return, time.tv_usec
contains nanoseconds, if STA_NANO status
flag is set, otherwise microseconds */
long tick; /* Microseconds between clock ticks */
long ppsfreq; /* PPS (pulse per second) frequency
(read-only); see NOTES for units */
long jitter; /* PPS jitter (read-only); nanoseconds, if
STA_NANO status flag is set, otherwise
microseconds */
int shift; /* PPS interval duration
(seconds, read-only) */
long stabil; /* PPS stability (read-only);
see NOTES for units */
long jitcnt; /* PPS count of jitter limit exceeded
events (read-only) */
long calcnt; /* PPS count of calibration intervals
(read-only) */
long errcnt; /* PPS count of calibration errors
(read-only) */
long stbcnt; /* PPS count of stability limit exceeded
events (read-only) */
int tai; /* TAI offset, as set by previous ADJ_TAI
operation (seconds, read-only,
since Linux 2.6.26) */
/* Further padding bytes to allow for future expansion */
};
Note the padding bytes at the end โ the kernel is future-proofing, main character energy fr fr. ๐
๐ The modes field ๐
The modes field is the one that decides which parameters, if any, get set. (And as we'll spill later on this page, the constants used for ntp_adjtime() are equivalent, they just got different names.) It's a bit mask โ so you take a bitwise OR of zero or more of these bits:
ADJ_OFFSETโ sets the time offset frombuf.offset. Since Linux 2.6.26, whatever value you hand over gets clamped to the range(-0.5s, +0.5s). On older kernels? Out-of-range value =EINVALerror. ๐ADJ_FREQUENCYโ sets the frequency offset frombuf.freq. Since Linux 2.6.26, the value is clamped to the range(-32768000, +32768000). On older kernels, an out-of-range value gets you anEINVALerror.ADJ_MAXERRORโ sets the maximum time error frombuf.maxerror.ADJ_ESTERRORโ sets the estimated time error frombuf.esterror.ADJ_STATUSโ sets the clock status bits frombuf.status. (Full breakdown of those bits is down below, don't worry. ๐ซถ)ADJ_TIMECONSTโ sets the PLL time constant frombuf.constant. And heads up: if theSTA_NANOstatus flag (see below) is clear, the kernel adds 4 to this value. Little sneaky bonus number, no cap.ADJ_SETOFFSET(since Linux 2.6.39) โ addsbuf.timeto the current time. Ifbuf.statusincludes theADJ_NANOflag, thenbuf.time.tv_usecgets interpreted as a nanosecond value; otherwise it's read as microseconds.
The value of buf.time is the sum of its two fields, but here's the catch: buf.time.tv_usec must always be nonnegative. Here's how you normalize a timeval with nanosecond resolution:
while (buf.time.tv_usec < 0) {
buf.time.tv_sec -= 1;
buf.time.tv_usec += 1000000000;
}
ADJ_MICRO(since Linux 2.6.26) โ selects microsecond resolution.ADJ_NANO(since Linux 2.6.26) โ selects nanosecond resolution. Only one ofADJ_MICROandADJ_NANOshould be specified, bestie. Pick a lane. ๐ADJ_TAI(since Linux 2.6.26) โ sets the TAI (Atomic International Time) offset frombuf.constant.
Do not use ADJ_TAI together with ADJ_TIMECONST, because ADJ_TIMECONST also uses the buf.constant field. They'd be fighting over the same variable and it would be a whole mess. ๐ญ
For the complete explanation of TAI and how TAI differs from UTC, see BIPM.
ADJ_TICKโ sets the tick value frombuf.tick.
๐ฆ The multibit mask values ๐ฆ
Alternatively โ and this is the plot twist โ modes can be set to either of the following (these are multibit mask values), and if you do that, you should not specify any other bits in modes:
ADJ_OFFSET_SINGLESHOTโ the old-fashionedadjtime(3): (gradually) adjusts the time by the value inbuf.offset, which specifies an adjustment in microseconds.ADJ_OFFSET_SS_READ(functional since Linux 2.6.28) โ returns (inbuf.offset) the remaining amount of time still to be adjusted after an earlierADJ_OFFSET_SINGLESHOToperation. This feature got added in Linux 2.6.24, but it was lowkey broken until Linux 2.6.28. So if you're on one of those middle versions, temperamental behavior, not your fault. ๐
๐จ Important: ordinary users are restricted to a value of either0orADJ_OFFSET_SS_READformodes. Only the superuser may set any parameters. Sorry bestie, root privileges required. ๐
๐ The buf.status field ๐
buf.status is a bit mask used to set and/or retrieve status bits tied to the NTP implementation. Some bits in the mask are both readable and settable, and some are strictly read-only. Let's go through all of them:
STA_PLL(read-write) โ enables phase-locked loop (PLL) updates viaADJ_OFFSET.STA_PPSFREQ(read-write) โ enables PPS (pulse-per-second) frequency discipline.STA_PPSTIME(read-write) โ enables PPS time discipline.STA_FLL(read-write) โ selects frequency-locked loop (FLL) mode.STA_INS(read-write) โ inserts a leap second after the last second of the UTC day, which extends the last minute of the day by one second. And it's persistent: leap-second insertion happens each day, so long as this flag stays set. ๐ โจSTA_DEL(read-write) โ deletes a leap second at the last second of the UTC day. Same deal: leap-second deletion happens each day, so long as this flag remains set.STA_UNSYNC(read-write) โ clock unsynchronized.STA_FREQHOLD(read-write) โ holds the frequency. Here's the vibe: normally, adjustments made viaADJ_OFFSETalso cause dampened frequency adjustments to happen as a side effect. So a single call fixes the current offset, but as you keep making offsets in the same direction over and over, those small frequency adjustments accumulate and fix the long-term skew. This flag stops that small frequency adjustment from being made when correcting for anADJ_OFFSETvalue. It's the "no, stay exactly as you are" button. ๐STA_PPSSIGNAL(read-only) โ a valid PPS (pulse-per-second) signal is present.STA_PPSJITTER(read-only) โ PPS signal jitter exceeded.STA_PPSWANDER(read-only) โ PPS signal wander exceeded.STA_PPSERROR(read-only) โ PPS signal calibration error.STA_CLOCKERR(read-only) โ clock hardware fault. ๐STA_NANO(read-only; since Linux 2.6.26) โ resolution (0= microsecond,1= nanoseconds). Set viaADJ_NANO, cleared viaADJ_MICRO.STA_MODE(since Linux 2.6.26) โ mode (0= Phase Locked Loop,1= Frequency Locked Loop).STA_CLK(read-only; since Linux 2.6.26) โ clock source (0= A,1= B); currently unused.
โ ๏ธ Attempts to set read-only status bits are silently ignored. No error, no drama, it just doesn't happen. Not me finding that out the hard way... ๐
๐ธ clock_adjtime() ๐ธ
The clock_adjtime() system call (added in Linux 2.6.39) behaves just like adjtimex(), except it takes an extra clk_id argument to specify the particular clock you want it to act on. Choose your fighter. โฐ
๐ง ntp_adjtime() ๐ง
The ntp_adjtime() library function (described in the NTP "Kernel Application Program API", a.k.a. KAPI) is the more portable interface for doing the exact same task as adjtimex(). Other than the following points, it is identical to adjtimex():
- The constants used in
modesare prefixed with"MOD_"rather than"ADJ_", and they have the same suffixes (thus,MOD_OFFSET,MOD_FREQUENCY, and so on) โ other than the exceptions noted in the following points. MOD_CLKAis the synonym forADJ_OFFSET_SINGLESHOT.MOD_CLKBis the synonym forADJ_TICK.- The is no synonym for
ADJ_OFFSET_SS_READ, which is not described in the KAPI.
okay that's it for part 1 bestie โ part 2 is coming right up, don't go anywhere ๐
๐ RETURN VALUE ๐
okay so bestie, on success adjtimex() and ntp_adjtime() hand you back the clock state, which is one of these iconic little values:
TIME_OKโ clock is synchronized, nothing leap-second-y is pending. we're chill. ๐TIME_INSโ heads up, a leap second is gonna get inserted at the end of the UTC day.TIME_DELโ a leap second is gonna get deleted at the end of the UTC day. bye bestie ๐TIME_OOPโ the leap second insertion is literally in progress right now, mid-oop.TIME_WAITโ the leap-second insertion or deletion just finished. this value keeps coming back until the nextADJ_STATUSoperation clears theSTA_INSandSTA_DELflags.TIME_ERRORโ the system clock is not synchronized to a reliable server. you get this whenever any of the following is true, no cap:- either
STA_UNSYNCorSTA_CLOCKERRis set. STA_PPSSIGNALis clear and eitherSTA_PPSFREQorSTA_PPSTIMEis set.STA_PPSTIMEandSTA_PPSJITTERare both set.STA_PPSFREQis set and eitherSTA_PPSWANDERorSTA_PPSJITTERis set.
- either
The symbolic name TIME_BAD is a synonym for TIME_ERROR, kept around for backward compatibility. legacy queen, we respect her ๐
heads up bestie: starting with Linux 3.4, the call operates asynchronously, so the return value usually will not reflect a state change caused by the call itself. it's giving "don't @ me, check back later" โจ
On failure, these calls return -1 and set errno to show you what went wrong.
๐ญ ERRORS ๐ญ
EFAULTโbufdoes not point to writable memory. she cannot write there, bestie, that memory said no.EINVALโ (before Linux 2.6.26) an attempt was made to setbuf.freqto a value outside the range(-33554432, +33554432).EINVALโ (before Linux 2.6.26) an attempt was made to setbuf.offsetto a value outside the permitted range. Before Linux 2.0, the permitted range was(-131072, +131072). From Linux 2.0 onwards, the permitted range was(-512000, +512000).EINVALโ an attempt was made to setbuf.statusto a value other than those listed above. (you tried to make up your own status flag? not on this kernel's watch ๐ )EINVALโ theclk_idgiven toclock_adjtime()is invalid, for one of two reasons. Either the System-V style hard-coded positive clock ID value is out of range, or the dynamicclk_iddoes not refer to a valid instance of a clock object. Seeclock_gettime(2)for a discussion of dynamic clocks.EINVALโ an attempt was made to setbuf.tickto a value outside the range900000/HZto1100000/HZ, whereHZis the system timer interrupt frequency.ENODEVโ the hot-pluggable device (like USB for example) represented by a dynamicclk_idhas disappeared after its character device was opened. so rude. Seeclock_gettime(2)for a discussion of dynamic clocks.EOPNOTSUPPโ the givenclk_iddoes not support adjustment. it's just not that kind of clock, bestie.EPERMโbuf.modesis neither0norADJ_OFFSET_SS_READ, and the caller does not have sufficient privilege. Under Linux, theCAP_SYS_TIMEcapability is required. ๐
๐ ATTRIBUTES ๐
for an explanation of the terms used in this section, see attributes(7).
- Interface:
ntp_adjtime()โ Attribute: Thread safety โ Value:MT-Safe
(she is MT-Safe, she is unbothered, she is thriving ๐ซถ)
๐ STANDARDS ๐
adjtimex()clock_adjtime()โ both are Linux.
The preferred API for the NTP daemon is ntp_adjtime(). periodt.
๐ง NOTES ๐ง
In struct timex, freq, ppsfreq, and stabil are ppm (parts per million) with a 16-bit fractional part, which means that a value of 1 in one of those fields actually means 2^-16 ppm, and 2^16=65536 is 1 ppm. This is the case for both input values (in the case of freq) and output values. so tiny. so precise. math girlie behavior โจ
The leap-second processing triggered by STA_INS and STA_DEL is done by the kernel in timer context. Thus, it will take one tick into the second for the leap second to be inserted or deleted. lowkey a whole tick of patience required ๐ฐ๏ธ
๐ฆ SEE ALSO ๐ฆ
clock_gettime(2), clock_settime(2), settimeofday(2), adjtime(3), ntp_gettime(3), capabilities(7), time(7), adjtimex(8), hwclock(8)
NTP "Kernel Application Program Interface"
and that's the whole vibe bestie, we ate that entire page and left no crumbs ๐ this one's from Linux man-pages 6.20, dated 2026-02-08 โ go off, timekeepers, you absolute main characters ๐
๐ฆ where this page comes from
- arch rolling: man-pages 6.20-1 ยท BSD-2-Clause AND BSD-3-Clause AND BSD-4.3TAHOE AND BSD-4-Clause-UC AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later AND GPL-3.0-or-later AND LGPL-3.0-linking-exception AND LGPL-3.0-or-later AND LicenseRef-Public-Domain AND LicenseRef-UltraPermissive AND Linux-man-pages-1-para AND Linux-man-pages-copyleft AND Linux-man-pages-copyleft-2-para AND Linux-man-pages-copyleft-var AND MIT AND Spencer-94
- nixpkgs unstable: man-pages 6.20
- ๐ the page's license: GPL-2.0-or-later
- Copyright 1995, Michael Chastain <mec@shell.portal.com>
- Copyright 2014-2016, Michael Kerrisk <mtk.manpages@gmail.com>
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms