Working with Times and Dates in Zig
This small Zig program demonstrates several ways to retrieve and display the current date and time, mixing Zig’s standard library with direct calls to the C standard library — a common and powerful pattern in Zig since it has excellent C interoperability.
The program is the following:
const std = @import("std");
const c = @cImport({
@cInclude("time.h");
});
pub fn main() void {
var NOW = std.time.milliTimestamp();
std.debug.print("Current time (milliseconds): {}\n", .{NOW});
NOW = std.time.timestamp();
std.debug.print("Current time (seconds): {}\n", .{NOW});
// time_t timestamp
const now = c.time(null);
// struct tm*
const tm_ptr = c.localtime(&now);
const tm = tm_ptr.*;
const monthNames = [_][]const u8{
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December",
};
const month = tm.tm_mon; // 0 = Jan
const currentMonth = monthNames[@as(usize, @intCast(month))];
const year = tm.tm_year + 1900;
std.debug.print("Current Month: {s}\n", .{currentMonth});
std.debug.print("Current Year: {}\n", .{year});
}
The program begins by importing the Zig standard library with const std = @import("std"); and the C <time.h> header via @cImport({ @cInclude("time.h"); }), creating an alias c that lets us call any function from time.h exactly as it appears in C. Inside main(), it first uses two pure-Zig timestamp functions that require no operating-system calls or C library linkage: std.time.milliTimestamp() returns the number of milliseconds since the Unix epoch (1970-01-01 00:00:00 UTC), and std.time.timestamp() returns the same instant in seconds. These values are immediately printed, giving a high-resolution, portable view of “now” that is perfect for benchmarking or logging.
The code then switches to the classic C approach by invoking c.time(null), which returns the current Unix timestamp as a time_t (typically a 64-bit integer representing seconds since the epoch). Passing null tells the function to return the value directly rather than storing it through a pointer. This raw timestamp is fed to c.localtime(&now), which converts it into a broken-down local-time representation (adjusted for the system’s timezone and daylight-saving rules) and returns a pointer to a static struct tm. The program dereferences that pointer (tm_ptr.*) to obtain a copy of the structure for convenient field access.
To produce readable month and year output, a fixed array of month names is declared. Because the tm_mon field in struct tm is zero-based (0 = January, 11 = December), the code safely casts the value to usize with @intCast and @as before using it as an index into the array, yielding the full English month name (e.g., “November”). The tm_year field stores the number of years since 1900, so adding 1900 produces the correct Gregorian year (e.g., 2025). Finally, std.debug.print is used to output the human-readable month name and the full year.
Save the code as time.zig and execute it on your machine:
$ zig version
0.15.2
$ zig run time.zig
Current time (milliseconds): 1763615855531
Current time (seconds): 1763615855
Current Month: November
Current Year: 2025
Happy coding in Zig!