* tekst powstał jesienią 2005 roku
Jak co roku jesienią nadchodzi pora przejścia z czasu zimowego na czas letni. Odwieczny rytuał przestawiania wszystkich zegarków o jedną godzinę. Chodząc z pokoju do pokoju i aktualizując wskazania zegarów naściennych wzrastał we mnie poziom dezaprobaty. Czy zegarki nie mogłyby same się przestawiać? W gruncie rzeczy by mogły (z kilkoma wyjątkami zegarków nie mających mikrokontrolera w sobie). Zaprzątała mnie myśl jak zrealizować tą czynność w sposób programowy. Wiadomo przecież, że zmiana czasu (ta z letniego na zimowy) następuje o godzinie 3 w nocy z soboty na niedzielę (czyli właściwie w niedzielę) w ostatni weekend października. Jak określić w programie, datę kiedy należy wykonać akcję przestawienia zegara. I tak chodząc od zegara do zegara (a mam ich ze sześć sztuk) w mojej podświadomości jakiś programista układał algorytm dla programu i nagle ... eureka. Zamiast określać kiedy ma nastąpić zmiana czasu (w sensie określenia daty i czasu wykonania akcji – typowej akcji budzika) można podejść do problemu z innej strony. Należy sprawdzić czy tą akcję należy wykonać dzisiaj, a to nie jest już skomplikowane.
Wiadomo, że zmiana czasu następuje w ostatnią niedzielę października. W grę wchodzą daty od 25 października do 31 października włącznie (czyli ostatni tydzień), a właściwie to wystarczy sprawdzić, czy numer dnia jest większy od 25 (bo mniejszy lub równy 31 będzie zawsze). Należy tylko sprawdzić czy dany dzień w tym przedziale jest niedzielą. Proste. Już kiedyś miałem sposobność programowej zabawy z datami i dniami tygodnia. Algorytm jest prosty. Należy obliczyć liczbę dni jakie upłynęły od wybranego konkretnego dnia do dnia, który jest badany. Odgrzebałem jakiś stary program, w którym takie zabawy były robione. W moich procedurach wybranym dniem jest 1 styczeń 1900 roku. Pozwala to na określenia dnia tygodnia dla dowolnego dnia poczynając od początku XX wieku. Mając obliczoną liczbę dni jaka minęła od wybranego dnia, obliczamy resztę z dzielenia tej liczby przez 7. Wynik (reszta z dzielenia) określa nam dzień tygodnia. Dla przyjętych warunków początkowych jest to:
- poniedziałek jeżeli reszta z dzielenia wynosi 2
- wtorek jeżeli reszta z dzielenia wynosi 3
- środa jeżeli reszta z dzielenia wynosi 4
- czwartek jeżeli reszta z dzielenia wynosi 5
- piątek jeżeli reszta z dzielenia wynosi 6
- sobota jeżeli reszta z dzielenia wynosi 0
- niedziela jeżeli reszta z dzielenia wynosi 1
Program jest następujący (w wersji na komputer PC):
Kod: Zaznacz cały
#include "stdio.h"
#define Nie 1
#define Pon 2
#define Wto 3
#define Sro 4
#define Czw 5
#define Pia 6
#define Sob 0
unsigned short ZamienDateNaLiczbe ( unsigned short Rok ,
unsigned short Miesiac ,
unsigned short Dzien )
{
unsigned short RokDiv ;
unsigned short RokMod ;
unsigned short Wynik ;
unsigned short DniWLutym ;
/* ------------------- */
Rok = Rok - 1900 ;
RokDiv = Rok / 4 ;
RokMod = Rok % 4 ;
Wynik = Rok * 365 + RokDiv ;
if ( RokMod )
{
DniWLutym = 28 ;
Wynik ++ ;
} /* if ... */
else
DniWLutym = 29 ;
switch ( Miesiac )
{
case 1 :
break ;
case 2 : /* minal styczen (+31) */
Wynik += 31 ;
break ;
case 3 : /* minal luty (+31+DniWLutym) */
Wynik += 31 + DniWLutym ;
break ;
case 4 : /* minal marzec (+31+DniWLutym+31) */
Wynik += 62 + DniWLutym ;
break ;
case 5 : /* minal kwiecien (+31+DniWLutym+31+30) */
Wynik += 92 + DniWLutym ;
break ;
case 6 : /* minal maj (+31+DniWLutym+31+30+31) */
Wynik += 123 + DniWLutym ;
break ;
case 7 : /* minal czerwiec (+31+DniWLutym+31+30+31+30) */
Wynik += 153 + DniWLutym ;
break ;
case 8 : /* minal lipec (+31+DniWLutym+31+30+31+30+31) */
Wynik += 184 + DniWLutym ;
break ;
case 9 : /* minal sierpien (+31+DniWLutym+31+30+31+30+31+31) */
Wynik += 215 + DniWLutym ;
break ;
case 10 : /* minal wrzesien (+31+DniWLutym+31+30+31+30+31+
31+30) */
Wynik += 245 + DniWLutym ;
break ;
case 11 : /* minal pazdziernik (+31+DniWLutym+31+30+31+30+31+
31+30+31) */
Wynik += 276 + DniWLutym ;
break ;
case 12 : /* minal listopad (+31+DniWLutym+31+30+31+30+31+
31+30+31+30) */
Wynik += 306 + DniWLutym ;
break ;
default :
break ;
} /* switch */ ;
return ( Wynik + Dzien ) ;
} /* ZamienDateNaLiczbe */
unsigned short DzienTygodnia ( unsigned short Rok ,
unsigned short Miesiac ,
unsigned short Dzien )
{
return ( ZamienDateNaLiczbe ( Rok , Miesiac , Dzien ) % 7 ) ;
} /* DzienTygodnia */
int ZmianaCzasuNaZimowy ( unsigned short Rok ,
unsigned short Miesiac ,
unsigned short Dzien )
{
int FlagaZmiany = 0 ;
/* ------------------- */
if ( Miesiac == 10 )
if ( Dzien >= 25 )
if ( DzienTygodnia ( Rok , Miesiac , Dzien ) == Nie )
FlagaZmiany = 1 ;
return ( FlagaZmiany ) ;
} /* ZmianaCzasuNaZimowy */
int main ( void )
{
unsigned short Dzien ;
unsigned short Miesiac ;
unsigned short Rok ;
/* ------------------- */
Miesiac = 1 ;
Rok = 2006 ;
Dzien = 1 ;
printf ( "Najblizszy nowy rok jest w " );
switch ( DzienTygodnia ( Rok , Miesiac , Dzien ) )
{
case Nie :
printf ( "niedziele\n" ) ;
break ;
case Pon :
printf ( "poniedzialek\n" ) ;
break ;
case Wto :
printf ( "wtorek\n" ) ;
break ;
case Sro :
printf ( "sroda\n" ) ;
break ;
case Czw :
printf ( "czwartek\n" ) ;
break ;
case Pia :
printf ( "piated\n" ) ;
break ;
case Sob :
printf ( "sobote\n" ) ;
break ;
default :
;
} /* switch */ ;
Miesiac = 10 ;
Rok = 2005 ;
for ( Dzien = 20 ; Dzien <= 31 ; Dzien ++ )
{
if ( ZmianaCzasuNaZimowy ( Rok , Miesiac , Dzien ) )
printf ( "Zmiana czasu w %d.%d.%d : niedziela\n" ,
Dzien , Miesiac , Rok ) ;
} /* for */ ;
} /* main */
Oczywiście nie jest to pełny algorytm. Do pełni szczęścia należałoby przechowywać gdzieś w pamięci nieulotnej aktualny rodzaj czasu. Niech to będzie zmienna o nazwie CzasLetni, która przyjmuje wartość 0 jeżeli jest czas zimowy lub 1 jeżeli jest czas letni.
Kod: Zaznacz cały
....
unsigned char CzasLetni
for ( ; ; )
{
PobierzCzasIDate ( ) ;
if ( CzasLetni )
{
if ( ZmianaCzasuNaZimowy ( Rok , Miesiac , Dzien ) )
{
if ( Godz == 3 )
{
CzasLetni = 0 ;
ZmianaCzasuZLetniegoNaZimowy ( ) ;
} /* if */ ;
} /* if */ ;
} /* if ... */
else
{
//..............
} /* if ... else */ ;
} /* for */ ;
Czas przenieść algorytmy do środowiska procków (przykładowo AVR) i sprawdzić dziłanie.
Kod: Zaznacz cały
(…)
#include <inttypes.h>
#include <avr/io.h>
#define nop() __asm__ __volatile__ ("nop")
#define Nie 1
#define Pon 2
#define Wto 3
#define Sro 4
#define Czw 5
#define Pia 6
#define Sob 0
uint16_t ZamienDateNaLiczbe ( uint16_t Rok ,
uint16_t Miesiac ,
uint16_t Dzien )
{
uint16_t RokDiv ;
uint16_t RokMod ;
uint16_t Wynik ;
uint16_t DniWLutym ;
/* ------------------- */
Rok = Rok - 1900 ;
RokDiv = Rok / 4 ;
RokMod = Rok % 4 ;
Wynik = Rok * 365 + RokDiv ;
if ( RokMod )
{
DniWLutym = 28 ;
Wynik ++ ;
} /* if ... */
else
DniWLutym = 29 ;
switch ( Miesiac )
{
case 1 :
break ;
case 2 : /* minal styczen (+31) */
Wynik += 31 ;
break ;
case 3 : /* minal luty (+31+DniWLutym) */
Wynik += 31 + DniWLutym ;
break ;
case 4 : /* minal marzec (+31+DniWLutym+31) */
Wynik += 62 + DniWLutym ;
break ;
case 5 : /* minal kwiecien (+31+DniWLutym+31+30) */
Wynik += 92 + DniWLutym ;
break ;
case 6 : /* minal maj (+31+DniWLutym+31+30+31) */
Wynik += 123 + DniWLutym ;
break ;
case 7 : /* minal czerwiec (+31+DniWLutym+31+30+31+30) */
Wynik += 153 + DniWLutym ;
break ;
case 8 : /* minal lipec (+31+DniWLutym+31+30+31+30+31) */
Wynik += 184 + DniWLutym ;
break ;
case 9 : /* minal sierpien (+31+DniWLutym+31+30+31+30+31+31) */
Wynik += 215 + DniWLutym ;
break ;
case 10 : /* minal wrzesien (+31+DniWLutym+31+30+31+30+31+31+30) */
Wynik += 245 + DniWLutym ;
break ;
case 11 : /* minal pazdziernik (+31+DniWLutym+31+30+31+30+31+31+30+31)*/
Wynik += 276 + DniWLutym ;
break ;
case 12 : /* minal listopad (+31+DniWLutym+31+30+31+30+31+31+30+31+30)*/
Wynik += 306 + DniWLutym ;
break ;
default :
break ;
} /* switch */ ;
return ( Wynik + Dzien ) ;
} /* ZamienDateNaLiczbe */
uint16_t DzienTygodnia ( uint16_t Rok ,
uint16_t Miesiac ,
uint16_t Dzien )
{
return ( ZamienDateNaLiczbe ( Rok , Miesiac , Dzien ) % 7 ) ;
} /* DzienTygodnia */
uint8_t ZmianaCzasuNaZimowy ( uint16_t Rok ,
uint16_t Miesiac ,
uint16_t Dzien )
{
uint8_t FlagaZmiany = 0 ;
/* ------------------- */
if ( Miesiac == 10 )
if ( Dzien >= 25 )
if ( DzienTygodnia ( Rok , Miesiac , Dzien ) == Nie )
FlagaZmiany = 1 ;
return ( FlagaZmiany ) ;
} /* ZmianaCzasuNaZimowy */
int main ( void )
{
uint16_t Dzien ;
uint16_t Miesiac ;
uint16_t Rok ;
uint8_t Cos ;
uint8_t Cos2 ;
/* ------------------- */
Cos = ' ' ;
Cos2 = ' ' ;
Miesiac = 1 ;
Rok = 2006 ;
Dzien = 1 ;
switch ( DzienTygodnia ( Rok , Miesiac , Dzien ) )
{
case Nie :
Cos = 'N' ;
break ;
case Pon :
Cos = 'P' ;
break ;
case Wto :
Cos = 'W' ;
break ;
case Sro :
Cos = 'S' ;
break ;
case Czw :
Cos = 'C' ;
break ;
case Pia :
Cos = 'p' ;
break ;
case Sob :
Cos = 's' ;
break ;
default :
;
} /* switch */ ;
Miesiac = 10 ;
Rok = 2005 ;
for ( Dzien = 20 ; Dzien <= 31 ; Dzien ++ )
{
if ( ZmianaCzasuNaZimowy ( Rok , Miesiac , Dzien ) )
{
Cos2 = 'Z' ;
break ;
} /* if */ ;
} /* for */ ;
/*
w tym miejscu zmienna Cos2 powinna miec 'Z',
zmienna Dzien powinna miec 30
zmienna Cos powinna miec 'N'
*/
for ( ; ; )
{
nop ( ) ;
} /* for */ ;
} /* main */
Załącznik z programer dla AVR'a: