GDate
je struktura knihovny GLib reprezentující den od 1. 1. roku 1 po několik tisíc let v budoucnosti. (Přesný limit není stanoven. Samotná struktura GDate
zvládne uložit i rok 65535, ale funkce g_date_set_parse()
umí přečíst datum zhruba jen do roku 8000.) GDate
neukládá astronomická nebo historická data. Pro to nemá dispozice. Hodí se jedině tehdy, chcete-li pracovat s obyčejnými („každodenními“) letopočty. Mechanismus akceptuje jak formát den-měsíc-rok, tak Juliánskou reprezentaci (počet dnů od 1. ledna roku 1). (Technicky vzato, termín "Juliánský kalendář zde taky není na místě. Juliánský letopočet začal roku 4713 př. Kr. V dalším výkladu tento termín budeme ale chápat tak, jak jsme si jej definovali.)
Při práci s GDate
jsem přišel na jednu nesrovnalost. Mechanismus neošetřuje speciální událost v roce 1752, kdy proběhla Gregoriánská reforma kalendáře. Lidé si uvědomili, že se jim kalendář rozchází s astronomickými pozorováními a upravili výpočet přestupných let. V té době se však už „lidský“ kalendář zpožďoval o 11 dnů. V září roku 1752 proto učinili skok a kritických 11 dnů vypustili. Po středě 2. září 1752 následoval čtvrtek 14. září 1752. No a právě tento fakt GDate
ignoruje.
Struktura GDate
je bitové pole a na 64-bitových platformách může dojít k jejímu sbalení do jednoho velkého intu, což je z hlediska efektivity jistě velice příjemné. Pro přístup k položkám struktury by se měly používat výhradně funkce, které si v dnešním článku popíšeme.
Práce s časem (intermezzo)
Práce s časem v knihovně GLib se omezují jen na získání aktuálního času a se strukturou GDate
nemá nic společného:
void g_get_current_time(GTimeVal *result); struct GTimeVal { glong tv_sec; glong tv_usec; };
Struktura GTimeVal
reprezentuje precizní čas se sekundami a mikrosekundami. Struktura je stejná jako timeval
vracená unixovým voláním gettimeofday()
. Jediný rozdíl je v tom, že g_get_current_time()
funguje i na platformách Win32.
V rozhraní knihovny je ještě definován typ
typedef gint32 GTime;
…což je obyčejná náhrada typu time_t
.
Vytvoření nového GDate
GDate* g_date_new(void);
…alokuje novou strukturu GDate
a nastaví ji do „nedefinovaného“ (neplatného) stavu.
GDate* g_date_new_dmy(GDateDay day, GDateMonth month, GDateYear year);
…alokuje nový GDate
a uloží do něj zadané datum. Typy argumentů jsou definovány takto:
typedef enum { G_DATE_BAD_MONTH = 0, G_DATE_JANUARY = 1, G_DATE_FEBRUARY = 2, G_DATE_MARCH = 3, G_DATE_APRIL = 4, G_DATE_MAY = 5, G_DATE_JUNE = 6, G_DATE_JULY = 7, G_DATE_AUGUST = 8, G_DATE_SEPTEMBER = 9, G_DATE_OCTOBER = 10, G_DATE_NOVEMBER = 11, G_DATE_DECEMBER = 12 } GDateMonth; typedef guint8 GDateDay; typedef guint16 GDateYear;
Podobně jako GDateMonth
má konstantu G_DATE_BAD_MONTH
označující chybný měsíc, i GDateDay
a GDateYear
mají definována makra G_DATE_BAD_DAY
a G_DATE_BAD_YEAR
, kterými můžete označovat nesprávné hodnoty.
GDate* g_date_new_julian(guint32 julian_day);
…vytvoří nový GDate
a uloží do něj datum podle atributu julian_day
, což je počet dnů od prvního dne našeho letopočtu ( julian_day = 1
pro datum 1. 1. 1). Nedefinovaná Juliánská hodnota se označuje konstantním makrem G_DATE_BAD_JULIAN
.
Pokud GDate
nealokujete na haldě, ale vytváříte je jako lokální proměnné na zásobníku nebo jako pole, měli byste je vždy ihned alespoň vynulovat, aby dostaly „nedefinovanou“ („neplatnou“), ale pro mechanismus zcela korektní hodnotu:
void g_date_clear(GDate *date, guint n_dates);
…funkce inicializuje jednu nebo více struktur GDate
, na které ukazuje pointer date
. Atribut n_dates
je počet datových prvků v poli. Například:
GDate datum; GDate pole_datumu[5]; g_date_clear(&datum, 1); g_date_clear(&pole_datumu, 5);
Pro úplnost ještě uvedu další výčtové typy spřízněné s mechanismem GDate
:
typedef enum { G_DATE_DAY = 0, G_DATE_MONTH = 1, G_DATE_YEAR = 2 } GDateDMY; typedef enum { G_DATE_BAD_WEEKDAY = 0, G_DATE_MONDAY = 1, G_DATE_TUESDAY = 2, G_DATE_WEDNESDAY = 3, G_DATE_THURSDAY = 4, G_DATE_FRIDAY = 5, G_DATE_SATURDAY = 6, G_DATE_SUNDAY = 7 } GDateWeekday;
První z nich ( GDateDMY
) není knihovnou používán, ale vám může být užitečný pro poznámku k číslu, že jde o den, měsíc nebo rok. Ve druhém výčtovém typu jistě poznáváte dny v týdnu. Také v něm je definována konstanta pro neplatné označení dne v týdnu – G_DATE_BAD_WEEKDAY
.
Nastavení data
Pro uložení data do struktury GDate
máte k dispozici celou sadu funkcí. Význam jejich názvů a parametrů je natolik zřejmý, že jistě nepotřebují žádný další komentář:
void g_date_set_day(GDate *date, GDateDay day); void g_date_set_month(GDate *date, GDateMonth month); void g_date_set_year(GDate *date, GDateYear year); void g_date_set_dmy(GDate *date, GDateDay day, GDateMonth month, GDateYear y); void g_date_set_julian(GDate *date, guint32 julian_date);
Funkce nastaví příslušné položky data. Předávané argumenty by samozřejmě měly být platné. Pokud výsledná trojice den-měsíc-rok uložená ve struktuře GDate
je neplatná (např. 31. února 2001), stane se neplatným celé datum date
.
Chcete-li do GDate
uložit datum z hodnoty typu GTime
( time_t
), použijte funkci
void g_date_set_time(GDate *date, GTime time);
Například dnešní datum získáte voláním:
g_date_set_time(date, time(NULL));
Chcete-li datum získat čtením z řetězce znaků, použijte funkci
void g_date_set_parse(GDate *date, const gchar *str);
Funkce g_date_set_parse()
se při své práci ohlíží na locales a pokouší se uhodnout, jakým způsobem je datum zapsáno. Nutno ale podotknout, že její výsledky nejsou nikterak oslnivé. Často se stane, že naprosto obyčejný formát data nepochopí – zvláště pak datumy napsané tak, jak je v české kotlině zvykem. A to je škoda.
Kalendářová matematika
S datem uloženým v GDate
můžete provádět jednoduché matematické operace (podmínkou je, že upravované datum ( date
) musí být platné):
void g_date_add_days(GDate *date, guint n_days); void g_date_add_months(GDate *date, guint n_months); void g_date_add_years(GDate *date, guint n_years);
…přičítání dnů, měsíců a let. Chcete raději odčítání? Prosím:
void g_date_subtract_days(GDate *date, guint n_days); void g_date_subtract_months(GDate *date, guint n_months); void g_date_subtract_years(GDate *date, guint n_years);
Příklad:
date = g_date_new_dmy(29, G_DATE_FEBRUARY, 2000); g_date_add_years(date, 5); /* v date je nyni 28. 2. 2005 */
Hodnoty GDate
lze i porovnávat:
gint g_date_compare(GDate *lhs, GDate *rhs);
Datumy lhs
a rhs
musí být platné (definované). Funkce g_date_compare()
vrátí nulu, pokud si jsou datumy rovny, číslo menší než nula, pokud lhs
je menší než rhs
, a číslo větší než nula v opačném případě.
Získávání hodnot z GDate
Hodnoty jednou uložené do GDate
je pochopitelně možné vytáhnout zpět – viz následující funkce:
GDateDay g_date_day(GDate *date); GDateMonth g_date_month(GDate *date); GDateYear g_date_year(GDate *date);
…vrátí složku dne, měsíce nebo roku data date
.
guint32 g_date_julian(GDate *date);
…vrátí počet dnů od 1. 1. roku 1 po datum date
. (Hodnota se tuším od skutečného počtu liší o 11 dnů – viz problém roku 1752.)
GDateWeekday g_date_weekday(GDate *date);
…vrátí den v týdnu (pondělí, úterý, …) náležející datu date
.
guint g_date_day_of_year(GDate *date);
…výsledkem je pořadí dne určeného datem date
v roce. Např.: 5. únor 2001 je 36. dnem v roce 2001. (29. únor v přestupných letech je samozřejmě zohledněn.)
Další funkce
guint8 g_date_days_in_month(GDateMonth month, GDateYear year);
…vrátí počet dnů v měsíci month
v roce year
. Např. leden 2001 – 31 dnů.
gboolean g_date_is_first_of_month(GDate *date); gboolean g_date_is_last_of_month(GDate *date);
… TRUE
, jestliže je date
prvním (posledním) dnem v měsíci.
gboolean g_date_is_leap_year(GDateYear year);
… TRUE
, jestliže je rok year
přestupným rokem.
guint g_date_monday_week_of_year(GDate *date); guint g_date_sunday_week_of_year(GDate *date);
…výsledkem funkcí je pořadí týdne v roce, do kterého datum date
spadá, bere-li se za počátek týdne pondělí- monday
(neděle- sunday
). Jestliže se datum date
nachází před prvním pondělím (nedělí) v roce, vrátí nulu.
guint8 g_date_monday_weeks_in_year(GDateYear year); guint8 g_date_sunday_weeks_in_year(GDateYear year);
…tyto funkce vypočítají, kolik týdnů má rok year
, chápeme-li jako počátek týdne pondělí (neděli). Rok má vždy 52 nebo 53 týdnů. Tyto funkce nám sdělí, kolik pondělí (nedělí) se v roce year
naskytne.
Chcete-li datum zkonvertovat do řetězce, není nic jednoduššího. Prostě použijte funkci
gsize g_date_strftime(gchar *s, gsize slen, const gchar *format, GDate *date);
Rutina g_date_strftime()
uloží do textového bufferu s
, jehož velikost je slen
, řetězec s datem date
podle formátu format
. Při konverzi se berou v úvahu uživatelovy locales. Funkce vrací počet znaků zapsaných do bufferu s
, nebo 0, pokud je buffer příliš malý. Formát format
je stejný jako u funkce strftime()
. Akceptují se ale jen řídicí sekvence pro datum, „časové“ formáty produkují nedefinované výsledky.
void g_date_to_struct_tm(GDate *date, struct tm *tm);
…uloží datum date
do struktury tm
. Časové položky vyplní nesmyslnými, ale korektními hodnotami.
Kontroly platnosti
Poslední sadou funkcí pro práci s datem v knihovně GLib jsou funkce, které kontrolují platnost datumových údajů. Názvy funkcí i jejich parametry jsou samovysvětlující – každý jistě správně odhadne, co která funkce dělá:
gboolean g_date_valid(GDate *date); gboolean g_date_valid_day(GDateDay day); gboolean g_date_valid_month(GDateMonth month); gboolean g_date_valid_year(GDateYear year); gboolean g_date_valid_dmy(GDateDay day, GDateMonth month, GDateYear year); gboolean g_date_valid_julian(guint32 julian_date); gboolean g_date_valid_weekday(GDateWeekday weekday);
Uvolnění GDate
z paměti
Máte-li struktury GDate
alokovány na haldě, sluší se je po skončení práce uvolnit:
void g_date_free(GDate *date);
No a to by tak bylo z mé strany o práci s datem v knihovně GLib vše. U dalšího pokračování se těší