Skip to content

Instantly share code, notes, and snippets.

@Chemaclass
Created June 24, 2025 11:19
Show Gist options
  • Select an option

  • Save Chemaclass/65f68f2383202f528b7a550ee7478092 to your computer and use it in GitHub Desktop.

Select an option

Save Chemaclass/65f68f2383202f528b7a550ee7478092 to your computer and use it in GitHub Desktop.
Visualizes a life calendar in the terminal. Each line is a year, each character is 2 days. Tracks progress and supports leap years.
#!/usr/bin/env php
<?php
declare(strict_types=1);
/**
* Life Calendar Visualizer
*
* This script prints a visual timeline of a person's life in years and "day pairs",
* where each line represents a year, and each character represents 2 days.
* It uses `X` for days already lived and `_` for days yet to come.
*
* Usage:
* ./life-passes.php <birth-date> [max-years]
*
* Examples:
* ./life-passes.php 1993-06-15
* ./life-passes.php 15-06-1993 90
* ./life-passes.php 1993 100
*
* Supports birth date formats: YYYY-MM-DD, DD-MM-YYYY, or YYYY
* Supports leap years and provides life stats at the end.
*/
// ------------------------------------------------------------
// Helpers
// ------------------------------------------------------------
/**
* Parses a birthdate string into a DateTime object.
*/
function parseBirthDate(string $input): DateTime
{
$formats = ['Y-m-d', 'd-m-Y', 'Y'];
foreach ($formats as $format) {
$d = DateTime::createFromFormat($format, $input);
if ($d && $d->format($format) === $input) {
return $d;
}
}
fwrite(STDERR, "❌ Invalid date format. Use YYYY-MM-DD, DD-MM-YYYY, or YYYY.\n");
exit(1);
}
/**
* Parses optional max-years argument.
*/
function parseMaxYears(?string $input, int $default = 80): int
{
if ($input === null) {
return $default;
}
if (!ctype_digit($input) || (int)$input <= 0) {
fwrite(STDERR, "❌ Invalid max years. Must be a positive integer.\n");
exit(1);
}
return (int)$input;
}
/**
* Checks if a given year is a leap year.
*/
function isLeapYear(int $year): bool
{
return ($year % 4 === 0 && $year % 100 !== 0) || ($year % 400 === 0);
}
// ------------------------------------------------------------
// Core Logic
// ------------------------------------------------------------
/**
* Prints the visual matrix and life stats.
*/
function printLifeMatrix(DateTime $birthDate, int $maxYears): void
{
$today = new DateTime();
$birthYear = (int)$birthDate->format('Y');
$startOfLife = new DateTime("{$birthYear}-01-01");
$totalExpectedDays = 0;
$realDaysLived = (int)$birthDate->diff($today)->days;
echo PHP_EOL;
for ($y = 0; $y < $maxYears; ++$y) {
$year = $birthYear + $y;
$lineIndex = str_pad((string)$y, 2, ' ', STR_PAD_LEFT);
$line = "{$year}({$lineIndex}): ";
$daysInYear = isLeapYear($year) ? 366 : 365;
$charsInLine = (int)floor($daysInYear / 2);
for ($c = 0; $c < $charsInLine; ++$c) {
$dayOffset = $c * 2;
$day = (new DateTime("{$year}-01-01"))->modify("+{$dayOffset} days");
$char = ($day < $birthDate) ? '_'
: (($day <= $today) ? 'X' : '_');
$line .= $char;
}
$totalExpectedDays += $daysInYear;
echo $line . PHP_EOL;
}
echo PHP_EOL;
echo "πŸ“ˆ Life Summary\n";
echo "---------------------------\n";
echo "πŸŽ‚ Days lived: {$realDaysLived}\n";
echo "πŸ“… Days expected: {$totalExpectedDays}\n";
printf("πŸ“Š Life progress: %.2f%%\n", min(100, ($realDaysLived / $totalExpectedDays) * 100));
}
// ------------------------------------------------------------
// Entry Point
// ------------------------------------------------------------
if ($argc < 2) {
fwrite(STDERR, "Usage: ./life-passes.php <birth-date> [max-years]\n");
exit(1);
}
$birthDate = parseBirthDate($argv[1]);
$maxYears = parseMaxYears($argv[2] ?? null);
printLifeMatrix($birthDate, $maxYears);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment