Üzərində işlədiyimiz proyektlərin birində PHP vasitəsilə istifadəçinin hər ay etdiyi ödənişlərini verilmiş aralıqda təqvim formasında çıxartmalı idik. Ödənişlər haqqında xüsusi servisdən əldə edilən məlumatlarda yalnız istifadəçinin ödəniş etdiyi ay və məbləğ qaytarılırdı. Yəni məsələn, bir yanvar ayında və bir də aprel ayında ödəniş edilibsə, servisdən ancaq iki – yanvar və aprel – ayı haqqında məlumat qaytarılırdı.
Təqvimi göstərmək üçün əvvəlcə gərək verilmiş aralıqda ardıcıl olaraq ilin ayları müəyyən edilsin, sonra həmən aylar servisdən gələn məlumatla tutuşdurulub ödəniş varsa həmən ayda edilmiş ödəniş, yoxdursa 0 çap edilsin.
Beləliklə, istifadəçi proqramda başlanğıc və son ayları daxil edir. Qurulmuş məntiqə görə skript dövrü işə salır, başlanğıc ayın üzərinə hər dəfə bir ay gələrək alınan nəticənin sonuncu aya bərabər olub olmadığını yoxlayır və beləliklə aylar ardıcıllığını əldə etmiş olur.
Hər şey hazırlanıb iş təhfil veriləndən sonra maraqlı hallar baş verməyə başladı. Belə ki, php skript hərdən işini bitirə bilmir və verilmiş taymautdan sonra işini bitirməmiş dayandırılırdı. Skripti tələsik, sadə debaq etdikdən sonra məlum oldu ki, proqram sonsuz dövrə düşür və nəticədə PHP-də tarixlərlə işləyən standart funksiyalarda xəbərimiz olmayan bug aşkar etmiş olduq.
Növbəti aylar PHP-in strtotime
funksiyasına başlanğıc ay string
formasında və yanına +n month
yazısı əlavə etməklə müəyyən edilir. Məsələn: strtotime("2018-05-10 +1 month")
verilmiş tarixdən bir ay sonrakı təqvim gününü timestamp formasında qaytarmalıdır. Verilmiş başlanğıc ayından sonra 20 ayı çap edən skript aşağıdakı kimidir:
$begin_month = 1; $begin_day = 10; $begin_year = 2018; $date_string = date("Y-m-d", mktime(0, 0, 0, $begin_month, $begin_day, $begin_year)); for ($i = 1; $i < 20; ++$i) { $next_month = date("m/Y", strtotime($date_string . " +" . $i . " month")); print $next_month . '<br>'; }
Nəticə gözəldir:
02/2018 03/2018 04/2018 05/2018 06/2018 07/2018 08/2018 09/2018 10/2018 11/2018 12/2018 01/2019 02/2019 03/2019 04/2019 05/2019 06/2019 07/2019 08/2019
Amma başlanğıc tarixdə $begin_month = 12; $begin_day = 31; $begin_year = 2018;
kimi nəzərə alsaq nəticə çox gözlənilməz olur:
01/2019 03/2019 03/2019 05/2019 05/2019 07/2019 07/2019 08/2019 10/2019 10/2019 12/2019 12/2019 01/2020 03/2020 03/2020 05/2020 05/2020 07/2020 07/2020
Göründüyü kimi, strtotime funksiyası +n month
əlavəsi ilə özünü normal aparmır. Proqramlaşdırma zamanı bu xətanı müəyyən etdiyimizdən və bir az araşdırmalardan sonra tarixi PHP-in standart DateTime
obyekti ilə hesablamağa qərar vermişdik:
$begin_month = 1; $begin_day = 10; $begin_year = 2018; $mk_date = date('U', mktime(0, 0, 0, $begin_month, $begin_day, $begin_year)); $date = new \DateTime('@' . $mk_date); for ($i = 1; $i < 20; ++$i) { $date->modify('+1 month'); print $date->format('m/Y') . '<br>'; }
Nəticə:
02/2018
03/2018
04/2018
05/2018
06/2018
07/2018
08/2018
09/2018
10/2018
11/2018
12/2018
01/2019
02/2019
03/2019
04/2019
05/2019
06/2019
07/2019
08/2019
Gözəldir, hər şey işləyir və testlərdən sonra proqramı bu formada işə buraxmışdıq. Ancaq, sonradan məlum oldu ki, məsələn başlanğıc tarixdə $begin_month = 12; $begin_day = 31; $begin_year = 2018;
kimi nəzərə alsaq nəticə yenə də gözlənilməz olur:
01/2019
03/2019
04/2019
05/2019
06/2019
07/2019
08/2019
09/2019
10/2019
11/2019
12/2019
01/2020
02/2020
03/2020
04/2020
05/2020
06/2020
07/2020
08/2020
Göründüyü kimi, verilən məsələdə DateTime
obyekti özünü strtotime funksiyasına nisbətən normal aparsa da, hazırki nümunədə vefral ayı çap edilməyib. İstifadəçi də təsadüfən fevral ayına qədər nəticəni görmək istədiyəndə proqram fevral ayını nəzərə almadığına görə sonsuz dövr alınır.
Nisbətən uzun araşdırmalardan və testlərdən sonra normal nəticəni DateTime
obyektinin modify
metoduna 'last day of next month'
parametrini ötürməklə ala bildik:
$begin_month = 12; $begin_day = 31; $begin_year = 2018; $mk_date = date('U', mktime(0, 0, 0, $begin_month, $begin_day, $begin_year)); $date = new \DateTime('@' . $mk_date); for ($i = 1; $i < 20; ++$i) { $date->modify('last day of next month'); print $date->format('m/Y') . '<br>'; }
Nəticə gözəldir və uzun testlərdən sonra həqiqətən də normal işlədiyinə əmin olduq:
01/2019
02/2019
03/2019
04/2019
05/2019
06/2019
07/2019
08/2019
09/2019
10/2019
11/2019
12/2019
01/2020
02/2020
03/2020
04/2020
05/2020
06/2020
07/2020
Göründüyü kimi, PHP-nin imkanlarını əzbər bilsək də qurduğumuz məntiqin düzgünlüyünə əmin olmağımız hələ proqramın düzgün nəticə verəcəyinə 100% dəlalət etmir. PHP-in rəsmi saytında komentlərdə bu bug barəsində az-çox məlumat da var.
Sizin də rast gəldiyiniz belə bug-lar varsa komentə yazın.
Əla məqalədir. Əllərinizə sağlıq.