C語(yǔ)言輔導(dǎo):時(shí)間和日期

字號(hào):

時(shí)間和日期對(duì)于初級(jí)程序員可能是難以理解的,因?yàn)樗鼈儾皇呛?jiǎn)單的變量。它們包含好幾個(gè)成員。造成進(jìn)一步混淆的是,一個(gè)C編譯程序往往會(huì)提供多個(gè)處理時(shí)間的函數(shù),而它們的處理方式卻互不相同。這些函數(shù)分別應(yīng)該在什么情況下使用呢?本章試圖回答一些關(guān)于時(shí)間和日期的常見(jiàn)問(wèn)題。
     13.1 怎樣把日期存儲(chǔ)到單個(gè)數(shù)字中?有這方面的標(biāo)準(zhǔn)嗎?
     有好幾個(gè)原因使你想把日期轉(zhuǎn)換成單個(gè)數(shù)字,包括為了節(jié)省存儲(chǔ)空間或進(jìn)行簡(jiǎn)單的比較。此外,你也許想用轉(zhuǎn)換所得的數(shù)字作為編程結(jié)構(gòu)中的一部分。無(wú)論如何,如果你想用單個(gè)數(shù)字表示日期,你就需要問(wèn)一下自己為什么要這樣做,以及你想怎樣處理轉(zhuǎn)換所得的數(shù)字?;卮疬@些問(wèn)題將有助于你確定哪種轉(zhuǎn)換方法。首先,請(qǐng)看一個(gè)簡(jiǎn)單的例子:
    # include
    # include
    main ( )
    {
     int month, day, year;
     unsigned long result;
     printf( "Enter Month, Day, Year : \n");
     fflush( stdout ) ;
     scanf( "%d %d %d" , &month, &day, &year ))
     result = year;
     result | =month << 12;
     result | =day << 14;
     printf( "The result is: %ul. \n" , result );
    }
     這個(gè)程序通過(guò)位操作把三個(gè)變量轉(zhuǎn)換為單個(gè)數(shù)字,以下是它的一種運(yùn)行示例:
     Enter Month,Day,Year:
     11 22 1972
     The result is:470281.
     盡管這個(gè)程序確實(shí)能工作(你可以把它輸入計(jì)算機(jī)測(cè)試一下),但它還有一些缺陷。在進(jìn)一步討論之前,還是先指出其中的一些缺陷分別是什么。
     你想到其中的某些缺陷了嗎?以下是其中的幾種缺陷:
     ·月份、日和年份是不受限制的,這意味著它們的存儲(chǔ)區(qū)域必須比實(shí)際需要的大,而這將犧牲效率。此外,用戶可以輸入一個(gè)任意大的數(shù)值,以致超出位域的邊界,從而導(dǎo)致一個(gè)完全錯(cuò)誤的日期。
     ·由日期轉(zhuǎn)換而來(lái)的數(shù)字不是有序的,你不能根據(jù)這些數(shù)字對(duì)日期進(jìn)行比較,而這種功能卻能帶來(lái)很大的方便。
     ·各成員在轉(zhuǎn)換所得的數(shù)字中的安置是簡(jiǎn)單的,甚至是隨意的,然而把它們抽取出來(lái)卻不那么簡(jiǎn)單了(你能想出一個(gè)簡(jiǎn)單的辦法嗎?)。你真正需要的可能是一種存儲(chǔ)日期和抽取日期都比較簡(jiǎn)單的格式。
     下面我們逐個(gè)分析這些問(wèn)題。
     月份的范圍應(yīng)該從1到12,日期的范圍應(yīng)該從1到31,然而年份卻與眾不同。你可以根據(jù)你的目的把程序中要使用的年份限制在一個(gè)范圍內(nèi)。這個(gè)范圍是可以變化的,具體視程序的目的而定。有些程序需要使用的日期可能在遙遠(yuǎn)的過(guò)去,而另一些程序需要使用的日期可能在遙遠(yuǎn)的將來(lái)。然而,如果你的程序只需要使用1975到2020之間的年份,你就能節(jié)省一位存儲(chǔ)空間。顯然,在把日期轉(zhuǎn)換成數(shù)字之前,你應(yīng)該先檢查日期的各成員,以確保它們都在正確的范圍之內(nèi)。
     注意:一個(gè)考古學(xué)數(shù)據(jù)庫(kù)就是一個(gè)需要使用遠(yuǎn)古日期的很好的例子。
     在C語(yǔ)言中,通常從零開(kāi)始計(jì)算(如數(shù)組等)。在這種情況下,強(qiáng)制使所有數(shù)字的范圍都從零開(kāi)始是有好處的。因此,如果你要存儲(chǔ)的最早的年份是1975年,你應(yīng)該從所有的年份中減去1975,以使年份的序列從零開(kāi)始。請(qǐng)看改為以這種方式工作的程序:
    # include
    # include
    main()
    {
     int month, day. year;
     unsigned long result;
     / * prompt the user for input * /
     printf( "Enter Month, Day, Year: \n" ) ;
     fflush( stdout);
     scanf( "%d %d %d" , &month, &day, &year) ;
     / * Make all of the ranges begin at zero * /
     --month;
     --day;
     year - = 1975;
     / * Make sure all of the date elements are in proper range * /
     if (
     ( year <0 || year>127) || / * Keep the year in range * /
     ( month <0 || month>ll) || / * Keep the month in range * /
     ( day <0 || day>31) / * Keep the day in range * /
     )
     {
     printf( "You entered an improper date! \n" ) ;
     exit(1);
     }
     result = year;
     result | = month <<7;
     result | =day<<11;
     printf ( "The result is : %ul. \n" , result) ;
    }
     這個(gè)程序并沒(méi)有考慮到有些月份不到31天的情況,但只需作一點(diǎn)小小的改進(jìn)就可彌補(bǔ)這一缺陷。注意,當(dāng)你限制了日期的范圍后,在對(duì)月值和日值進(jìn)行移位時(shí),要少移幾位。
     上述程序所生成的數(shù)字仍然不能用來(lái)對(duì)日期進(jìn)行排序。為了解決這個(gè)問(wèn)題,你需要注意到這個(gè)數(shù)字最左邊的幾位是高于最右邊的幾位的有效位。因此,你應(yīng)該把日期中的有效部分存入最左邊的幾位中。為此,上述程序中把二個(gè)變量安置到數(shù)字中的那部分代碼應(yīng)該改為如下形式:
    result = day;
    result | = month<<5;
    result | = year<<9;
    以下是用幾個(gè)示例日期測(cè)試上述修改的結(jié)果:
     Enter Month, Day, Year :
    11 22 1980
     The result is : 110771.
     Enter Month, Day, Year:
    12 23 1980
     The result is : 116211.
     Enter Month, Day, Year;
    8 15 1998
     The result is : 74151.
     現(xiàn)在,你可以存儲(chǔ)記錄,而記錄的日期可以存成上述格式,并且可以根據(jù)轉(zhuǎn)換所得的數(shù)字對(duì)日期進(jìn)行排序,而對(duì)排序結(jié)果的正確性可以充滿信心。