Timestamps, permissions, subnets, and cron
Epoch timestamps, permission bits, CIDR ranges, and cron expressions share a property: each is compact, each is read far more often than it is written, and each has semantics that differ from the obvious guess. That combination is why they account for a disproportionate share of production incidents.
Epoch time is simple; timezones are not
A Unix timestamp counts seconds since midnight UTC on 1 January 1970. It has no timezone, no locale, and no ambiguity, which is exactly why it is the right thing to store and transmit. Two systems anywhere in the world agree on what a given integer means.
Everything difficult happens at the boundaries. Converting to a human-readable string requires a timezone, and the offset for a given zone changes across the year, has changed historically by political decision, and is not knowable from the timestamp alone. Local time is not a property of the instant; it is a rendering of it.
The rule is to store UTC and render local, and to store the zone identifier rather than the offset when the zone itself matters. An offset like plus five is a fact about one moment; a zone like America/New_York is a rule that produces the right offset for every moment. Storing an offset for a future appointment gets it wrong the next time the rules change.
Two ambiguities are unavoidable. When clocks go back, a local time occurs twice and cannot be resolved to one instant without more information. When they go forward, a local time does not occur at all, so validating user input against a real instant is necessary rather than optional. Both cause duplicate or skipped scheduled work every year.
Unit confusion is the other reliable bug. Unix time is conventionally seconds; JavaScript Date and most JVM APIs use milliseconds. A seconds value fed to a milliseconds constructor produces a date in January 1970, and a milliseconds value fed to a seconds parser produces one somewhere past the year 50000. Both are obvious once seen and easy to ship.
Permission bits, and the two that surprise people
A Unix mode is three octal digits for owner, group, and everyone else, each the sum of read as four, write as two, and execute as one. Once the arithmetic is internalised, 644 and 755 are read directly rather than looked up.
The first surprise is that execute on a directory does not mean execute. It means traverse: the right to resolve a path through that directory. Read on a directory lets you list its contents; execute lets you reach anything inside. Read without execute gives a listing of names you cannot open, and execute without read lets you open files whose names you already know while hiding the listing. This is why directories are 755 rather than 644, and it is the most common cause of a permission denied on a file whose own mode is clearly correct.
The second is that permissions are checked against the first matching class only. The system tests owner, then group, then other, and stops. A mode of 077 denies the owner everything while granting everyone else full access, because the owner matches first and the owner bits are zero. Permissions do not accumulate.
Beyond the three digits there is a fourth for setuid, setgid, and the sticky bit. Setuid runs an executable as its owner and is a well-worn privilege escalation route, which is why an unexpected setuid binary is treated as an indicator of compromise. Setgid on a directory makes new files inherit the group, which is the usual way to make a shared directory work. The sticky bit restricts deletion in a world-writable directory to file owners, which is what keeps /tmp usable.
Finally, 777 is not a fix. It grants write to every process on the machine, which for a web root means any compromised service can modify the code being served. The correct answer is nearly always ownership rather than permissions.
CIDR is a prefix length, not a range
CIDR notation gives an address and a prefix length. The prefix is the number of leading bits identifying the network; the remainder identify the host. A /24 fixes 24 bits and leaves 8, giving 256 addresses.
The relationship is inverse and worth having as reflex: each bit removed from the prefix doubles the network. A /24 is 256 addresses, /23 is 512, /22 is 1024, /16 is 65,536. Going the other way, /25 is 128 and /30 is 4.
Two addresses in each ordinary subnet are unusable. All host bits zero is the network address; all ones is the broadcast address. So a /24 offers 254 usable hosts, not 256, and a /30 offers 2, which is why /30 is the traditional point-to-point link size. Two exceptions: /31 is defined for point-to-point links where the convention is waived, and /32 is a single host route.
The address in CIDR notation need not be the network address, and this causes real confusion. 192.168.1.100/24 describes an interface within the network 192.168.1.0/24. Some tools accept it, some normalise it, and some reject it, so being explicit about which you mean avoids a class of configuration error.
Cloud subnetting adds a constraint worth knowing: providers reserve additional addresses in every subnet, typically five rather than two, for the gateway, DNS, and future use. A /28 in a VPC therefore yields around 11 usable addresses rather than 14, which matters when sizing small subnets for load balancers.
Cron: five fields and one non-obvious rule
The five fields are minute, hour, day of month, month, and day of week, in that order. Reading them as a sentence is unreliable, because the syntax composes in ways that do not map onto English.
The rule that catches everyone: when both day of month and day of week are restricted, they combine with OR, not AND. An expression restricting day of month to 1 and day of week to Monday runs on the first of the month and on every Monday, not on Mondays that fall on the first. Every other pair of fields combines with AND, which makes this genuinely surprising rather than merely obscure.
Step values are computed from the start of the field range, not from the current time. A step of 5 in the minute field fires at 0, 5, 10 and so on past the hour, so a job intended to run every five minutes starting now will drift to the nearest multiple. Steps that do not divide the range evenly produce an uneven gap at the wrap point: a step of 7 in the minute field fires at 56 and then at 0, four minutes later.
Two operational properties matter more than the syntax. Cron has no concept of overlap, so a job that takes longer than its interval will have a second copy started alongside the first, which is how a slow nightly task becomes a pile-up. And cron runs in the system timezone, so a job scheduled inside a daylight saving transition is either skipped or run twice. Scheduling anything sensitive in UTC avoids the second problem entirely, and an external lock avoids the first.
Frequently asked questions
Should I store local time or UTC?
Store UTC and render local. Where the zone itself matters, store the zone identifier rather than a fixed offset, since offsets change with daylight saving and by political decision.
Why does my file have the right permissions but still fail to open?
Almost always a directory in the path lacking execute for your user. Execute on a directory means traverse, and it is required to resolve a path through it.
How many usable addresses are in a /24?
254. All host bits zero is the network address and all ones is broadcast, so two of the 256 are unusable. Cloud providers typically reserve five.
Does cron combine day of month and day of week with AND?
No, with OR when both are restricted, which is unique among the fields. The expression runs on either match rather than requiring both.
What happens if a cron job runs longer than its interval?
A second copy starts alongside the first. Cron has no overlap detection, so long-running jobs need an external lock.