Donnerstag, 2. Dezember 2010

 

Datum in C selbst gebastelt

Datumsberechnungen sind immer wieder eine Quelle großer langwieriger, fehleranfälliger Programmteile. Vor allem in Sprachen, die solche Datumsoperationen nicht unterstützen. Sie sind auch immer wieder geeignet, Programmieren zu üben. Hier die Musterlösung zu einem einfachen Kalenderprogramm in C, bei dem ein Großteil der Datumsfunktionen selbstgestrickt sind. Viel Spaß damit!
Verwendung:
Ohne Parameter gibt es einfach das aktuelle Monat mit Markierung < 2> für den heutigen Tag und : 8: als Markierung für österreichische Feiertage. Danach wird die Liste der Feiertage ausgegeben.
$ ./cal 
Dezember 2010

    Montag       6  13  20  27     
  Dienstag       7  14  21  28     
  Mittwoch   1 : 8: 15  22  29     
Donnerstag < 2>  9  16  23  30     
   Freitag   3  10  17 :24::31:    
   Samstag   4  11  18 :25:        
   Sonntag : 5::12::19::26:    

 5.12. 2. Advent
 8.12. Maria Empfängnis
12.12. 3. Advent
19.12. 4. Advent
24.12. Heiligabend
25.12. Christtag
26.12. Stefanitag
31.12. Silvester :-)

Here comes the Source...

/*
 * File:    cal.c
 *
 * Zweck:   Kalenderprogramm
 *          ...für ein gegebenes Monat.
 *          mit/ohne argc/argv
 *
 * Autor:   Harald Haberstroh
 *
 * Algorithmus:
 *
 * History: 2002-09-29
 *          2003-10-15, Feiertage + Argumente von main()
 *          2004-11-05, Liste der Feiertage
 */

/*--------------------------- includes ------------------------------*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>

/*--------------------------- defines -------------------------------*/
#define  WOCHENTAG   3                 /* Wochentag des 1.1.1582 */
#define  MONTAG 0
#define  DIENSTAG 1
#define  MITTWOCH 2
#define  DONNERSTAG 3
#define  FREITAG 4
#define  SAMSTAG 5
#define  SONNTAG 6

/*--------------------------- typedefs ------------------------------*/

/*--------------------------- globals -------------------------------*/
enum {
   KEIN = 0,
   NEUJAHR,
   HL3KOENIGE,
   ASCHERMITTWOCH,
   PALMSONNTAG,
   KARFREITAG,
   OSTERSONNTAG,
   OSTERMONTAG,
   STAATSFEIERTAG,
   MUTTERTAG,
   PFINGSTSONNTAG,
   PFINGSTMONTAG,
   DREIFALTIGKEITSSONNTAG,
   FRONLEICHNAM,
   MARIAHIMMELFAHRT,
   NATIONALFEIERTAG,
   REFORMATIONSTAG,
   ALLERHEILIGEN,
   ALLERSEELEN,
   MARIAEMPFAENGNIS,
   ADVENT1,
   ADVENT2,
   ADVENT3,
   ADVENT4,
   HEILIGABEND,
   CHRISTIANI,
   STEFANI,
   SILVESTER
};

/*-------------------------- prototypes -----------------------------*/
void printFeiertage(int jahr, int monat);
int tagemonat( int jahr, int monat );
int sonntag( int jahr, int monat, int tag );
void tag2datum( int tagzahl, int *jahr, int *monat, int *tag );
void ostern( int jahr, int *monat, int *tag );
int feiertag(int jahr, int monat, int tag);
int anzProMonat(int jahr, int monat);
void printcal(int start, int anz, int heute, int monat, int jahr);
int schaltjahr( int Jahr );
int Anzahl_Tage( int Jahr, int Monat, int Tag );

/*----------------------------- main --------------------------------*/
int main( int argc, char *argv[] )
{
   int tage, tag, monat, jahr, start;
   struct tm *zeit;
   time_t   sec;

   if (argc == 2 && strcmp(argv[1], "-i") == 0)
   {                                   /* interaktiv */
      printf("Jahr> ");scanf("%d", &jahr);
      printf("Monat> ");scanf("%d", &monat);
      printf("Tag (0 für kein bestimmter)> ");scanf("%d", &tag);
      if (tag == 0)
      {
         tag = 1;
         if (1 <= monat && monat <= 12 &&
             1582 < jahr && jahr < 3000)
         {
            tage = Anzahl_Tage( jahr, monat, 1);
            start = (tage + WOCHENTAG) % 7;
            printcal(start, anzProMonat(jahr, monat), 0, monat, jahr);
            return 0;
         }
         else
         {
            fprintf(stderr, "Monat zwischen 1 und 12 und Jahr zwischen "
                            "1583 und 2999\n");
            return 2;
         }
      }
      if (1 <= monat && monat <= 12 &&
          1582 < jahr && jahr < 3000 &&
          1 <= tag && tag <= anzProMonat(jahr,monat))
      {
         tage = Anzahl_Tage( jahr, monat, 1);
         start = (tage + WOCHENTAG) % 7;
         printcal(start, anzProMonat(jahr, monat), tag, monat, jahr);
         return 0;
      }
      else
      {
         fprintf(stderr, "Monat zwischen 1 und 12 und Jahr zwischen "
                         "1583 und 2999\n"
                         "[Tag zwischen 1 und Anzahl für dieses Monat]\n");
         return 2;
      }
   }
   else if (argc == 3)                      /* Monat, Jahr */
   {
      monat = atoi(argv[1]);
      jahr = atoi(argv[2]);
      tag = 1;
      if (1 <= monat && monat <= 12 &&
          1582 < jahr && jahr < 3000)
      {
         tage = Anzahl_Tage( jahr, monat, 1);
         start = (tage + WOCHENTAG) % 7;
         printcal(start, anzProMonat(jahr, monat), 0, monat, jahr);
         return 0;
      }
      else
      {
         fprintf(stderr, "Monat zwischen 1 und 12 und Jahr zwischen "
                         "1583 und 2999\n");
         return 2;
      }
   }
   else if (argc == 4)                 /* ganzes Datum */
   {
      monat = atoi(argv[2]);
      jahr = atoi(argv[3]);
      tag = atoi(argv[1]);
      if (1 <= monat && monat <= 12 &&
          1582 < jahr && jahr < 3000 &&
          1 <= tag && tag <= anzProMonat(jahr,monat))
      {
         tage = Anzahl_Tage( jahr, monat, 1);
         start = (tage + WOCHENTAG) % 7;
         printcal(start, anzProMonat(jahr, monat), tag, monat, jahr);
         return 0;
      }
      else
      {
         fprintf(stderr, "Monat zwischen 1 und 12 und Jahr zwischen "
                         "1583 und 2999\n"
                         "[Tag zwischen 1 und Anzahl für dieses Monat]\n");
         return 2;
      }
   }
   else if (argc == 1)                 /* aktuelles Monat */
   {
      time(&sec);
      zeit = localtime(&sec);
      tag = zeit->tm_mday;
      monat = zeit->tm_mon + 1;
      jahr = zeit->tm_year + 1900;
      tage = Anzahl_Tage( jahr, monat, 1);
      start = (tage + WOCHENTAG) % 7;
      printcal(start, anzProMonat(jahr, monat), tag, monat, jahr);
      return 0;
   }
   else
   {
      fprintf(stderr, "Aufruf:\n%s [[tag] monat jahr]\n", argv[0]);
      return 1;
   }
}

/*-------------------------- functions ------------------------------*/
/** Kalender ausgeben
 *  start ... Wochentag des 1. dieses Monats
 *  anz   ... Anzahl der Tage dieses Monats
 *  heute ... Datum des heutigen Tages, 0, falls in einem anderen Monat
 *  monat ... Monat (1..12)
 *  jahr
 */
void printcal(int start, int anz, int heute, int monat, int jahr)
{
   char *tage[] = { "Montag", "Dienstag", "Mittwoch", "Donnerstag",
                    "Freitag", "Samstag", "Sonntag" };
   char *monate[] = { "Jänner", "Februar", "März", "April",
                      "Mai", "Juni", "Juli", "August",
                      "September", "Oktober", "November", "Dezember" };
   int i;
   int tag;

   printf("%s %d\n\n", monate[monat-1], jahr);
   for (i = 0; i < 7; i++)
   {
      printf("%10s ", tage[i]);
      for (tag = i + 1 - start; tag < anz + 7 + start; tag += 7)
         if (tag == heute && feiertag(jahr, monat, tag)
               && tag >= 1 && tag <= anz)
            printf("[%2d]", tag);
         else if (tag == heute && tag >= 1 && tag <= anz)
            printf("<%2d>", tag);
         else if (feiertag(jahr, monat, tag) && tag >= 1 && tag <= anz)
            printf(":%2d:", tag);
         else if (tag >= 1 && tag <= anz)
            printf("%3d ", tag);
         else
            printf("    ");
      printf("\n");
   }
   printf("\n");
   printFeiertage(jahr, monat);
}

/*
 * Function schaltjahr
 *
 * Zweck:   Liefert 1, wenn angegebenes Jahr ein Schaltjahr ist
 *
 * Algorithmus:
 *          ab 1582 gilt folgende Regel für Schaltjahre:
 *             Ist die Jahreszahl durch 400 teilbar oder
 *             ist die Jahreszahl durch 4 teilbar aber nicht durch 100
 *             dann ist das Jahr ein Schaltjahr
 *
 * Parameter:
 *       IN: Jahr
 *      OUT:
 *
 * Return-Wert: 1 ... Schaltjahr
 *              0 ... sonst
 */
int schaltjahr( int Jahr )
{
   if ( Jahr % 400 == 0 )
      return 1;
   else if ( Jahr % 100 == 0 )
      return 0;
   else if ( Jahr % 4 == 0 )
      return 1;
   else 
      return 0;
}

int anzProMonat(int jahr, int monat)
{
   switch(monat)
   {
      case 2:
         return 28 + schaltjahr(jahr);
         break;
      case 4:
      case 6:
      case 9:
      case 11:
         return 30;
         break;
      default:
         return 31;
   }
}

/*
 * Function Anzahl_Tage
 *
 * Zweck:    Bestimmt die Anzahl der Tage ab 1.1.1582
 *
 * Algorithmus:
 *           Tag + Anzahl Tage für Monate + Anzahl Tage für Jahre
 *
 * Parameter:
 *       IN: Jahr, Monat, Tag
 *      OUT:
 *
 * Return-Wert: Anzahl der Tage ab 1.1.1582
 */
int Anzahl_Tage( int Jahr, int Monat, int Tag )
{
   int   j;
   int   m;
   int   tage = Tag; /* Monatstag */

   /*
    * Tage für Jahre berechnen
    */
   for ( j = 1582; j < Jahr; j++ )
      tage += 365 + schaltjahr( j );

   /*
    * Tage für Monate berechnen
    */
   for ( m = 1; m < Monat; m++ )
      switch( m )
      {
         case 2:
            tage += 28 + schaltjahr( Jahr );
            break;
         case 4:
         case 6:
         case 9:
         case 11:
            tage += 30;
            break;
         default:
            tage += 31;
      }
   return( tage );
}

/** Bestimmt Feiertag.
 *  @param jahr
 *  @param monat
 *  @param tag
 *  @return Nummer des Feiertags, 0 sonst
 */
int feiertag(int jahr, int monat, int tag)
{
   int tagzahl, j, m, t;
   ostern(jahr, &m, &t);
   tagzahl = Anzahl_Tage(jahr, m, t);
   
   if (monat == m && tag == t)         /* Ostern */
      return OSTERSONNTAG;
   tag2datum(tagzahl + 1, &j, &m, &t);
   if (monat == m && tag == t)         /* Ostern */
      return OSTERMONTAG;
   tag2datum(tagzahl - 46, &j, &m, &t);
   if (monat == m && tag == t)         /* Aschermittwoch */
      return ASCHERMITTWOCH;
   tag2datum(tagzahl - 7, &j, &m, &t);
   if (monat == m && tag == t)         /* Palmsonntag */
      return PALMSONNTAG;
   tag2datum(tagzahl - 2, &j, &m, &t);
   if (monat == m && tag == t)         /* Karfreitag */
      return KARFREITAG;
   tag2datum(tagzahl + 49, &j, &m, &t);
   if (monat == m && tag == t)         /* Pfingstsonntag */
      return PFINGSTSONNTAG;
   tag2datum(tagzahl + 50, &j, &m, &t);
   if (monat == m && tag == t)         /* Pfingstmontag */
      return PFINGSTMONTAG;
   tag2datum(tagzahl + 56, &j, &m, &t);
   if (monat == m && tag == t)         /* Dreifaltigkeitssonntag */
      return DREIFALTIGKEITSSONNTAG;
   tag2datum(tagzahl + 60, &j, &m, &t);
   if (monat == m && tag == t)         /* Fronleichnam */
      return FRONLEICHNAM;

   if (monat == 1 && tag == 1)         /* Neujahr */
      return NEUJAHR;
   if (monat == 1 && tag == 6)         /* Heilige 3 Könige */
      return HL3KOENIGE;
   if (monat == 5 && tag == 1)         /* Staatsfeiertag */
      return STAATSFEIERTAG;
   tag2datum(sonntag(jahr, 5, 13), &j, &m, &t);
   if (monat == 5 && tag == t)         /* Muttertag FIXME */
      return MUTTERTAG;
   if (monat == 8 && tag == 15)        /* Mariahimmelfahrt */
      return MARIAHIMMELFAHRT;
   if (monat == 10 && tag == 26)       /* Nationalfeiertag */
      return NATIONALFEIERTAG;
   if (monat == 10 && tag == 31)       /* Reformationstag */
      return REFORMATIONSTAG;
   if (monat == 11 && tag == 1)        /* Allerheiligen */
      return ALLERHEILIGEN;
   if (monat == 11 && tag == 2)        /* Allersselen */
      return ALLERSEELEN;
   if (monat == 11)
   {
      if (sonntag(jahr, 12, 24) == Anzahl_Tage(jahr, 12, 24))
         tagzahl = sonntag(jahr, 12, 24) - 21;
      else
         tagzahl = sonntag(jahr, 12, 24) - 28;
      tag2datum(tagzahl, &j, &m, &t);
      if (tag == t && m == 11)         /* 1. Advent */
         return ADVENT1;
   }
   if (monat == 12)
   {
      if (tag == 8)                    /* Maria Emfängnis */
         return MARIAEMPFAENGNIS;

      if (sonntag(jahr, 12, 24) == Anzahl_Tage(jahr, 12, 24))
         tagzahl = sonntag(jahr, 12, 24) - 21;
      else
         tagzahl = sonntag(jahr, 12, 24) - 28;
      tag2datum(tagzahl, &j, &m, &t);
      if (tag == t && m == 12)         /* 1. Advent */
         return ADVENT1;
      tag2datum(tagzahl+7, &j, &m, &t);
      if (tag == t)                    /* 2. Advent */
         return ADVENT2;
      tag2datum(tagzahl+14, &j, &m, &t);
      if (tag == t)                    /* 3. Advent */
         return ADVENT3;
      tag2datum(tagzahl+21, &j, &m, &t);
      if (tag == t)                    /* 4. Advent */
         return ADVENT4;
      if (tag == 24)                   /* Hl. Abend */
         return HEILIGABEND;
      if (tag == 25)                   /* Christiani */
         return CHRISTIANI;
      if (tag == 26)                   /* Stefanitag */
         return STEFANI;
      if (tag == 31)                   /* Silvester :-) */
         return SILVESTER;
   }

   return KEIN;                        /* Kein Feiertag */
}

void ostern( int jahr, int *monat, int *tag )
{
   int a = jahr % 19;
   int b = jahr / 100;
   int c = jahr % 100;
   int d = b / 4;
   int e = b % 4;
   int f = ( b + 8 ) / 25;
   int g = ( b - f + 1 ) / 3;
   int h = ( 19 * a + b - d - g + 15 ) % 30;
   int i = c / 4;
   int k = c % 4;
   int l = ( 32 + 2 * e + 2 * i -h -k ) % 7;
   int m = ( a + 11 * h + 22 * l ) / 451;
   int p = ( h + l - 7 * m + 114 ) % 31;

   *monat = ( h + l - 7 * m + 114 ) / 31;
   *tag = p + 1;
}
void tag2datum( int tagzahl, int *jahr, int *monat, int *tag )
{
   /*
    * Jahr berechnen
    */
   *jahr = 1582;
   while ( tagzahl > 365 + schaltjahr( *jahr ) )
   {
      tagzahl -= 365 + schaltjahr( *jahr );
      (*jahr)++;
   }
   /*
    * Monat
    */
   for ( *monat = 1; tagzahl > tagemonat( *jahr, *monat ); (*monat)++ )
      tagzahl -= tagemonat( *jahr, *monat );
   /*
    * Tag
    */
   *tag = tagzahl;
}

int sonntag( int jahr, int monat, int tag )
{
   int tagzahl = Anzahl_Tage( jahr, monat, tag );
   int wt = ( DONNERSTAG + tagzahl ) % 7;
   if ( wt == SONNTAG ) /* bereits Sonntag */
      return tagzahl;
   else
      return tagzahl + ( SONNTAG - wt );
}
int tagemonat( int jahr, int monat )
{
   switch( monat )
   {
      case 2:
         return 28 + schaltjahr( jahr );
         break;
      case 4:
      case 6:
      case 9:
      case 11:
         return 30;
         break;
      default:
         return 31;
   }
}

/** gib eine Liste der Feiertage aus.
 *  @param jahr IN
 *  @param monat OUT
 */
void printFeiertage(int jahr, int monat)
{
   int i;
   char *bez = "";
   for (i = 1; i <= tagemonat(jahr, monat); i++)
   {
      bez = "";
      switch (feiertag(jahr, monat, i))
      {
         case NEUJAHR:
            bez = "Neujahr";
            break;
         case HL3KOENIGE:
            bez = "Heilige Dreikönige";
            break;
         case ASCHERMITTWOCH:
            bez = "Aschermittwoch";
            break;
         case PALMSONNTAG:
            bez = "Palmsonntag";
            break;
         case KARFREITAG:
            bez = "Karfreitag";
            break;
         case OSTERSONNTAG:
            bez = "Ostersonntag";
            break;
         case OSTERMONTAG:
            bez = "Ostermontag";
            break;
         case STAATSFEIERTAG:
            bez = "Staatsfeiertag";
            break;
         case MUTTERTAG:
            bez = "Muttertag";
            break;
         case PFINGSTSONNTAG:
            bez = "Pfingstsonntag";
            break;
         case PFINGSTMONTAG:
            bez = "Pfingstmontag";
            break;
         case DREIFALTIGKEITSSONNTAG:
            bez = "Dreifaltigkeitssonntag";
            break;
         case FRONLEICHNAM:
            bez = "Fronleichnam";
            break;
         case MARIAHIMMELFAHRT:
            bez = "Maria Himmelfahrt";
            break;
         case NATIONALFEIERTAG:
            bez = "Nationalfeiertag";
            break;
         case REFORMATIONSTAG:
            bez = "Reformationstag";
            break;
         case ALLERHEILIGEN:
            bez = "Allerheiligen";
            break;
         case ALLERSEELEN:
            bez = "Allerseelen";
            break;
         case MARIAEMPFAENGNIS:
            bez = "Maria Empfängnis";
            break;
         case ADVENT1:
            bez = "1. Advent";
            break;
         case ADVENT2:
            bez = "2. Advent";
            break;
         case ADVENT3:
            bez = "3. Advent";
            break;
         case ADVENT4:
            bez = "4. Advent";
            break;
         case HEILIGABEND:
            bez = "Heiligabend";
            break;
         case CHRISTIANI:
            bez = "Christtag";
            break;
         case STEFANI:
            bez = "Stefanitag";
            break;
         case SILVESTER:
            bez = "Silvester :-)";
            break;
      }
      if (strlen(bez) > 0)
         printf("%2d.%d. %s\n", i, monat, bez);
   }
}

Labels: , ,


Kommentare:

Kommentar veröffentlichen

Abonnieren Kommentare zum Post [Atom]





<< Startseite

This page is powered by Blogger. Isn't yours?

Abonnieren Posts [Atom]