/* 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 . 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 /* 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); }