-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1146 from Kijewski/struct-tm-utils
sys: add utility functions for `struct tm`
- Loading branch information
Showing
4 changed files
with
331 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/** | ||
* @addtogroup sys_timex | ||
* @{ | ||
* @brief Utility library for `struct tm`. | ||
*/ | ||
|
||
#ifndef __SYS__TIMEX__TM__H | ||
#define __SYS__TIMEX__TM__H | ||
|
||
#include <time.h> | ||
#include <sys/time.h> | ||
#include <stdint.h> | ||
|
||
#include "attributes.h" | ||
|
||
#define TM_WDAY_SUN (0) /**< Sunday in `struct tm::tm_wday`. */ | ||
#define TM_WDAY_MON (1) /**< Monday in `struct tm::tm_wday`. */ | ||
#define TM_WDAY_TUE (2) /**< Tuesday in `struct tm::tm_wday`. */ | ||
#define TM_WDAY_WED (3) /**< Wednesday in `struct tm::tm_wday`. */ | ||
#define TM_WDAY_THU (4) /**< Thursday in `struct tm::tm_wday`. */ | ||
#define TM_WDAY_FRI (5) /**< Friday in `struct tm::tm_wday`. */ | ||
#define TM_WDAY_SAT (6) /**< Saturday in `struct tm::tm_wday`. */ | ||
|
||
#define TM_MON_JAN ( 0) /**< January in `struct tm::tm_mon` */ | ||
#define TM_MON_FEB ( 1) /**< February in `struct tm::tm_mon` */ | ||
#define TM_MON_MAR ( 2) /**< March in `struct tm::tm_mon` */ | ||
#define TM_MON_APR ( 3) /**< April in `struct tm::tm_mon` */ | ||
#define TM_MON_MAY ( 4) /**< May in `struct tm::tm_mon` */ | ||
#define TM_MON_JUN ( 5) /**< June in `struct tm::tm_mon` */ | ||
#define TM_MON_JUL ( 6) /**< July in `struct tm::tm_mon` */ | ||
#define TM_MON_AUG ( 7) /**< August in `struct tm::tm_mon` */ | ||
#define TM_MON_SEP ( 8) /**< September in `struct tm::tm_mon` */ | ||
#define TM_MON_OCT ( 9) /**< October in `struct tm::tm_mon` */ | ||
#define TM_MON_NOV (10) /**< November in `struct tm::tm_mon` */ | ||
#define TM_MON_DEC (11) /**< December in `struct tm::tm_mon` */ | ||
|
||
/** | ||
* @brief The number of days in common years. | ||
* @see http://oeis.org/A008685 | ||
*/ | ||
extern const int8_t TM_MON_DAYS[12]; | ||
|
||
/** | ||
* @brief The prefixsum of the number of days in common years. | ||
* @see http://oeis.org/A061251 | ||
*/ | ||
extern const int16_t TM_MON_DAYS_ACCU[12]; | ||
|
||
/** | ||
* @brief Tells if a given year is a leap year in the Gregorian calendar. | ||
* @param[in] year The year. Probably should be ≥ 1582, but needs to be ≥ 1. | ||
* @returns `1` if it is a leap year, `0` if it is a common year. | ||
*/ | ||
int tm_is_leap_year(unsigned year) CONST; | ||
|
||
/** | ||
* @brief Returns the congruent weekday of the Doomsday (March 0). | ||
* @details Only applies for years in the Gregorian calendar. | ||
* @param[in] year The year. Probably should be ≥ 1582, but needs to be ≥ 1. | ||
* @returns The result `% 7` is the weekday of the Doomsday of the given year. | ||
*/ | ||
int tm_doomsday(int year) CONST; | ||
|
||
/** | ||
* @brief Calculates the day of the year and the weekday of a given date. | ||
* @details Illegal dates are not catched. | ||
* @param[in] year The year. Probably should be ≥ 1582, but needs to be ≥ 1. | ||
* @param[in] mon The month, TM_MON_JAN to TM_MON_DEC. | ||
* @param[in] day The day in the month, 1 to 31. | ||
* @param[out] wday Returns the day of the week. | ||
* @param[out] yday Returns the day of the year (Jan 1st is 0). | ||
*/ | ||
void tm_get_wyday(int year, int mon, int mday, int *wday, int *yday); | ||
|
||
/** | ||
* @brief Fills in `struct tm::tm_wday` and `struct tm::tm_yday` given a date. | ||
* @details `struct tm::tm_year`, `struct tm::tm_mon`, and `struct tm::tm_mday` | ||
* need to be set before you call this function. | ||
* @param[in,out] The datum to operate on. | ||
*/ | ||
void tm_fill_derived_values(struct tm *tm); | ||
|
||
/** | ||
* @brief Tests if a date is valid. | ||
* @details Dates before 1582-10-15 are invalid. | ||
* @param[in] year The year. | ||
* @param[in] mon The month. | ||
* @param[in] day The day in the month. | ||
* @returns 0 iff the date is invalid. | ||
*/ | ||
int tm_is_valid_date(int year, int mon, int mday) CONST; | ||
|
||
/** | ||
* @brief Shallow test if a time is valid. | ||
* @details This function accepts leap seconds at any given time, because the timezone is unknown. | ||
* @param[in] hour The hour. | ||
* @param[in] min The minutes. | ||
* @param[in] sec The seconds. | ||
* @returns 0 iff the time is invalid. | ||
*/ | ||
int tm_is_valid_time(int hour, int min, int sec) CONST; | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
/* | ||
* Copyright (C) 2014 René Kijewski <[email protected]> | ||
* | ||
* This library is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU Lesser General Public | ||
* License as published by the Free Software Foundation; either | ||
* version 2.1 of the License, or (at your option) any later version. | ||
* | ||
* This library 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 | ||
* Lesser General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Lesser General Public | ||
* License along with this library; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
*/ | ||
|
||
/** | ||
* @file | ||
* @author René Kijewski <[email protected]> | ||
*/ | ||
|
||
#include "tm.h" | ||
|
||
#include <stdint.h> | ||
|
||
const int8_t TM_MON_DAYS[12] = { | ||
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 | ||
}; | ||
|
||
const int16_t TM_MON_DAYS_ACCU[12] = { | ||
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 | ||
}; | ||
|
||
int tm_is_leap_year(unsigned year) | ||
{ | ||
return ((year & 3) == 0) && ((year % 400 == 0) || (year % 100 != 0)); | ||
} | ||
|
||
int tm_doomsday(int year) | ||
{ | ||
int result; | ||
result = TM_WDAY_TUE; | ||
result += year; | ||
result += year >>= 2; | ||
result -= year /= 25; | ||
result += year >>= 2; | ||
return result; | ||
} | ||
|
||
void tm_get_wyday(int year, int mon, int mday, int *wday, int *yday) | ||
{ | ||
int is_leap_year = tm_is_leap_year(year); | ||
*yday = TM_MON_DAYS_ACCU[mon] + mday + (mon <= TM_MON_FEB ? 0 : is_leap_year) - 1; | ||
int jan1 = tm_doomsday(year) - 2 - is_leap_year; | ||
*wday = (jan1 + *yday) % 7; | ||
} | ||
|
||
void tm_fill_derived_values(struct tm *tm) | ||
{ | ||
tm_get_wyday(tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, &tm->tm_wday, &tm->tm_yday); | ||
} | ||
|
||
int tm_is_valid_date(int year, int mon, int mday) | ||
{ | ||
if ((mon < TM_MON_JAN) || (mon > TM_MON_DEC)) { | ||
return 0; | ||
} | ||
if ((mday <= 0) || (mday > TM_MON_DAYS[mon])) { | ||
if ((mday != 29) || (mon != TM_MON_FEB) || !tm_is_leap_year(year)) { | ||
return 0; | ||
} | ||
} | ||
if (year <= 1582) { | ||
if (year < 1582) { | ||
return 0; | ||
} | ||
if ((mon < TM_MON_OCT) || ((mon == TM_MON_OCT) && (mday < 15))) { | ||
return 0; | ||
} | ||
} | ||
|
||
return 1; | ||
} | ||
|
||
int tm_is_valid_time(int hour, int min, int sec) | ||
{ | ||
return (hour >= 0) && (hour < 24) && | ||
(min >= 0) && (min < 60) && | ||
(sec >= 0) && (sec <= 60); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
PROJECT = test_struct_tm_utility | ||
include ../Makefile.tests_common | ||
|
||
DISABLE_MODULE += auto_init | ||
|
||
USEMODULE += shell | ||
USEMODULE += posix | ||
USEMODULE += timex | ||
|
||
# The MSP-430 toolchain lacks sscanf: | ||
BOARD_BLACKLIST := chronos msb-430 msb-430h telosb wsn430-v1_3b wsn430-v1_4 z1 | ||
|
||
# Too little RAM | ||
BOARD_BLACKLIST += redbee-econotag | ||
|
||
include $(RIOTBASE)/Makefile.include |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
/** | ||
* @ingroup tests | ||
* @{ | ||
* @file | ||
* @brief Test the `struct tm` helpers in "tm.h" of the module "timex". | ||
* @author René Kijewski <[email protected]> | ||
* @} | ||
*/ | ||
|
||
#include <stdio.h> | ||
|
||
#include "shell.h" | ||
#include "posix_io.h" | ||
#include "board_uart0.h" | ||
#include "tm.h" | ||
|
||
#define SHELL_BUFSIZE (UART0_BUFSIZE) | ||
|
||
static const char MON_NAMES[12][3] = { | ||
"JAN", "FEB", "MAR", "APR", | ||
"MAY", "JUN", "JUL", "AUG", | ||
"SEP", "OCT", "NOV", "DEC", | ||
}; | ||
static const char DAY_NAMES[7][3] = { | ||
"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" | ||
}; | ||
static const char BOOL_NAMES[2][3] = { "NO", "YES" }; | ||
|
||
static void cmd_days_in(int argc, char **argv) | ||
{ | ||
int mon; | ||
if ((argc != 2) || (sscanf(argv[1], "%d", &mon) != 1) || (mon < 1) || (mon > 12)) { | ||
printf("Usage: %s <Month[1..12]>\n", argv[0]); | ||
} | ||
else { | ||
printf("There are %d days in %.3s in common years.\n", | ||
TM_MON_DAYS[mon - 1], MON_NAMES[mon - 1]); | ||
} | ||
} | ||
|
||
static void cmd_leap_year(int argc, char **argv) | ||
{ | ||
int year; | ||
if ((argc != 2) || (sscanf(argv[1], "%d", &year) != 1)) { | ||
printf("Usage: %s <Year>\n", argv[0]); | ||
} | ||
else { | ||
printf("Was %d a leap year? %.3s.\n", | ||
year, BOOL_NAMES[tm_is_leap_year(year)]); | ||
} | ||
} | ||
|
||
static void cmd_doomsday(int argc, char **argv) | ||
{ | ||
int year; | ||
if ((argc != 2) || (sscanf(argv[1], "%d", &year) != 1)) { | ||
printf("Usage: %s <Year>\n", argv[0]); | ||
} | ||
else { | ||
printf("What weekday was MAR 0 of %d? %.3s.\n", | ||
year, DAY_NAMES[tm_doomsday(year) % 7]); | ||
} | ||
} | ||
|
||
static void cmd_day(int argc, char **argv) | ||
{ | ||
int year, mon, day; | ||
if ((argc != 4) || (sscanf(argv[1], "%d", &year) != 1) | ||
|| (sscanf(argv[2], "%d", &mon) != 1) | ||
|| (sscanf(argv[3], "%d", &day) != 1)) { | ||
printf("Usage: %s <Year> <Month[1..12]> <Day[1..31]>\n", argv[0]); | ||
} | ||
else { | ||
if (!tm_is_valid_date(year, mon - 1, day)) { | ||
puts("The supplied date is invalid, but no error should occur."); | ||
} | ||
|
||
int wday, yday; | ||
tm_get_wyday(year, mon - 1, day, &wday, &yday); | ||
printf("What weekday was %04d-%02d-%02d? The %d(th) day of the year was a %.3s.\n", | ||
year, mon, day, yday + 1, DAY_NAMES[wday]); | ||
} | ||
} | ||
|
||
static const shell_command_t shell_commands[] = { | ||
{ "days_in", "Tells you the number of days in a month.", cmd_days_in }, | ||
{ "leap_year", "Tells you if a supplied year is a leap year.", cmd_leap_year }, | ||
{ "doomsday", "Tells you the wday Doomsday of the supplied year.", cmd_doomsday }, | ||
{ "day", "Tells you the day of the supplied date.", cmd_day }, | ||
{ NULL, NULL, NULL } | ||
}; | ||
|
||
static int shell_readc(void) | ||
{ | ||
char c; | ||
int result = posix_read(uart0_handler_pid, &c, 1); | ||
if (result != 1) { | ||
return -1; | ||
} | ||
return (unsigned char) c; | ||
} | ||
|
||
static void shell_putchar(int c) | ||
{ | ||
putchar(c); | ||
} | ||
|
||
int main(void) | ||
{ | ||
board_uart0_init(); | ||
posix_open(uart0_handler_pid, 0); | ||
|
||
shell_t shell; | ||
shell_init(&shell, shell_commands, SHELL_BUFSIZE, shell_readc, shell_putchar); | ||
|
||
puts("`struct tm` utility shell."); | ||
shell_run(&shell); | ||
|
||
return 0; | ||
} |