php february problem

PHP-də fevral problemi

Ü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.

One thought on “PHP-də fevral problemi”

Leave a Reply