Time in a computer system is more than one number. A filesystem uses several timestamps to record access, content modification, and metadata changes, while applications must also handle UTC, time-zone rules, and historical dates. This article first explains Linux file times, stat, touch, and atime mount options, then covers Unix timestamps and IANA time zones.
1. File timestamps, system commands, and mount options
1.1 Identify four timestamps with stat
The stat command displays a file’s timestamps and inode metadata:
$ stat /var/log/syslog
File: /var/log/syslog
Size: 1048576 Blocks: 2048 IO Block: 4096 regular file
Device: 253,0 Inode: 131074 Links: 1
Access: (0640/-rw-r-----) Uid: ( 0/ root) Gid: ( 4/ adm)
Access: 2026-07-31 21:30:00.123456789 +0800
Modify: 2026-07-31 20:15:33.987654321 +0800
Change: 2026-07-31 20:15:33.987654321 +0800
Birth: 2026-07-01 08:00:00.000000000 +0800
| Field | Timestamp | Full name | Meaning | Operations that commonly update it |
|---|---|---|---|---|
Access: | atime | Access Time | Most recent access time | cat, less, grep, read() |
Modify: | mtime | Modification Time | Most recent content modification | write(), truncate(), saving in an editor |
Change: | ctime | Change Time | Most recent inode-status change | chmod, chown, link-count changes, content modification |
Birth: | birthtime | Birth/Creation Time | Time when the file was created | Recorded at creation when the filesystem supports it |
The c in ctime means change, not creation. Modifying content changes both mtime and ctime; changing permissions, ownership, or link count normally changes only ctime. Linux birthtime support depends on the kernel, filesystem, and user-space tools, so stat may display -. Ordinary file APIs generally cannot set birthtime as they can mtime, and copying data into a new file gives the copy a new creation time.
An mtime is not a trustworthy audit record either. touch -m file can change it, while rsync —archive can preserve the source file’s mtime. Security auditing requires an audit log or another record that ordinary file operations cannot easily rewrite.
GNU stat has format options that are useful in scripts:
stat -c '%Y' filename
stat -c '%W' filename
stat -c '%x %y %z %w' filename
stat -c 'mtime=%Y atime=%X ctime=%Z' /etc/passwd
%Y, %X, and %Z output mtime, atime, and ctime as Unix seconds. %W outputs birthtime in seconds and commonly returns 0 when birthtime is unavailable. The BSD stat on macOS uses -f and different format specifiers, so a portable script must handle the two implementations separately.
1.2 What atime can and cannot tell you
An atime can provide an approximate indication of when a file was last read. Some mail clients compare atime with mtime to decide whether mail has been read, temporary-file cleaners may retain active files according to access time, and cache systems may consult access time when choosing entries to evict.
Atime stores only the latest access time. It does not record who read the file, how often it was read, or its access history, and relatime and noatime reduce its precision further. Atime therefore cannot provide security forensics or compliance auditing by itself. Use Linux Audit, application logs, or the storage system’s audit facility when access records must be reliable.
1.3 strictatime, relatime, and noatime
Updating atime turns a read into an inode metadata write. Scanning logs, static assets, or large collections of small files can therefore increase write amplification. Linux offers three common mount policies:
| Mode | Update rule | Use and cost |
|---|---|---|
| strictatime | Update atime according to strict access-time semantics | Most precise, but read-heavy workloads cause more metadata writes |
| relatime | Update when atime is older than mtime or ctime, or sufficiently old | Preserves most compatibility needs while greatly reducing writes; common Linux default |
| noatime | Do not update atime on reads | Suitable for read-heavy filesystems whose software does not depend on atime |
Run findmnt -no OPTIONS /mountpoint or inspect /proc/mounts to determine the active option. Before selecting noatime, check whether mail, temporary-file cleanup, or backup software relies on access times. Use strictatime only when an application has a concrete requirement for precise atime behavior.
1.4 How file operations affect timestamps
The following table shows typical effects. Whether an atime reaches disk still depends on mount options, and an editor may save by writing a temporary file and renaming it, so the table describes common behavior rather than an absolute guarantee across every filesystem and tool.
| Operation | atime | mtime | ctime | Explanation |
|---|---|---|---|---|
echo “data” >> file | Usually unchanged | Updated | Updated | Appends in write-only mode without reading existing content |
echo “data” > file | Usually unchanged | Updated | Updated | Truncates and writes the same file |
cat file | Updated according to mount policy | Unchanged | Unchanged | Reads file content |
touch file | Updated | Updated | Updated | Sets atime and mtime, which also changes ctime |
touch -m file | Unchanged | Updated | Updated | Explicitly sets mtime |
chmod file | Unchanged | Unchanged | Updated | Changes inode metadata |
sed -i ‘s/a/b/’ file | Implementation-dependent | Updated | Updated | Common implementations read the old file and replace it |
A write-only append does not update atime merely because strictatime is active; accessing file content triggers atime handling. The final timestamps may still differ if a script reads before writing, another process reads concurrently, or a tool uses a different save procedure. A program that must preserve an earlier atime can record the timestamp and restore it with touch -a -d after writing. Lower-level programs can use O_NOATIME, subject to permission checks, to prevent their own reads from changing atime.
This experiment shows the typical result of a write-only append:
$ stat -c 'atime=%x mtime=%y ctime=%z' test.txt
atime=2026-07-31 20:00:00 mtime=2026-07-31 20:00:00 ctime=2026-07-31 20:00:00
$ echo "new line" >> test.txt
$ stat -c 'atime=%x mtime=%y ctime=%z' test.txt
atime=2026-07-31 20:00:00 mtime=2026-07-31 21:45:00 ctime=2026-07-31 21:45:00
1.5 date and the Linux time-tool ecosystem
GNU date can display, parse, and convert times:
date '+%Y-%m-%dT%H:%M:%S%z'
date -u '+%Y-%m-%dT%H:%M:%SZ'
date '+%s'
date -d @1785498540 '+%Y-%m-%d %H:%M:%S %z'
date -d '2026-07-31 21:39:00' '+%s'
TZ='America/Los_Angeles' date -d '2026-07-31 21:39:00 UTC' '+%Y-%m-%d %H:%M:%S %Z'
date -d 'now + 3 days - 2 hours' '+%Y-%m-%d %H:%M:%S'
date -d 'next monday 09:00' '+%s'
The date -d option is a GNU extension and is not part of POSIX. BusyBox and macOS provide different date syntax, so scripts cannot assume that every platform accepts the same options.
| Tool or library | Purpose | Notes |
|---|---|---|
date | Query and convert time in a shell | GNU, BusyBox, and BSD variants differ |
timedatectl | Manage system time zone, NTP, and RTC | Provided by systemd |
hwclock | Read and write the hardware clock | Distinguish UTC and local-time modes |
chronyc, ntpq | Inspect time synchronization | Report sources, offset, and drift |
tzselect | Choose an IANA time zone | Helps identify a value for TZ |
zdump | Display time-zone transition rules | Useful for diagnosing tzdata problems |
C time.h | Application-level time API | Includes interfaces such as gmtime_r and strftime |
1.6 Change file times with touch
The touch command can set atime and mtime. The kernel updates ctime when the inode state changes:
touch -m -t 202301011200.00 filename
touch -a -t 202301011200.00 filename
touch -t 202301011200.00 filename
touch -r reference_file target_file
The first command specifies only mtime, the second only atime, the third both, and the fourth copies the reference file’s atime and mtime to the target. touch cannot directly set ctime or birthtime. Do not alter inode timestamps with debugfs on a production filesystem.
2. Unix time and time-zone conversion
2.1 A Unix timestamp is a signed time axis
A Unix timestamp uses 1970-01-01T00:00:00Z as zero. Negative values represent earlier instants, so the epoch is not the beginning of representable time.
| UTC instant | Unix timestamp (seconds) | Meaning |
|---|---|---|
| 2026-07-31T13:39:00Z | 1785498540 | After the epoch |
| 1970-01-01T00:00:00Z | 0 | The epoch |
| 1949-10-01T00:00:00Z | -636249600 | Before the epoch |
| 1900-01-01T00:00:00Z | -2208988800 | Before the epoch |
Check the data type and database range before storing historical times. An unsigned integer cannot represent negative values, and signed 32-bit Unix seconds overflow in 2038, so systems commonly use a 64-bit type with a documented range. The range of MySQL TIMESTAMP depends on version and implementation; historical data may call for DATETIME or an integer timestamp according to its business meaning.
For early historical dates, the IANA database may have only approximate local rules and may return Local Mean Time (LMT). Front-end controls, serialization libraries, and monitoring tools may also mishandle negative timestamps. Test the storage, API, and display path separately when dates before 1970 matter.
2.2 Fixed offsets, abbreviations, and IANA zones
Military or NATO letter zones map letters to fixed UTC offsets. Z means UTC, while J means the observer’s local time. Except for Z as adopted by ISO 8601, these communication shortcuts contain neither daylight-saving transitions nor political rules and should not identify a user’s time zone in software.
Human-readable abbreviations such as PDT, CST, and IST are ambiguous too. CST can mean China Standard Time or Central Standard Time; IST can refer to India, Ireland, or Israel. An abbreviation is suitable only for display in an unambiguous context, not as a durable zone identifier.
Applications should use IANA names in Area/Location form, such as Asia/Shanghai, America/Los_Angeles, and Asia/Pyongyang. IANA tzdata records historical offsets, daylight-saving transitions, and rule changes for a region. A fixed +08:00 describes only an offset at an instant and cannot represent a complete time zone.
The NATO fixed-offset letters are listed below; J has no fixed offset:
| Letter | UTC offset | Letter | UTC offset |
|---|---|---|---|
| A / Alpha | +01:00 | N / November | -01:00 |
| B / Bravo | +02:00 | O / Oscar | -02:00 |
| C / Charlie | +03:00 | P / Papa | -03:00 |
| D / Delta | +04:00 | Q / Quebec | -04:00 |
| E / Echo | +05:00 | R / Romeo | -05:00 |
| F / Foxtrot | +06:00 | S / Sierra | -06:00 |
| G / Golf | +07:00 | T / Tango | -07:00 |
| H / Hotel | +08:00 | U / Uniform | -08:00 |
| I / India | +09:00 | V / Victor | -09:00 |
| K / Kilo | +10:00 | W / Whiskey | -10:00 |
| L / Lima | +11:00 | X / X-ray | -11:00 |
| M / Mike | +12:00 | Y / Yankee | -12:00 |
| Z / Zulu | ±00:00 | J / Juliet | Local time |
2.3 Time-zone rules change
North Korea changed from UTC+09:00 to UTC+08:30 on August 15, 2015, and returned to UTC+09:00 on May 5, 2018. Hard-coding the country’s zone as +08:30 makes local times after the 2018 change wrong. With Asia/Pyongyang and current tzdata, a standard library can apply the rule for the date being converted.
Daylight-saving changes, administrative boundaries, and temporary government decisions create the same problem elsewhere. Business data often needs to preserve both “the instant when an event occurred” and “the time zone selected by the user.” UTC can represent the former, while an IANA name preserves the rules needed for local display and calendar arithmetic.
2.4 Storage, transport, and display principles
A system can represent internal instants with signed 64-bit UTC timestamps or datetime types that have explicit UTC semantics. An API should emit ISO 8601, such as 2026-07-31T13:39:00Z or the offset-bearing 2026-07-31T21:39:00+08:00. If a database must reconstruct a user’s future local schedule, it should also retain the IANA zone instead of storing only the current offset.
The display layer converts an instant according to user preference. Keep tzdata current, and add regression tests for daylight-saving gaps, repeated local times, negative historical timestamps, and the 2038 boundary. UTC timestamps identify instants well, but a calendar rule such as “09:00 on the last day of every month” cannot be represented by a fixed interval in seconds.
| Scenario | Error-prone choice | Recommended choice |
|---|---|---|
| Store a time zone | PDT or integer +8 | An IANA name such as America/Los_Angeles |
| Represent UTC | 2026-07-31 13:39:00 | 2026-07-31T13:39:00Z |
| Represent a pre-1970 instant | UINT32 | A signed 64-bit type with a documented range |
| Represent US Pacific local time | Permanently hard-code UTC-7 | Use America/Los_Angeles |
| Inspect file times | Run only ls -l | Run stat filename |
| Mount a read-heavy filesystem | Select strictatime by habit | Evaluate relatime or noatime |
| Convert times in a shell | Add offsets by hand | Use TZ and a time-zone database |
A timestamp maps an instant onto a numerical axis, while time-zone rules map an instant onto a local calendar. For both filesystem and application time, define the intended meaning before choosing a data type, command, or zone rule.