🛰️ time, reimagined

BSH — the BrightShell

A zsh-compatible shell with BrightDate woven natively into its builtins, parameters, prompt escapes, and file timestamps. One sortable, timezone-free scalar — everywhere.

# date — current BrightDate (universal, no timezone)
[9622.841738] % date
9622.841738
# subtract two timestamps — no parsing, no math libs
[9622.841738] % echo $(( 9700.5 - 9622.84 ))
77.66 # days, sortable as a float
# live read-only param — updates on every access
[9622.841738] % echo $BRIGHTEPOCH
9622.841739
$ Build from source → View on GitHub

Why a BrightDate-native shell?

Shell scripts that touch time pay a hidden tax: parsing date output, juggling $TZ, sub-shelling out to gdate on macOS, fighting epoch formats that differ between stat on Linux vs BSD, and re-implementing duration math by hand. BSH replaces all of that with a single decimal scalar.

🕰️ One scalar, everywhere

BrightDate is a Float64 count of SI days since the astronomical epoch J2000.0, computed on a TAI substrate. Trivially sortable, diffable, and storable — b − a = elapsed days, no exceptions.

Problem BrightDate fix
Timezone soup Single universal value, no zones
Leap-second stutters TAI substrate — monotonic, no jumps
Duration arithmetic b - a in shell $(( … ))
Sorting timestamps Float compare; works in sort -n, awk, jq
2038 / Y10K ±287,000 years at sub-µs in current era
date portability Identical output on macOS, Linux, BSD, WSL

🐚 BSH in one line

BSH = zsh 5.10 + a statically-linked Rust core (brightdate-rust) wired into the shell's C internals through a single FFI entry point:

double bsh_unix_to_brightdate(double unix_secs);

From that one anchor, every time-related surface — builtins, parameters, prompt escapes, history, file timestamps — speaks BrightDate. Nothing parses strings. Nothing re-derives the epoch. The number that comes out of $BRIGHTEPOCH is the same number that stat prints for mtime, the same number in your prompt, and the same number you can subtract in an arithmetic expansion.

BSH stays 100% zsh-compatible: every traditional builtin, option, and module still works. BrightDate is layered on top — you opt in by loading the modules you want.

Builtins that speak BrightDate.

BSH bakes BrightDate directly into the classic Unix date/time commands. date, time, uptime, cal, and watch are shell builtins that emit BrightDate values — no fork, no external process, no $TZ dance. The traditional binaries are shadowed transparently, so existing scripts switch over for free.

built-in by default
dateCurrent date as BrightDate (replaces system date)
timeCurrent time of day in BrightDate units (replaces system time)
uptimeSystem uptime in millidays — 1 md = 86.4 s (replaces system uptime)
calCalendar in BrightDate notation (replaces system cal)
watchWatch a command; elapsed time reported in millidays (replaces system watch)
[9622.84] % date
9622.841738
[9622.84] % uptime
14.327 md # ≈ 20 minutes
[9622.84] % watch -n 2 'ls | wc -l'
Every 0.023 md: ls | wc -l

Parameters, prompts, history, files.

$BRIGHTEPOCH

From bsh/datetime. Read-only Float64 parameter holding the current BrightDate, updated on every access — the analogue of $EPOCHREALTIME. Perfect for elapsed-time arithmetic in scripts.

start=$BRIGHTEPOCH
do_work
echo "elapsed: $(( BRIGHTEPOCH - start )) md"
💬

Prompt escape %P

In any PROMPT, RPROMPT, or PS4, %P expands to the current BrightDate (6 d.p.). %B is already bold, so BrightDate claims P for "Present."

PROMPT='[%P] %# '
# renders as:
# [9622.841738] %
📜

history -t / fc -D

With EXTENDED_HISTORY set, command-execution timestamps appear as BrightDate values. fc -l -D reports command durations in millidays instead of seconds — so log analysis just becomes sort -n.

📁

ls -l, stat

From bsh/files and bsh/stat. The btime, atime, mtime, ctime fields are emitted as BrightDate decimals by default. btime (birth time) is new — populated from st_birthtimespec on platforms that expose it.

-rw-r--r-- jessica staff 1234
  b=9591.841000
  a=9591.841500
  m=9591.841200  README
⏱️

times builtin

Reports user and system CPU time (for the shell and its children) in millidays rather than the traditional m:ss format — uniform units across every time surface in BSH.

🦀

Rust core, C shell

The conversion engine is the brightdate Rust crate, statically linked into the bsh binary via a tiny FFI surface. No runtime dependencies. No subprocess overhead. Same TAI/J2000 substrate as the Rust and npm libraries.

Module index: bsh/brightdate bsh/datetime bsh/files bsh/stat

Build from source.

BSH builds with the standard autoconf workflow inherited from zsh, plus a Rust toolchain for the brightdate-rust static library.

1

Prerequisites

A C compiler (clang or gcc), make, autoconf, and a recent Rust toolchain (rustup, stable channel). macOS, Linux, and *BSD are all supported.

2

Clone with submodules

git clone --recursive https://github.com/Digital-Defiance/bsh.git
cd bsh
3

Configure & build

./configure
make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu)
make check          # optional: run the test suite
4

Install & switch shell

sudo make install
echo "$(command -v bsh)" | sudo tee -a /etc/shells
chsh -s "$(command -v bsh)"
5

Try the BrightDate surface

bsh -c 'date'                                  # BrightDate, baked in
bsh -c 'echo $BRIGHTEPOCH'
bsh -c 'PROMPT="[%P] %# "; print -P "$PROMPT"'

Looking for a configuration framework? See Oh My Bsh — 300+ plugins and 150+ themes layered on top of BSH.

The BrightDate ecosystem.

📦

npm — @brightchain/brightdate

TypeScript/JavaScript library. Same J2000.0 / TAI semantics. npm · source

🦀

crates.io — brightdate

Rust crate powering BSH internally. Also ships standalone CLI tools (bdate, btime, buptime, bcal, bwatch) — the b- prefixed versions for use outside BSH, where the native date etc. still rule. crates.io · source

🍺

Homebrew tap

The CLI tools and BSH itself are available via the Digital Defiance tap:

brew tap digital-defiance/tap
brew install bdate btime buptime bcal bwatch bsh