On Linux, all three commands use different sources of information by default.
uptime uses the information given by the kernel in
/proc/uptime. The latter contains two pieces of information: the system’s uptime, including time spent suspended, and the time spent in the idle process. These values are accurate.
who -b uses the information stored in
/var/run/utmp. On current systems, this is really
/run/utmp, and only has information for the current boot (
/run is a
tmpfs which loses its contents when the system is rebooted); but for the current boot, it is also accurate.
last reboot uses the information stored in
/var/log/wtmp. The information stored there is also generally accurate, but the information you need might no longer be stored there:
wtmp is rotated in many setups, typically monthly. This means that if the system’s current boot time is older than
wtmp’s last rotation time, the information presented will be partial. In particular,
last reboot ends up showing the last rotation time, not the system’s actual boot time. This is why
last shows the time at which
wtmp begins: that’s the time horizon for the information displayed by
wtmp contains the last boot time,
last reboot does show it:
$ last reboot | head -n 1
reboot system boot 5.10.0-8-amd64 Mon Sep 13 15:56 still running
$ who -b
system boot 2021-09-13 15:56
09:11:03 up 31 days, 17:15, 13 users, load average: 0.48, 0.34, 0.42