Skip to content

Instantly share code, notes, and snippets.

@hopeseekr
Created September 24, 2025 13:39
Show Gist options
  • Select an option

  • Save hopeseekr/3ac57243ad8ed282ad0075cb2fe4fa5b to your computer and use it in GitHub Desktop.

Select an option

Save hopeseekr/3ac57243ad8ed282ad0075cb2fe4fa5b to your computer and use it in GitHub Desktop.

Revisions

  1. hopeseekr created this gist Sep 24, 2025.
    318 changes: 318 additions & 0 deletions nthDay.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,318 @@
    <?php

    // Day | dateSuffixOriginal (s) | date_suffix_v5 (s) | Difference (date_suffix_v5 - dateSuffixOriginal) | % Difference
    // ----|------------|------------|------------|------------
    // Avg | 0.024745 | 0.012849 | -0.011896 | -48.06%

    const DAY_SUFFIXES = ['th', 'st', 'nd', 'rd', 'th', 'th', 'th', 'th', 'th', 'th'];
    function date_suffix_v5(int $day)
    {
    // Single comparison for 11th, 12th, 13th
    if ($day - 11 <= 2 && $day >= 11) {
    return 'th';
    }

    return DAY_SUFFIXES[$day % 10];
    }

    // Original function
    function dateSuffixOriginal($x) {
    $s = [0, "st", "nd", "rd"];
    return (in_array($x, [1, 2, 3, 21, 22, 23, 31])) ? $s[$x % 10] : "th";
    }

    // Rewritten, more readable function
    function dateSuffixReadable($day) {
    // Validate input: ensure it's a positive integer
    if (!is_numeric($day) || $day < 1 || $day != (int)$day) {
    return "th"; // Default to "th" for invalid inputs
    }

    // Array mapping last digit to suffix (1 => "st", 2 => "nd", 3 => "rd")
    $suffixes = [
    1 => "st",
    2 => "nd",
    3 => "rd"
    ];

    // Special case: numbers ending in 11, 12, 13 use "th"
    $lastTwoDigits = $day % 100;
    if ($lastTwoDigits >= 11 && $lastTwoDigits <= 13) {
    return "th";
    }

    // Get the last digit of the day
    $lastDigit = $day % 10;

    // Return the appropriate suffix or "th" if not 1, 2, or 3
    if (isset($suffixes[$lastDigit])) {
    return $suffixes[$lastDigit];
    } else {
    return "th";
    }
    }

    function dateSuffixReadable_v2(int $day)
    {
    if ($day < 1) {
    throw new \InvalidArgumentException("Days can never be negative.");
    }

    // Use a switch statement instead of multiple if/else statements
    switch ($lastDigit = $day % 10) {
    case 1:
    $suffix = "st";
    break;
    case 2:
    $suffix = "nd";
    break;
    case 3:
    $suffix = "rd";
    break;
    default:
    $suffix = "th";
    }

    if ($day === 11 || $day === 12) {

    return "th";
    }

    return $suffix;
    }

    function test_day_suffix(callable $tested_function)
    {
    // Get the function name using reflection
    $reflection = new ReflectionFunction($tested_function);
    $implementationName = $reflection->getName();

    // Test cases: array mapping days to their expected suffixes
    $testCases = [
    1 => 'st',
    2 => 'nd',
    3 => 'rd',
    4 => 'th',
    5 => 'th',
    6 => 'th',
    7 => 'th',
    8 => 'th',
    9 => 'th',
    10 => 'th',
    11 => 'th',
    12 => 'th',
    13 => 'th',
    14 => 'th',
    15 => 'th',
    16 => 'th',
    17 => 'th',
    18 => 'th',
    19 => 'th',
    20 => 'th',
    21 => 'st',
    22 => 'nd',
    23 => 'rd',
    24 => 'th',
    25 => 'th',
    26 => 'th',
    27 => 'th',
    28 => 'th',
    29 => 'th',
    30 => 'th',
    31 => 'st'
    ];

    // Test each day from 1 to 31
    for ($a = 1; $a <= 31; ++$a) {
    $suffix = $tested_function($a);
    $expectedSuffix = $testCases[$a];
    $status = ($suffix === $expectedSuffix) ? "PASSED" : "FAILED";
    echo "[$implementationName] $a: $a{$suffix} - $status (Expected: $a{$expectedSuffix})\n";
    }
    }

    // Example function to test
    function date_suffix($day) {
    // Handle special cases for 11th, 12th, 13th
    if ($day >= 11 && $day <= 13) {
    return 'th';
    }

    // Get the last digit of the day
    $lastDigit = $day % 10;

    switch ($lastDigit) {
    case 1:
    return 'st';
    case 2:
    return 'nd';
    case 3:
    return 'rd';
    default:
    return 'th';
    }
    }

    function date_suffix_v2(int $day)
    {
    // Handle special cases for 11th, 12th, 13th
    if ($day >= 11 && $day <= 13) {
    return 'th';
    }

    // Get the last digit of the day
    $lastDigit = $day % 10;

    return match ($lastDigit) {
    1 => 'st',
    2 => 'nd',
    3 => 'rd',
    default => 'th'
    };
    }

    function date_suffix_v3(int $day): string
    {
    // Handle special cases for 11th, 12th, 13th
    if ($day >= 11 && $day <= 13) {
    return 'th';
    }

    // Direct array lookup (most efficient)
    static $suffixes = [1 => 'st', 2 => 'nd', 3 => 'rd'];
    $lastDigit = $day % 10;

    return $suffixes[$lastDigit] ?? 'th';
    }

    function date_suffix_v4(int $day): string
    {
    return match (true) {
    $day % 100 >= 11 && $day % 100 <= 13 => 'th',
    $day % 10 === 1 => 'st',
    $day % 10 === 2 => 'nd',
    $day % 10 === 3 => 'rd',
    default => 'th'
    };
    }

    // Run the tests
    $implementations = [
    // 'dateSuffixOriginal',
    // 'dateSuffixReadable',
    'dateSuffixReadable_v2',
    // 'date_suffix',
    ];

    foreach ($implementations as $implementation) {
    test_day_suffix($implementation);
    }

    function benchmark_functions(callable $firstImplementation, callable $secondImplementation, bool $shortTest = true)
    {
    // Get function names using reflection
    $reflection1 = new ReflectionFunction($firstImplementation);
    $funcName1 = $reflection1->getName();

    $reflection2 = new ReflectionFunction($secondImplementation);
    $funcName2 = $reflection2->getName();

    echo "Benchmarking functions: $funcName1 and $funcName2\n";
    echo "------------------------------------------------------------\n";

    // Benchmark settings
    $iterations = 1000000; // Number of iterations for each day
    if ($shortTest === true) {
    $days = [1, 2, 3, 4, 5, 11, 12, 21, 22, 23];
    } else {
    $days = range(1, 31);
    }

    $timeData = [];

    foreach ($days as $day) {
    // Benchmark first implementation
    $start1 = microtime(true);
    for ($i = 0; $i < $iterations; $i++) {
    $firstImplementation($day);
    }
    $end1 = microtime(true);
    $timeData[$day]['func1'] = $end1 - $start1;

    // Benchmark second implementation
    $start2 = microtime(true);
    for ($i = 0; $i < $iterations; $i++) {
    $secondImplementation($day);
    }
    $end2 = microtime(true);
    $timeData[$day]['func2'] = $end2 - $start2;
    }

    // Calculate averages
    $avg1 = array_sum(array_column($timeData, 'func1')) / count($days);
    $avg2 = array_sum(array_column($timeData, 'func2')) / count($days);

    // Output results
    echo "Benchmark Results (over $iterations iterations per day, days 1-31)\n";
    echo "------------------------------------------------------------\n";
    echo "$funcName1 Average Time: " . number_format($avg1, 6) . " seconds\n";
    echo "$funcName2 Average Time: " . number_format($avg2, 6) . " seconds\n";
    echo "------------------------------------------------------------\n";

    // Detailed results per day
    // Detailed results per day
    echo "\nDetailed Results Per Day:\n";
    echo "Day | $funcName1 (s) | $funcName2 (s) | Difference ($funcName2 - $funcName1) | % Difference\n";
    echo "----|---------------|---------------|--------------------------|------------\n";

    $sumFunc1 = $sumFunc2 = $sumDiff = $sumPercent = 0.0;
    $cnt = count($days); // number of days

    foreach ($days as $day) {
    $func1 = $timeData[$day]['func1'];
    $func2 = $timeData[$day]['func2'];
    $diff = $func2 - $func1;
    $percent = $func1 != 0 ? ($diff / $func1) * 100 : 0.0;

    // keep running totals
    $sumFunc1 += $func1;
    $sumFunc2 += $func2;
    $sumDiff += $diff;
    $sumPercent += $percent;

    printf(
    " %2d | %10.6f | %10.6f | %10.6f | %8.2f%%\n",
    $day,
    $func1,
    $func2,
    $diff,
    $percent
    );
    }

    echo "----|------------|------------|------------|------------\n";

    // --- final average row ----------------------------------------------------
    $avgFunc1 = $cnt ? $sumFunc1 / $cnt : 0.0;
    $avgFunc2 = $cnt ? $sumFunc2 / $cnt : 0.0;
    $avgDiff = $cnt ? $sumDiff / $cnt : 0.0;
    $avgPercent = $cnt ? $sumPercent / $cnt : 0.0;

    echo "Avg | ";
    printf(
    "%10.6f | %10.6f | %10.6f | %8.2f%%\n",
    $avgFunc1,
    $avgFunc2,
    $avgDiff,
    $avgPercent
    );
    }

    benchmark_functions('dateSuffixOriginal', 'dateSuffixReadable');
    benchmark_functions('dateSuffixReadable', 'dateSuffixReadable_v2');
    benchmark_functions('dateSuffixReadable', 'date_suffix');
    benchmark_functions('date_suffix', 'date_suffix_v2');
    benchmark_functions('date_suffix_v2', 'date_suffix_v3');
    benchmark_functions('date_suffix_v3', 'date_suffix_v4');
    benchmark_functions('date_suffix_v3', 'date_suffix_v5');
    benchmark_functions('dateSuffixOriginal', 'date_suffix_v5', shortTest: false);