403 lines
12 KiB
C
403 lines
12 KiB
C
/// @file time.c
|
|
/// @brief Clock functions.
|
|
/// @copyright (c) 2014-2023 This file is distributed under the MIT License.
|
|
/// See LICENSE.md for details.
|
|
|
|
#include "time.h"
|
|
#include "system/syscall_types.h"
|
|
#include "sys/errno.h"
|
|
#include "string.h"
|
|
#include "stdio.h"
|
|
|
|
/// @brief List of week days name.
|
|
static const char *weekdays[] = {
|
|
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
|
|
};
|
|
|
|
/// @brief List of months name.
|
|
static const char *months[] = {
|
|
"January", "February", "March", "April", "May", "June",
|
|
"July", "August", "September", "October", "November", "December"
|
|
};
|
|
|
|
/// @brief Time function.
|
|
_syscall1(time_t, time, time_t *, t)
|
|
|
|
time_t difftime(time_t time1, time_t time2)
|
|
{
|
|
return time1 - time2;
|
|
}
|
|
|
|
/// @brief Computes day of week
|
|
/// @param y Year
|
|
/// @param m Month of year (in range 1 to 12)
|
|
/// @param d Day of month (in range 1 to 31)
|
|
/// @return Day of week (in range 1 to 7)
|
|
static inline int day_of_week(unsigned int y, unsigned int m, unsigned int d)
|
|
{
|
|
int h, j, k;
|
|
// January and February are counted as months 13 and 14 of the previous year
|
|
if (m <= 2) {
|
|
m += 12;
|
|
y -= 1;
|
|
}
|
|
// J is the century
|
|
j = (int)(y / 100);
|
|
// K the year of the century
|
|
k = (int)(y % 100);
|
|
// Compute H using Zeller's congruence
|
|
h = (int)(d + (26 * (m + 1) / 10) + k + (k / 4) + (5 * j) + (j / 4));
|
|
// Return the day of the week
|
|
return ((h + 5) % 7) + 1;
|
|
}
|
|
|
|
tm_t *localtime(const time_t *time)
|
|
{
|
|
static tm_t date;
|
|
unsigned int a, b, c, d, e, f;
|
|
time_t t = *time;
|
|
// Negative Unix time values are not supported
|
|
if (t < 1) {
|
|
t = 0;
|
|
}
|
|
//Retrieve hours, minutes and seconds
|
|
date.tm_sec = (int)(t % 60);
|
|
t /= 60;
|
|
date.tm_min = (int)(t % 60);
|
|
t /= 60;
|
|
date.tm_hour = (int)(t % 24);
|
|
t /= 24;
|
|
// Convert Unix time to date
|
|
a = (unsigned int)((4 * t + 102032) / 146097 + 15);
|
|
b = (unsigned int)(t + 2442113 + a - (a / 4));
|
|
c = (20 * b - 2442) / 7305;
|
|
d = b - 365 * c - (c / 4);
|
|
e = d * 1000 / 30601;
|
|
f = d - e * 30 - e * 601 / 1000;
|
|
// January and February are counted as months 13 and 14 of the previous year
|
|
if (e <= 13) {
|
|
c -= 4716;
|
|
e -= 1;
|
|
} else {
|
|
c -= 4715;
|
|
e -= 13;
|
|
}
|
|
//Retrieve year, month and day
|
|
date.tm_year = (int)c;
|
|
date.tm_mon = (int)e;
|
|
date.tm_mday = (int)f;
|
|
// Calculate day of week.
|
|
date.tm_wday = day_of_week(date.tm_year, date.tm_mon, date.tm_mday);
|
|
return &date;
|
|
}
|
|
|
|
size_t strftime(char *str, size_t maxsize, const char *format, const tm_t *timeptr)
|
|
{
|
|
if (format == NULL || str == NULL || maxsize <= 0) {
|
|
return 0;
|
|
}
|
|
char *f = (char *)format;
|
|
char *s = str;
|
|
int value;
|
|
size_t ret = 0;
|
|
while ((*f) != '\0') {
|
|
if ((*f) != '%') {
|
|
(*s++) = (*f);
|
|
} else {
|
|
++f;
|
|
switch (*f) {
|
|
#if 0
|
|
case 'a': /* locale's abbreviated weekday name */
|
|
PUT_STRN(weekdays[timeptr->tm_wday], 3);
|
|
break;
|
|
|
|
case 'A': /* locale's full weekday name */
|
|
PUT_STR(weekdays[timeptr->tm_wday]);
|
|
break;
|
|
#endif
|
|
case 'b': // Abbreviated month name
|
|
{
|
|
strncat(s, months[timeptr->tm_mon], 3);
|
|
break;
|
|
}
|
|
case 'B': // Full month name
|
|
{
|
|
unsigned int i = (unsigned int)timeptr->tm_mon;
|
|
strcat(s, months[i]);
|
|
break;
|
|
}
|
|
case 'd': /* day of month as decimal number (01-31) */
|
|
{
|
|
value = timeptr->tm_mday;
|
|
(*s++) = (char)('0' + ((value / 10) % 10));
|
|
(*s++) = (char)('0' + (value % 10));
|
|
break;
|
|
}
|
|
case 'H': /* hour (24-hour clock) as decimal (00-23) */
|
|
{
|
|
value = timeptr->tm_hour;
|
|
(*s++) = (char)('0' + ((value / 10) % 10));
|
|
(*s++) = (char)('0' + (value % 10));
|
|
break;
|
|
}
|
|
#if 0
|
|
case 'I': /* hour (12-hour clock) as decimal (01-12) */
|
|
SPRINTF("%02d", ((timeptr->tm_hour + 11) % 12) + 1);
|
|
break;
|
|
#endif
|
|
case 'j': // Day of year as decimal number (001-366)
|
|
{
|
|
value = timeptr->tm_yday;
|
|
(*s++) = (char)('0' + ((value / 10) % 10));
|
|
(*s++) = (char)('0' + (value % 10));
|
|
break;
|
|
}
|
|
case 'm': // Month as decimal number (01-12)
|
|
{
|
|
value = timeptr->tm_mon;
|
|
(*s++) = (char)('0' + ((value / 10) % 10));
|
|
(*s++) = (char)('0' + (value % 10));
|
|
break;
|
|
}
|
|
#if 0
|
|
case 'M': /* month as decimal number (00-59) */
|
|
SPRINTF("%02d", timeptr->tm_min);
|
|
break;
|
|
|
|
case 'p': /* AM/PM designations (12-hour clock) */
|
|
PUT_STR(timeptr->tm_hour < 12 ? "AM" : "PM");
|
|
/* s.b. locale-specific */
|
|
break;
|
|
|
|
case 'S': /* second as decimal number (00-61) */
|
|
SPRINTF("%02d", timeptr->tm_sec);
|
|
break;
|
|
|
|
case 'U': /* week of year (first Sunday = 1) (00-53) */
|
|
case 'W': /* week of year (first Monday = 1) (00-53) */
|
|
{
|
|
int fudge = timeptr->tm_wday - timeptr->tm_yday % 7;
|
|
if (*fp == 'W') {
|
|
fudge--;
|
|
}
|
|
fudge = (fudge + 7) % 7; /* +7 so not negative */
|
|
SPRINTF("%02d", (timeptr->tm_yday + fudge) / 7);
|
|
break;
|
|
}
|
|
|
|
case 'w': /* weekday (0-6), Sunday is 0 */
|
|
SPRINTF("%02d", timeptr->tm_wday);
|
|
break;
|
|
|
|
case 'x': /* appropriate date representation */
|
|
RECUR("%B %d, %Y"); /* s.b. locale-specific */
|
|
break;
|
|
|
|
case 'X': /* appropriate time representation */
|
|
RECUR("%H:%M:%S"); /* s.b. locale-specific */
|
|
break;
|
|
|
|
case 'y': /* year without century as decimal (00-99) */
|
|
SPRINTF("%02d", timeptr->tm_year % 100);
|
|
break;
|
|
|
|
case 'Y': /* year with century as decimal */
|
|
SPRINTF("%d", timeptr->tm_year + 1900);
|
|
break;
|
|
|
|
case 'Z': /* time zone name or abbreviation */
|
|
/* XXX %%% */
|
|
break;
|
|
|
|
case '%':
|
|
PUT_CH('%');
|
|
break;
|
|
#endif
|
|
default:
|
|
(*s++) = '%';
|
|
(*s++) = (*f);
|
|
}
|
|
}
|
|
++f;
|
|
}
|
|
|
|
#if 0
|
|
#define PUT_CH(c) (ret < maxsize ? (*dp++ = (c), ret++) : 0)
|
|
#define PUT_STR(str) \
|
|
p = (char *)(str); \
|
|
goto dostr;
|
|
#define PUT_STRN(str, n) \
|
|
p = (char *)(str); \
|
|
len = (n); \
|
|
goto dostrn;
|
|
#define SPRINTF(fmt, arg) \
|
|
sprintf(tmpbuf, fmt, arg); \
|
|
p = tmpbuf; \
|
|
goto dostr;
|
|
#define RECUR(fmt) \
|
|
{ \
|
|
size_t r = strftime(dp, maxsize - ret, fmt, timeptr); \
|
|
if (r <= 0) \
|
|
return r; \
|
|
dp += r; \
|
|
ret += r; \
|
|
}
|
|
|
|
char* fp = (char*) format;
|
|
char* dp = s;
|
|
size_t ret = 0;
|
|
|
|
if (format == NULL || s == NULL || maxsize <= 0) {
|
|
return 0;
|
|
}
|
|
|
|
while (*fp != '\0') {
|
|
char* p;
|
|
int len;
|
|
char tmpbuf[10];
|
|
|
|
if (*fp != '%') {
|
|
PUT_CH(*fp++);
|
|
continue;
|
|
}
|
|
|
|
fp++;
|
|
|
|
switch (*fp) {
|
|
|
|
case 'a': /* locale's abbreviated weekday name */
|
|
PUT_STRN(weekdays[timeptr->tm_wday], 3);
|
|
break;
|
|
|
|
case 'A': /* locale's full weekday name */
|
|
PUT_STR(weekdays[timeptr->tm_wday]);
|
|
break;
|
|
|
|
case 'b': /* locale's abbreviated month name */
|
|
PUT_STRN(months[timeptr->tm_mon], 3);
|
|
break;
|
|
|
|
case 'B': /* locale's full month name */
|
|
PUT_STR(months[timeptr->tm_mon]);
|
|
break;
|
|
|
|
case 'c': /* appropriate date and time representation */
|
|
RECUR("%X %x"); /* s.b. locale-specific */
|
|
break;
|
|
|
|
case 'd': /* day of month as decimal number (01-31) */
|
|
SPRINTF("%02d", timeptr->tm_mday);
|
|
break;
|
|
|
|
case 'H': /* hour (24-hour clock) as decimal (00-23) */
|
|
SPRINTF("%02d", timeptr->tm_hour);
|
|
break;
|
|
|
|
case 'I': /* hour (12-hour clock) as decimal (01-12) */
|
|
SPRINTF("%02d", ((timeptr->tm_hour + 11) % 12) + 1);
|
|
break;
|
|
|
|
case 'j': /* day of year as decimal number (001-366) */
|
|
SPRINTF("%03d", timeptr->tm_yday + 1);
|
|
break;
|
|
|
|
case 'm': /* month as decimal number (01-12) */
|
|
SPRINTF("%02d", timeptr->tm_mon + 1);
|
|
break;
|
|
|
|
case 'M': /* month as decimal number (00-59) */
|
|
SPRINTF("%02d", timeptr->tm_min);
|
|
break;
|
|
|
|
case 'p': /* AM/PM designations (12-hour clock) */
|
|
PUT_STR(timeptr->tm_hour < 12 ? "AM" : "PM");
|
|
/* s.b. locale-specific */
|
|
break;
|
|
|
|
case 'S': /* second as decimal number (00-61) */
|
|
SPRINTF("%02d", timeptr->tm_sec);
|
|
break;
|
|
|
|
case 'U': /* week of year (first Sunday = 1) (00-53) */
|
|
case 'W': /* week of year (first Monday = 1) (00-53) */
|
|
{
|
|
int fudge = timeptr->tm_wday - timeptr->tm_yday % 7;
|
|
if (*fp == 'W') {
|
|
fudge--;
|
|
}
|
|
fudge = (fudge + 7) % 7; /* +7 so not negative */
|
|
SPRINTF("%02d", (timeptr->tm_yday + fudge) / 7);
|
|
break;
|
|
}
|
|
|
|
case 'w': /* weekday (0-6), Sunday is 0 */
|
|
SPRINTF("%02d", timeptr->tm_wday);
|
|
break;
|
|
|
|
case 'x': /* appropriate date representation */
|
|
RECUR("%B %d, %Y"); /* s.b. locale-specific */
|
|
break;
|
|
|
|
case 'X': /* appropriate time representation */
|
|
RECUR("%H:%M:%S"); /* s.b. locale-specific */
|
|
break;
|
|
|
|
case 'y': /* year without century as decimal (00-99) */
|
|
SPRINTF("%02d", timeptr->tm_year % 100);
|
|
break;
|
|
|
|
case 'Y': /* year with century as decimal */
|
|
SPRINTF("%d", timeptr->tm_year + 1900);
|
|
break;
|
|
|
|
case 'Z': /* time zone name or abbreviation */
|
|
/* XXX %%% */
|
|
break;
|
|
|
|
case '%':
|
|
PUT_CH('%');
|
|
break;
|
|
|
|
dostr:
|
|
while (*p != '\0')
|
|
PUT_CH(*p++);
|
|
break;
|
|
|
|
dostrn:
|
|
while (len-- > 0)
|
|
PUT_CH(*p++);
|
|
break;
|
|
}
|
|
|
|
++fp;
|
|
}
|
|
if (ret >= maxsize) {
|
|
s[maxsize - 1] = '\0';
|
|
return 0;
|
|
}
|
|
*dp = '\0';
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
/// @brief nanosleep function.
|
|
_syscall2(int, nanosleep, const timespec *, req, timespec *, rem)
|
|
|
|
unsigned int sleep(unsigned int seconds)
|
|
{
|
|
timespec req, rem;
|
|
req.tv_sec = seconds;
|
|
// Call the nanosleep.
|
|
int __ret = nanosleep(&req, &rem);
|
|
// If the call to the nanosleep is interrupted by a signal handler,
|
|
// then it returns -1 with errno set to EINTR.
|
|
if ((__ret == -1) && (errno == EINTR)) {
|
|
return rem.tv_sec;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
_syscall2(int, getitimer, int, which, itimerval *, curr_value)
|
|
|
|
_syscall3(int, setitimer, int, which, const itimerval *, new_value, itimerval *, old_value)
|