  ͸
                                                                            
                                                                            
                    TARH FONKSYONLARI                                     
                                                                            
                                                   Ŀ    
  ͵    Kaan Aslan     ;
                                                    


 Bu ay tarih fonksiyonlar zerinde durmak istiyorum. Son birka haftadr bu
 konuda pekok soruyla karlatm.

 nce tarihler nasl saklanmal sorusuna yant aramalyz. Tarihleri
 "gg/aa/yy" formatnda bir karakter dizisi iinde saklamann iki tr sakncas
 var. Birincisi, byle bir saklama ekonomik olmamaz. En az 6 byte yer kaplar.
 kincisi de, byle bir saklama nmerik ilemlere elverili deildir.

 6 byte size fazla gelmeyebilir. Ancak bir veri tabanndaki binlerce kaytn
 herbirinde 8-10 tarih bulunursa, o zaman bunlarn toplamlar ciddi bir
 bykle eriecektir. Bu sebeple tarihlerin zel bir formatta saklanmalar
 uygundur. Tarih alanlarnn rnein DBase'de farkl bir trde saklandn,
 LOTUS'ta bir rakama karlk geldiini anmsayn.

 Ben tarihleri 16 bit iinde saklyorum. Yani her tarih 2 byte yer kaplyor
 benim programlarmda. Aada kullandm tarih yapsn inceleyelim.



typedef struct _DATE {
        unsigned day:   5;      /* 1 .. 31 */
        unsigned month: 4;      /* 1 .. 12 */
        unsigned year:  7;      /* 0 .. 127 = 1900 .. 2027 */
        } DATE;



 Bu tanmlamay kendi balk dosyanza yazabilirsiniz. Grld gibi
 yapnn her eleman bit alan olarak tanmlanm. Sayarsanz toplam
 16 bit. Gn iin 5, ay iin 4, yl iin 7 bit. Yl iin kullanlan
 7 bitte ancak 1900 den 2027 ye kadar olan yllar saklayabiliyoruz.
 Pekiyi 2030 yln nasl saklayabiliriz? Birka yntem bulunabilir.
 Fakat o tarih gelsin hele bir.

 Artk yllar nasl bulabiliriz? Bu bizim iimize ileride lazm olacak.
 Aslnda artk yllar her drt ylda bir deildir.
 nk dnyann gne etrafnda dnme zaman tam 365 gn, 6 saat deil.
 Dorusu 365 gn, 6 saat, 9 dakika, bilmem ka saniye.

 Aada, verilen yln artk yl olup olmadn tespit eden bir makro var.
 Neden bu bir fonksiyon deil de bir makro olarak tanmlanm? Bunu siz bulun.



#define isleap(y)     (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0)



 Makronun rettii sonu ya 1 ya 0'dr.

 Aadaki iki diziyi de kullanacaz,



static char     day_tab[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

char    *_dayS[] = {
        "Pazartesi", "Sal", "aramba", "Perembe",
        "Cuma", "Cumartesi", "Pazar"
        };




 nce ilk fonksiyonumuz bir karaker dizisini DATE trndeki yapya eviren
 atoDATE. Bu fonksiyonu iyi anlamak istiyorsanz, nce strtok ve atoi
 fonksiyonlarn dikkatle inceleyin. Bu iki fonksiyon da standarttr ve C
 ktphanelerinde mutlaka vardr.



int atoDATE(char *str, DATE *dt)
{
        char *token, *sep = "/.-";
        register int day, month, year;

        if ((token = strtok(str, sep)) != NULL) {
                day = (unsigned) atoi(token);
                if (day < 1 && day > 31)
                        return -1;
                dt->day = day;
                }
        else return -1;
        if ((token = strtok(NULL, sep)) != NULL) {
                month = (unsigned) atoi(token);
                if (month < 1 && month > 12)
                        return -1;
                dt->month = month;
                }
        else return -1;
        if ((token = strtok(NULL, sep)) != NULL) {
                year = (unsigned) atoi(token);
                dt->year = year < 100 ? year : year - 1900;
                }
        else return -1;
        return 0;
}



 Bu fonksiyonun tam tersi nedir? Tabii ki DATEtoa deil mi?



char *DATEtoa(DATE *dt)
{
        static char buf[12];

        if ((dt->day == 0 || dt->day > 31) ||
                (dt->month == 0 || dt->month > 12))
                sprintf(buf, "__/__/____");
        else
        sprintf(buf, "%02u/%02u/%4u", dt->day, dt->month, dt->year + 1900);
        return buf;
}



 Bu fonksiyonda yl bilgisi yzyl da iermektedir. Bunun alt tire
 iareti ile balayan dier bir ekli yzyl bilgisini iermiyor.



char *_DATEtoa(DATE *dt)
{
        static char buf[12];

        if ((dt->day == 0 || dt->day > 31) ||
                (dt->month == 0 || dt->month > 12))
                sprintf(buf, "__/__/__");
        else
                sprintf(buf, "%02u/%02u/%02u", dt->day, dt->month, dt->year);
        return buf;
}



 ki tarihi karlatrmak ou zaman gerekir. Aadaki fonksiyon
 tpk standart strcmp fonksiyonu gibi iki tarihi karlatrr.
 Eer bu tarihler birbirine eitse geri dn deeri 0 deilse 1 olacaktr.



int DATEcmp(DATE *d1, DATE *d2)
{
        if (d1->year != d2->year)
                return (int)((int)d1->year - (int)d2->year);
        if (d1->month != d2->month)
                return (int)((int)d1->month - (int)d2->month);
        if (d1->day != d2->day)
                return (int)((int)d1->day - (int)d2->day);
        return 0;
}



 ki tarihi kopyalamak da ok sk rastlanr. Aadaki fonksiyon
 strcpy gibi iki tarihi kopyalamaktadr. kinci parametresiyle
 belirtilen tarih, birinci parametresiyle belirtilen tarihin
 iine kopyalanacaktr. nc parametre birden fazla tarihi ayn anda
 kopyalam iin kullanlr.



DATE *DATEcpy(DATE *des, DATE *src, register unsigned nelem)
{
        register short *dp = (short *)des, *sp = (short *)src;

        while (nelem-- > 0)
                *dp++ = *sp++;
        return des;
}



 Gn, ay ve ylyla verilen bir tarihin iinde bulunduu yldaki geen
 gn says sonraki fonksiyonlarn tasarmnda kullanlmtr.



int _yday(unsigned day, unsigned month, unsigned year)
{
        register int mday, j;

        j = year;
        day_tab[1] = isleap(j) ? 29 : 28;

        for (j = month - 2, mday = 0; j >= 0; --j) {
                mday += day_tab[j];
                }
        mday += day;

        return mday;
}



 Aadaki fonksiyon 01/01/1900 den verilen tarihe kadar ka gn
 getiini bulmakta kullanlabilir. (Tpk LOTUS'taki gibi)



long relday(unsigned day, unsigned month, unsigned year)
{
        register int i;
        long days;

        days = (long)_yday(day, month, year) + 6;
        for (i = 1; i < year; ++i)
                days += isleap(i) ? 366 : 365;

        return days;
}



 Bunun tersi de olmal. 01/01/1900 den geen gn saysn da tarihe
 evirebilmeliyiz.



void rtoDATE(long relday, DATE *dt)
{
        register int i, j;

        relday -= 6;
        for (i = 1; relday > 0; ++i) {
                j = isleap(i) ? 366 : 365;
                relday -= j;
                }
        relday += j;
        yd_toDATE((int)relday, dt);
}



 ki tarihin arasnda ka gnlk fark vardr. ok nemli!



long diffDATE(DATE *cdt, DATE *odt)
{
        register int i;
        unsigned y1, y2;
        int sign, cdays, odays;
        long days = 0L;

        cdays = _yday(cdt->day, cdt->month, cdt->year + 1900);
        odays = _yday(odt->day, odt->month, odt->year + 1900);

        if (cdt->year > odt->year) {
                sign = 1;
                y2 = cdt->year;
                y1 = odt->year;
                }
        else {
                sign = -1;
                y2 = odt->year;
                y1 = cdt->year;
                }

        for (i = y1; i < y2; ++i)
                days += isleap(i) ? 366 : 365;
        if (sign == 1) days += cdays - odays;
        else days += odays - cdays;
        days *= sign;
        return days;
}



 Bir tarihten bilmem ka gn sonraki tarihi de bulmak isteyebilirsiniz.



DATE *newDATE(DATE *dt, long days)
{
        long rd;

        rd = relday(dt->day, dt->month, dt->year);
        rd += days;

        rtoDATE(rd, dt);
        return dt;
}



 Son olarak u andaki tarihi, bilgisayardan alarak bunu bizim
 DATE trne dntren getcDATE fonksiyonu. Bunun iin



void getcDATE(DATE *dt)
{
       time_t t;
       struct tm *today;

       time(&t);
       today = localtime(&t);
       dt->day = today->tm_mday;
       dt->month = today->tm_mon + 1;
       dt->year = today->tm_year;
}



 Bu fonksiyonlar aadaki ufak bir programla test edebilirsiniz.

 (Diskteki ismi DATETEST.EXE)



main()
{
       char s[12];
       char *pt;
       int tday, comp;
       long noday;
       DATE tarih1, tarih2;

clrscr();
printf("\nLtfen gg.aa.yy biiminde bir tarih giriniz ");
printf("(Ayra /.- olabilir) : ");
gets(s);
comp = atoDATE(s, &tarih1);
if (!comp)
        printf("Tarih DATE tipine dntrld (atoDATE) : %02d/%02d/%02d\n",
tarih1.day, tarih1.month, tarih1.year);
else    {
        printf("Tarih geersiz...\n");
        getch();
        exit(1);
        }

getch();
pt = _DATEtoa(&tarih1);
printf("Tarih tekrar karakter tipine dntrld (_DATEtoa) : %s\n",
pt);

getch();
pt = DATEtoa(&tarih1);
printf("Tarih bu kez gg/aa/yyyy biimine dntrld (DATEtoa) : %s\n",
pt);

getch();
tday = _yday(tarih1.day, tarih1.month, tarih1.year);
printf("Tarihin bulunduu yldaki geen gn says (_yday) : %d\n",
tday);

getch();
noday = relday(tarih1.day, tarih1.month, tarih1.year);
printf("01/01/1900'den geen gn says (relday) : %ld\n",
noday);

getch();
printf("gg/aa/yy biiminde bir tarih daha giriniz : ");
gets(s);
atoDATE(s, &tarih2);
comp = DATEcmp(&tarih1, &tarih2);
if (!comp)
        printf("Tarihler ayn (DATEcmp)\n");
else
        printf("Tarihler ayn deil (DATEcmp)\n");

getch();
noday = diffDATE(&tarih1, &tarih2);
printf("Girdiiniz iki tarih arasndaki gn fark (diffDATE) : %ld\n",
noday);

getch();
printf("01/01/1900 den geen gn saysn giriniz : ");
scanf("%ld", &noday);
rtoDATE(noday, &tarih1);
printf("Karlk gelen tarih (rtoday) : %02d/%02d/%02d\n",
tarih1.day, tarih1.month, tarih1.year);

getch();
printf("Bir rakam giriniz : ");
scanf("%ld", &noday);
newDATE(&tarih2, noday);
printf("kinci girdiiniz tarihten girdiiniz gn kadar sonras (newDATE) :
"); printf("%02d/%02d/%02d\n", tarih2.day, tarih2.month, tarih2.year);

getch();
getcDATE(&tarih2);
printf("Bugnk tarih (getcDATE) : %02d.%02d/%02d\n\n\n",
tarih2.day, tarih2.month, tarih2.year);
printf("KAAN ASLAN\n\n\n\n\n\n");
printf("FONKSYONLAR NASIL AMA ? ()Y/(K)T : ");

while ((toupper(comp = getch())) != 'I')
        getch();
}

>