Thursday 16 April 2015

Spot The Bug: The Year and Month Combination

Hi and welcome to the very first edition of Spot The Bug!

Are ya ready?!
Somewhere last year, I wrote a piece of PHP code that seemed all right initially, but somehow failed to work early January. It was a simple listing of featured birds for various months, grouped by Year and Month, starting from current Month and Year, and working its way through all records in descending order (latest first, least recent last), for 12 months. Seemed straightforward enough.

The data in MySQL: (tb_featuredbirds)
record_title record_no record_month record_year
Cuckoo 1 4 2014
Pigeon 2 4 2014
Woodpecker 1 5 2014
Eagle 1 6 2014
Owl 1 7 2014
Nightingale 2 7 2014
Tit 3 7 2014
Thrush 1 8 2014
Pelican 1 9 2014
Stork 1 10 2014
Crane 1 11 2014
Raven 1 12 2014
Ostrich 2 12 2014
Dodo 3 12 2014
Mockingbird 1 1 2015
Hummingbird 2 1 2015


The code, in PHP using the mysqli extension:
<?php

$DBConn = new mysqli($DBServer, $DBUser, $DBPass, $DBName);

$year=intval(date("YYYY",strtotime("now")));
$month=intval(date("m",strtotime("now")));

$months=1;

$current_year=0;

while ($months<=12)
{
    if ($current_year!=$year)
    {
        echo "<b><u>";
        echo $year;
        echo "</u></b>";

        $current_year=$year;
    }

    echo "<b>";
    echo date("M",strtotime($year."-".$month."-01"));
    echo "</b>"; 

    $strsql = $DBConn->prepare("SELECT record_title FROM tb_featuredbirds WHERE record_year=? and record_month=? ORDER BY record_no");
    $strsql->bind_param("ii", $year,$month);
    $strsql->execute();

    $strsql->bind_result($bird);

    echo "<ul>";

    while ($strsql->fetch())
    {
        echo "<li>" . $bird . "</li>";
    }

    echo "</ul>";

    $strsql->close();

    $month=($month==1?12:$month-1);
    $year=($month==1?$year-1:$year);

    $months++;
}
?>


The result:
2015
Jan
  • Mockingbird
  • Hummingbird
Dec
    Nov
      Oct
        Sep
          Aug
            Jul
              Jun
                May
                  Apr
                    Mar
                      Feb

                        What went wrong

                        Anything from last year and before, just didn't show up! It was January then, and only stuff that was dated January of this year, was displayed. There were no system error messages.

                        Why it went wrong

                        The error was at these lines. See the logical disconnect there?
                            $month=($month==1?12:$month-1);
                            $year=($month==1?$year-1:$year);

                        Of course, if you progress backwards, once the $month variable reaches January, the next value must be December. By that logic, once the $month variable reaches January, the $year variable should be decremented, to reflect December of the preceding year. I've highlighted the transition below.

                        Step 1: $month=1, $year=2015
                        Step 2: $month=12, $year=2014
                        Step 3: $month=11, $year=2014
                        Step 4: $month=10, $year=2014
                        .
                        .
                        .

                        But if $month variable turns to 12, then the next line, which checks if the $month variable is 1 before decrementing the $year variable, would never fire off! Instead, the progression would be more like:

                        Step 1: $month=1, $year=2015
                        Step 2: $month=12, $year=2015
                        Step 3: $month=11, $year=2015
                        Step 4: $month=10, $year=2015
                        .
                        .
                        .

                        It was all right in the middle or near the end of the current year (because the code would never have come into effect anyway), but once 2015 arrived, the logic bug kicked in. Because there were no records for the year of 2015 other than January, no records would be returned.

                        How I fixed it

                        I basically switched the two lines around, and it worked like a charm.
                            $year=($month==1?$year-1:$year);
                            $month=($month==1?12:$month-1);

                        The new result:
                        2015
                        Jan
                        • Mockingbird
                        • Hummingbird
                        2014
                        Dec
                        • Raven
                        • Ostrich
                        • Dodo
                        Nov
                        • Crane
                        Oct
                        • Stork
                        Sep
                        • Pelican
                        Aug
                        • Thrush
                        Jul
                        • Owl
                        • Nightingale
                        • Tit
                        Jun
                        • Eagle
                        May
                        • Woodpecker
                        Apr
                        • Cuckoo
                        • Pigeon

                        Moral of the story

                        Sequence matters. It matters a lot! Boy, did I feel like a noob when I debugged.

                        Thanks for joining me for Spot The Bug. Join me again next time. It's a date!
                        T___T

                        No comments:

                        Post a Comment