Date-Based Expiring Code Blocks in Nix
Published on
Oftentimes, in a NixOS configuration, you want to introduce temporary changes that aren’t meant for long term: for example, you want to run a new version of a software product before it’s in nixpkgs, or apply your own temporary patches, maybe even, use an API token that will expire eventually1.
I’ve recently read the article Nix Overrides That Expire Themselves by Jezen Thomas, in which the author comes up with a handy trick: make NixOS overlays automatically expire themselves, once they aren’t needed.
It’s something I would have needed many times before, and is much better than the alternative: scattering TODO comments around the code, in the hopes that some day, when the code isn’t needed anymore, it’ll be manually removed.
Their method for expiring an override is based on version numbers, but I think date-based expiration fits my needs better. So I set out to create code blocks with an expiration date in Nix.
Getting the date
Nix has builtins.currentTime, but of course, you can’t use it in pure evaluation mode; that would defeat the point of pure eval.
I use flakes and pure eval, so I was looking for a solution compatible with these.
The good news is: we have a proxy! Any flake input has a lastModified attribute that stores the last modification date of the input, and this is available in pure mode.
Because nixpkgs-unstable updates frequently, nixpkgs.lastModified is a good proxy for the current date.
Converting dates to timestamps
One problem: lastModified is a Unix timestamp, so to compare it with an expiration date, we first need to convert the date to a timestamp.
Thankfully, I’ve found a paper which presents many methods for converting dates to timestamps, and back2: Euclidean affine functions and their application to calendar algorithms by Cassio Neri and Lorenz Schneider.
Incidentally, an algorithm from that paper is used by the linux kernel itself.
The code
So, based on these, here’s the full code:
let
# algorithm 13 from https://onlinelibrary.wiley.com/doi/10.1002/spe.3172
date-to-nr-days-since-epoch =
year: month: day:
let
s = 82;
K = 719468 + 146097 * s;
L = 400 * s;
J = if month <= 2 then 1 else 0;
uint32_cast = x: if x < 0 then x + 4294967296 else x;
Y = uint32_cast year + L - J;
M = if J > 0 then month + 12 else month;
D = day - 1;
C = Y / 100;
y_star = 1461 * Y / 4 - C + C / 4;
m_star = (979 * M - 2919) / 32;
N = y_star + m_star + D;
in
N - K;
date-to-unix-timestamp =
year: month: day:
(date-to-nr-days-since-epoch year month day) * 86400;
/**
usage: expiringCodeBlock lastModified year month day code
where
lastModified: unix timestamp (eg. nixpkgs.lastModified)
year, month, day: integers
*/
expiringCodeBlock =
lastModified: year: month: day: code:
if lastModified <= (date-to-unix-timestamp year month day) then
code
else
throw "EXPIRED CODE BLOCK!\nThis code block expired on ${toString year}-${toString month}-${toString day}";
in
expiringCodeBlock
You can use this to expire any code block, after a given date. Applying it to the original problem, overlays:
nixpkgs.overlays = [
(expiringCodeBlock nixpkgs.lastModified 2026 09 01 (
final: prev: {
hello = throw "Hello world is disabled temporarily for maintenance!";
}
))
];