You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
330 lines
6.8 KiB
330 lines
6.8 KiB
/* |
|
|
|
datecalc.c |
|
|
|
Written 1996/96 by Oliver Kraus |
|
Published by Heinz Heise Verlag 1997 (c't 15/97) |
|
Completly rewritten and put under GPL 2011 by Oliver Kraus |
|
|
|
(c) 2011 by Oliver Kraus (olikraus@gmail.com) |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
|
|
Development goals: |
|
- English version |
|
- Optimized for 8 bit microcontroller |
|
|
|
Definitions: |
|
Short Name: y |
|
Long Name: year |
|
Range: 2000... |
|
|
|
Short Name: ydn |
|
Long Name: year day number |
|
Range: 1..366 |
|
|
|
Short Name: cdn |
|
Long Name: century day number |
|
Range: 1...65535 |
|
|
|
Short Name: ymd |
|
Long Name: Year, Month, Day |
|
Range: 2000...65535, 1..12, 1..31 |
|
|
|
Conversions |
|
ymd --> y, ydn |
|
get_year_day_number() |
|
y, ydn --> ymd |
|
get_month_by_year_day_number() |
|
get_day_by_year_day_number() |
|
y, ydn --> cdn |
|
to_century_day_number(); |
|
cdn --> y, ydn |
|
from_century_day_number(); |
|
|
|
|
|
*/ |
|
|
|
#include <stdint.h> |
|
|
|
/* |
|
Prototype: |
|
uint8_t is_leap_year(uint16_t y) |
|
Description: |
|
Calculate leap year |
|
Arguments: |
|
y year, e.g. 2011 for year 2011 |
|
Result: |
|
0 not a leap year |
|
1 leap year |
|
*/ |
|
static uint8_t is_leap_year(uint16_t y) |
|
{ |
|
if ( |
|
((y % 4 == 0) && (y % 100 != 0)) || |
|
(y % 400 == 0) |
|
) |
|
return 1; |
|
return 0; |
|
} |
|
|
|
/* |
|
Prototype: |
|
uint16_t get_year_day_number(uint16_t y, uint8_t m, uint8_t d) |
|
Description: |
|
Calculate the day number within a year. 1st of Jan has the number 1. |
|
"Robertson" Algorithm |
|
Arguments: |
|
y year, e.g. 2011 for year 2011 |
|
m month with 1 = january to 12 = december |
|
d day starting with 1 |
|
Result: |
|
The "day number" within the year: 1 for the 1st of Jan. |
|
See also: |
|
get_month_by_day_number() |
|
|
|
*/ |
|
uint16_t get_year_day_number(uint16_t y, uint8_t m, uint8_t d) |
|
{ |
|
uint8_t tmp1; |
|
uint16_t tmp2; |
|
tmp1 = 0; |
|
if ( m >= 3 ) |
|
tmp1++; |
|
tmp2 = m; |
|
tmp2 +=2; |
|
tmp2 *=611; |
|
tmp2 /= 20; |
|
tmp2 += d; |
|
tmp2 -= 91; |
|
tmp1 <<=1; |
|
tmp2 -= tmp1; |
|
if ( tmp1 != 0 ) |
|
tmp2 += is_leap_year(y); |
|
return tmp2; |
|
} |
|
|
|
/* |
|
Prototype: |
|
uint8_t get_month_by_year_day_number(uint16_t y, uint16_t ydn) |
|
Description: |
|
Get the month from year and day number within a year. |
|
"R. A. Stone" Algorithm |
|
Arguments: |
|
y year, e.g. 2011 for year 2011 |
|
ydn year day number (1st of Jan has the number 1) |
|
Result: |
|
The month within the year: 1 for January. |
|
See also: |
|
get_year_day_number() |
|
*/ |
|
|
|
static uint16_t corrected_year_day_number(uint16_t y, uint16_t ydn) |
|
{ |
|
uint8_t a; |
|
a = is_leap_year(y); |
|
if ( ydn > 59+a ) |
|
{ |
|
ydn += 2; |
|
ydn -= a; |
|
} |
|
ydn += 91; |
|
return ydn; |
|
} |
|
|
|
uint8_t get_month_by_year_day_number(uint16_t y, uint16_t ydn) |
|
{ |
|
uint8_t a; |
|
ydn = corrected_year_day_number(y, ydn); |
|
ydn *= 20; |
|
ydn /= 611; |
|
a = ydn; |
|
a -= 2; |
|
return a; |
|
} |
|
|
|
/* |
|
Prototype: |
|
uint8_t get_day_by_year_day_number(uint16_t y, uint16_t ydn) |
|
Description: |
|
Get the day within month from year and day number within a year. |
|
"R. A. Stone" Algorithm |
|
Arguments: |
|
y year, e.g. 2011 for year 2011 |
|
ydn year day number (1st of Jan has the number 1) |
|
Result: |
|
The day within a month: 1 for the first day of a month. |
|
See also: |
|
get_year_day_number() |
|
*/ |
|
uint8_t get_day_by_year_day_number(uint16_t y, uint16_t ydn) |
|
{ |
|
uint8_t m; |
|
uint16_t tmp; |
|
m = get_month_by_year_day_number(y, ydn); |
|
m += 2; |
|
ydn = corrected_year_day_number(y, ydn); |
|
tmp = 611; |
|
tmp *= m; |
|
tmp /= 20; |
|
ydn -= tmp; |
|
return ydn; |
|
} |
|
|
|
/* |
|
Prototype: |
|
uint8_t get_weekday_by_year_day_number(uint16_t y, uint16_t ydn) |
|
Description: |
|
Get the day within week from year and day number within a year. |
|
"Zeller" Algorithm |
|
Arguments: |
|
y year, e.g. 2011 for year 2011 |
|
ydn year day number (1st of Jan has the number 1) |
|
Result: |
|
The day within a week: 0..6 with 0 = Sunday, 1 = Monday, ... |
|
See also: |
|
get_year_day_number() |
|
*/ |
|
uint8_t get_weekday_by_year_day_number(uint16_t y, uint16_t ydn) |
|
{ |
|
uint8_t j, c, tmp8; |
|
uint16_t tmp16; |
|
y--; |
|
j = y % 100; |
|
c = y / 100; |
|
tmp16 = c; |
|
tmp16 *= 5; |
|
tmp16 += ydn; |
|
tmp8 = j; |
|
j >>= 2; |
|
c >>= 2; |
|
tmp8 += j; |
|
tmp8 += c; |
|
tmp8 += 28; |
|
tmp16 += tmp8; |
|
tmp16 %= 7; |
|
return tmp16; |
|
} |
|
|
|
/* |
|
Prototype: |
|
uint16_t to_century_day_number(uint16_t y, uint16_t ydn) |
|
Description: |
|
Calculate days since January, 1st, 2000 |
|
Arguments: |
|
y year, e.g. 2011 for year 2011 |
|
ydn year day number (1st of Jan has the number 1) |
|
*/ |
|
uint16_t to_century_day_number(uint16_t y, uint16_t ydn) |
|
{ |
|
uint16_t cdn; |
|
cdn = ydn; |
|
cdn--; |
|
while( y > 2000 ) |
|
{ |
|
y--; |
|
cdn += 365; |
|
cdn += is_leap_year(y); |
|
} |
|
return cdn; |
|
} |
|
|
|
void from_century_day_number(uint16_t cdn, uint16_t *year, uint16_t *ydn) |
|
{ |
|
uint16_t y, days_per_year; |
|
y = 2000; |
|
for(;;) |
|
{ |
|
days_per_year = 365; |
|
days_per_year += is_leap_year(y); |
|
if ( cdn >= days_per_year ) |
|
{ |
|
cdn -= days_per_year; |
|
y++; |
|
} |
|
else |
|
break; |
|
} |
|
cdn++; |
|
*year = y; |
|
*ydn = cdn; |
|
} |
|
|
|
/* |
|
Calculate the seconds after 2000-01-01 00:00. The largest possible |
|
time is 2136-02-07 06:28:15 |
|
*/ |
|
uint32_t to_time(uint16_t cdn, uint8_t h, uint8_t m, uint8_t s) |
|
{ |
|
uint32_t t; |
|
t = cdn; |
|
t *= 24; |
|
t += h; |
|
t *= 60; |
|
t += m; |
|
t *= 60; |
|
t += s; |
|
return t; |
|
} |
|
|
|
|
|
void from_time(uint32_t t, uint16_t *cdn, uint8_t *h, uint8_t *m, uint8_t *s) |
|
{ |
|
*s = t % 60; |
|
t /= 60; |
|
*m = t % 60; |
|
t /= 60; |
|
*h = t % 24; |
|
t /= 24; |
|
*cdn = t; |
|
} |
|
|
|
uint32_t to_sec_since_2000(uint16_t y, uint8_t mo, uint8_t d, uint8_t h, uint8_t mi, uint8_t s) |
|
{ |
|
uint16_t ydn = get_year_day_number(y, mo, d); |
|
uint16_t cdn = to_century_day_number(y, ydn); |
|
return to_time(cdn, h, mi, s); |
|
} |
|
|
|
/* |
|
Calculate the minutes after 2000-01-01 00:00. |
|
*/ |
|
uint32_t to_minutes(uint16_t cdn, uint8_t h, uint8_t m) |
|
{ |
|
uint32_t t; |
|
t = cdn; |
|
t *= 24; |
|
t += h; |
|
t *= 60; |
|
t += m; |
|
return t; |
|
} |
|
|
|
|
|
void from_minutes(uint32_t t, uint16_t *cdn, uint8_t *h, uint8_t *m) |
|
{ |
|
*m = t % 60; |
|
t /= 60; |
|
*h = t % 24; |
|
t /= 24; |
|
*cdn = t; |
|
} |
|
|
|
uint32_t to_minutes_since_2000(uint16_t y, uint8_t mo, uint8_t d, uint8_t h, uint8_t mi) |
|
{ |
|
uint16_t ydn = get_year_day_number(y, mo, d); |
|
uint16_t cdn = to_century_day_number(y, ydn); |
|
return to_minutes(cdn, h, mi); |
|
}
|
|
|