Implement localtime
Now we manage a timezone and daylight-savings aware version of localtime. We parse UNIX timezone strings. The default (active after the first call to localtime or localtime_r) is CET/CEST, the timezone of Europe/Vienna. The wallclock-time osd-example demonstrates how to set a different timezone via the timezone resource. Note: After startup no timezone is set. So in this state querying the timezone resource will return an empty string. After first call to localtime (if not timezone has been set via the timezone resource) a query to timezone will return the default timezone string for CET/CEST. The string returned by the localtime and utc timezones now also includes the timezone name. New fields tm_gmtoff and tm_zone were added to the tm structure. These are available in BSD systems and when setting special compiler definitions on Linux. Note: the timezone offset information in the tm structure (tm_gmtoff) as well as in the tz structure returned by gettimeofday (tz_minuteswest) may be wrong sign, this code is largely untested.
This commit is contained in:
parent
b8c5ee0e2b
commit
0068611b4d
|
@ -1 +1 @@
|
|||
time_src = time.c resource_gmtime.c resource_timestamp.c
|
||||
time_src = time.c resource_gmtime.c resource_timestamp.c resource_timezone.c
|
||||
|
|
20
apps/time/README
Normal file
20
apps/time/README
Normal file
|
@ -0,0 +1,20 @@
|
|||
Timezones
|
||||
=========
|
||||
|
||||
The new version supports time zones and daylight saving time (DST).
|
||||
Currently we support only a single timezone. We use the UNIX timezone
|
||||
format which is usually specified in an environment variable TZ.
|
||||
|
||||
Note that for timezone information you can have different
|
||||
representation, either relative to Universal Time Coordinated (UTC) or
|
||||
to International Atomic Time (TAI), the latter contains leap seconds.
|
||||
Since most systems today use UTC *and* the clock of a microcontroller
|
||||
is typically not accurate enough to care about leap seconds, we're using
|
||||
timezone files relative to UTC.
|
||||
|
||||
Wikipedia has a very good treatment of the public timezone database in
|
||||
https://en.wikipedia.org/wiki/Tz_database
|
||||
|
||||
The format of timezone strings is described in the Linux manual page
|
||||
tzset(3). The timezone specification for Europe/Vienna is
|
||||
CET-1CEST,M3.5.0,M10.5.0/3
|
|
@ -30,10 +30,11 @@ size_t time_to_string (const char *name, uint8_t is_json, char *buf, size_t bs)
|
|||
return snprintf
|
||||
( buf
|
||||
, bs
|
||||
, "%s%lu-%02u-%02u %02u:%02u:%02u%s"
|
||||
, "%s%lu-%02u-%02u %02u:%02u:%02u %s%s"
|
||||
, is_json ? "\"" : ""
|
||||
, 1900 + tm.tm_year
|
||||
, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec
|
||||
, tm.tm_zone
|
||||
, is_json ? "\"" : ""
|
||||
);
|
||||
}
|
||||
|
|
46
apps/time/resource_timezone.c
Normal file
46
apps/time/resource_timezone.c
Normal file
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* \file
|
||||
* Resource for timezone handling
|
||||
* \author
|
||||
* Ralf Schlatterbeck <rsc@runtux.com>
|
||||
*
|
||||
* \brief get/put timezone string
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "contiki.h"
|
||||
#include "time.h"
|
||||
#include "time_resource.h"
|
||||
#include "jsonparse.h"
|
||||
#include "er-coap.h"
|
||||
#include "generic_resource.h"
|
||||
|
||||
void timezone_from_string (const char *name, const char *s)
|
||||
{
|
||||
set_tz (s);
|
||||
}
|
||||
|
||||
size_t
|
||||
timezone_to_string (const char *name, uint8_t is_json, char *buf, size_t bsize)
|
||||
{
|
||||
if (get_tz (buf, bsize) == NULL) {
|
||||
*buf = '\0';
|
||||
}
|
||||
return strlen (buf);
|
||||
}
|
||||
|
||||
GENERIC_RESOURCE
|
||||
( timezone
|
||||
, TZ
|
||||
, s
|
||||
, timezone_from_string
|
||||
, timezone_to_string
|
||||
);
|
||||
|
||||
/*
|
||||
* VI settings, see coding style
|
||||
* ex:ts=8:et:sw=2
|
||||
*/
|
||||
|
738
apps/time/time.c
738
apps/time/time.c
|
@ -3,10 +3,35 @@
|
|||
*
|
||||
* @{
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include "contiki.h"
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "contiki-lib.h"
|
||||
#include "time.h"
|
||||
#include "tzparse.h"
|
||||
|
||||
#define SECSPERMIN 60
|
||||
#define MINSPERHOUR 60
|
||||
#define HOURSPERDAY 24
|
||||
#define DAYSPERWEEK 7
|
||||
#define DAYSPERNYEAR 365
|
||||
#define DAYSPERLYEAR 366
|
||||
#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
|
||||
#define SECSPERDAY ((long) SECSPERHOUR * HOURSPERDAY)
|
||||
#define MONSPERYEAR 12
|
||||
|
||||
static const int mon_lengths[2][MONSPERYEAR] =
|
||||
{ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
|
||||
, { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
|
||||
};
|
||||
|
||||
/*
|
||||
* Static timezone information
|
||||
*/
|
||||
static struct tzoffset_info localtime_tzoffset;
|
||||
|
||||
/* Used for gmtime and localtime, according to manpage on linux the
|
||||
* internal value may be overwritten "by subsequent calls to any of the
|
||||
|
@ -23,16 +48,17 @@ static struct tm tm;
|
|||
* The last_seconds is used to check if we had a seconds overflow,
|
||||
* although this happens only every 136 years :-)
|
||||
*/
|
||||
time_t clock_offset;
|
||||
uint32_t last_seconds;
|
||||
int16_t minuteswest;
|
||||
static time_t clock_offset;
|
||||
static uint32_t last_seconds;
|
||||
|
||||
static time_t
|
||||
transtime (time_t janfirst, int year, const struct tzrule *rp, long offset);
|
||||
|
||||
# define LEAP_YEAR(_year) \
|
||||
((_year % 4) == 0 && (_year % 100 != 0 || _year % 400 == 0))
|
||||
# define YDAYS(_year) (LEAP_YEAR(year) ? 366 : 365)
|
||||
|
||||
struct tm *
|
||||
gmtime_r (const time_t *timep, struct tm *ptm)
|
||||
struct tm *gmtime_r (const time_t *timep, struct tm *ptm)
|
||||
{
|
||||
unsigned int year;
|
||||
int days, month, month_len;
|
||||
|
@ -51,14 +77,14 @@ gmtime_r (const time_t *timep, struct tm *ptm)
|
|||
year++;
|
||||
}
|
||||
ptm->tm_year = year - 1900;
|
||||
days -= YDAYS(year);
|
||||
days -= YDAYS (year);
|
||||
t -= days;
|
||||
ptm->tm_yday = t;
|
||||
for (month=0; month<12; month++)
|
||||
{
|
||||
if (month == 1)
|
||||
{
|
||||
month_len = LEAP_YEAR(year) ? 29 : 28;
|
||||
month_len = LEAP_YEAR (year) ? 29 : 28;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -79,28 +105,70 @@ gmtime_r (const time_t *timep, struct tm *ptm)
|
|||
break;
|
||||
}
|
||||
}
|
||||
ptm->tm_mon = month;
|
||||
ptm->tm_mday = t + 1;
|
||||
ptm->tm_isdst = 0;
|
||||
ptm->tm_mon = month;
|
||||
ptm->tm_mday = t + 1;
|
||||
ptm->tm_isdst = 0;
|
||||
ptm->tm_gmtoff = 0;
|
||||
ptm->tm_zone = "UTC";
|
||||
return ptm;
|
||||
}
|
||||
|
||||
struct tm *
|
||||
gmtime (const time_t *timep)
|
||||
struct tm *gmtime (const time_t *timep)
|
||||
{
|
||||
return gmtime_r (timep, &tm);
|
||||
}
|
||||
|
||||
struct tm *
|
||||
localtime_r (const time_t *timep, struct tm *ptm)
|
||||
/*
|
||||
* Compute is_dst flag of given timestamp
|
||||
*/
|
||||
static int is_dst (const time_t *timep, const struct tzoffset_info *tzo)
|
||||
{
|
||||
time_t t = *timep;
|
||||
t += minuteswest * 60;
|
||||
return gmtime_r (&t, ptm);
|
||||
time_t janfirst = 0;
|
||||
time_t starttime, endtime;
|
||||
int year = 1970;
|
||||
int lastdst = 0;
|
||||
if (tzo->dstname == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (year = 1970; janfirst < *timep; year++) {
|
||||
starttime = transtime (janfirst, year, &tzo->start, tzo->stdoffset);
|
||||
endtime = transtime (janfirst, year, &tzo->end, tzo->dstoffset);
|
||||
if (starttime <= *timep && endtime <= *timep) {
|
||||
lastdst = (starttime > endtime);
|
||||
} else if (starttime > *timep && endtime <= *timep) {
|
||||
return 0;
|
||||
} else if (starttime <= *timep && endtime > *timep) {
|
||||
return 1;
|
||||
} else if (starttime > *timep && endtime > *timep) {
|
||||
return lastdst;
|
||||
}
|
||||
janfirst += YDAYS (year) * SECSPERDAY;
|
||||
}
|
||||
return lastdst;
|
||||
}
|
||||
|
||||
struct tm *
|
||||
localtime (const time_t *timep)
|
||||
struct tm *localtime_r (const time_t *timep, struct tm *ptm)
|
||||
{
|
||||
const struct tzoffset_info *tzo = &localtime_tzoffset;
|
||||
int isdst = 0;
|
||||
long offset = 0;
|
||||
time_t t = *timep;
|
||||
|
||||
if (tzo->stdname == NULL) {
|
||||
set_tz (DEFAULT_TIMEZONE);
|
||||
}
|
||||
isdst = is_dst (timep, tzo);
|
||||
offset = isdst ? tzo->dstoffset : tzo->stdoffset;
|
||||
t -= offset;
|
||||
gmtime_r (&t, ptm);
|
||||
ptm->tm_isdst = isdst;
|
||||
ptm->tm_gmtoff = -offset;
|
||||
ptm->tm_zone = isdst ? tzo->dstname : tzo->stdname;
|
||||
return ptm;
|
||||
}
|
||||
|
||||
struct tm *localtime (const time_t *timep)
|
||||
{
|
||||
return localtime_r (timep, &tm);
|
||||
}
|
||||
|
@ -114,14 +182,9 @@ localtime (const time_t *timep)
|
|||
* repeatedly reading seconds, sub-seconds, seconds until first and
|
||||
* second read of seconds match.
|
||||
*/
|
||||
int
|
||||
gettimeofday (struct timeval *tv, struct timezone *tz)
|
||||
int gettimeofday (struct timeval *tv, struct timezone *tz)
|
||||
{
|
||||
uint32_t cs;
|
||||
if (tz) {
|
||||
tz->tz_minuteswest = minuteswest;
|
||||
tz->tz_dsttime = 0;
|
||||
}
|
||||
if (tv) {
|
||||
int i;
|
||||
/* Limit tries to get the same second twice to two */
|
||||
|
@ -140,14 +203,22 @@ gettimeofday (struct timeval *tv, struct timezone *tz)
|
|||
}
|
||||
}
|
||||
}
|
||||
if (tz) {
|
||||
const struct tzoffset_info *tzo = &localtime_tzoffset;
|
||||
tz->tz_dsttime = is_dst (&tv->tv_sec, tzo);
|
||||
if (tz->tz_dsttime) {
|
||||
tz->tz_minuteswest = -tzo->dstoffset / 60;
|
||||
} else {
|
||||
tz->tz_minuteswest = -tzo->stdoffset / 60;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set time in seconds, microseconds ignored for now
|
||||
*/
|
||||
int
|
||||
settimeofday (const struct timeval *tv, const struct timezone *tz)
|
||||
int settimeofday (const struct timeval *tv, const struct timezone *tz)
|
||||
{
|
||||
/* Don't allow setting timezone */
|
||||
if (tz) {
|
||||
|
@ -162,4 +233,615 @@ settimeofday (const struct timeval *tv, const struct timezone *tz)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Save timezone names into reserved string buffer and fill in the names
|
||||
* into the given tzoffset_info.
|
||||
* Return -1 on error, 0 for success.
|
||||
*/
|
||||
static int save_tznames
|
||||
( const char *stdname, const char *dstname
|
||||
, size_t stdlen, size_t dstlen
|
||||
, struct tzoffset_info *tzo
|
||||
)
|
||||
{
|
||||
size_t len = stdlen;
|
||||
if (stdname == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (dstname != NULL) {
|
||||
len += dstlen;
|
||||
}
|
||||
if (len + 2 > sizeof (tzo->namebuf)) {
|
||||
return -1;
|
||||
}
|
||||
tzo->stdname = tzo->namebuf;
|
||||
strncpy (tzo->namebuf, stdname, stdlen);
|
||||
tzo->namebuf [stdlen] = '\0';
|
||||
if (dstlen) {
|
||||
strncpy (tzo->namebuf + stdlen + 1, dstname, dstlen);
|
||||
tzo->namebuf [stdlen + 1 + dstlen] = '\0';
|
||||
tzo->dstname = tzo->namebuf + stdlen + 1;
|
||||
} else {
|
||||
tzo->dstname = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility functions for timezone string parsing (POSIX section 8)
|
||||
* Code adapted from OpenBSD localtime.c 1.57 2015/12/12 21:25:44
|
||||
* which is in the public domain.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The DST rules to use if TZ has no rules:
|
||||
* We default to US rules as of 1999-08-17.
|
||||
* POSIX 1003.1 section 8.1.1 says that the default DST rules are
|
||||
* implementation dependent; for historical reasons, US rules are a
|
||||
* common default.
|
||||
*/
|
||||
#ifndef TZDEFRULESTRING
|
||||
#define TZDEFRULESTRING ",M4.1.0,M10.5.0"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Given the Epoch-relative time of January 1, 00:00:00 UTC, in a year, the
|
||||
* year, a rule, and the offset from UTC at the time that rule takes effect,
|
||||
* calculate the Epoch-relative time that rule takes effect.
|
||||
*/
|
||||
|
||||
static time_t
|
||||
transtime(time_t janfirst, int year, const struct tzrule *rulep, long offset)
|
||||
{
|
||||
int leapyear;
|
||||
time_t value;
|
||||
int i;
|
||||
int d, m1, yy0, yy1, yy2, dow;
|
||||
|
||||
value = 0;
|
||||
leapyear = LEAP_YEAR (year);
|
||||
switch (rulep->r_type) {
|
||||
|
||||
case JULIAN_DAY:
|
||||
/*
|
||||
* Jn - Julian day, 1 == January 1, 60 == March 1 even in leap
|
||||
* years.
|
||||
* In non-leap years, or if the day number is 59 or less, just
|
||||
* add SECSPERDAY times the day number-1 to the time of
|
||||
* January 1, midnight, to get the day.
|
||||
*/
|
||||
value = janfirst + (rulep->r_day - 1) * SECSPERDAY;
|
||||
if (leapyear && rulep->r_day >= 60) {
|
||||
value += SECSPERDAY;
|
||||
}
|
||||
break;
|
||||
|
||||
case DAY_OF_YEAR:
|
||||
/*
|
||||
* n - day of year.
|
||||
* Just add SECSPERDAY times the day number to the time of
|
||||
* January 1, midnight, to get the day.
|
||||
*/
|
||||
value = janfirst + rulep->r_day * SECSPERDAY;
|
||||
break;
|
||||
|
||||
case MONTH_NTH_DAY_OF_WEEK:
|
||||
/*
|
||||
* Mm.n.d - nth "dth day" of month m.
|
||||
*/
|
||||
value = janfirst;
|
||||
for (i = 0; i < rulep->r_mon - 1; ++i) {
|
||||
value += mon_lengths [leapyear][i] * SECSPERDAY;
|
||||
}
|
||||
|
||||
/*
|
||||
** Use Zeller's Congruence to get day-of-week of first day of
|
||||
** month.
|
||||
*/
|
||||
m1 = (rulep->r_mon + 9) % 12 + 1;
|
||||
yy0 = (rulep->r_mon <= 2) ? (year - 1) : year;
|
||||
yy1 = yy0 / 100;
|
||||
yy2 = yy0 % 100;
|
||||
dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7;
|
||||
if (dow < 0) {
|
||||
dow += DAYSPERWEEK;
|
||||
}
|
||||
|
||||
/*
|
||||
** "dow" is the day-of-week of the first day of the month. Get
|
||||
** the day-of-month (zero-origin) of the first "dow" day of the
|
||||
** month.
|
||||
*/
|
||||
d = rulep->r_day - dow;
|
||||
if (d < 0) {
|
||||
d += DAYSPERWEEK;
|
||||
}
|
||||
for (i = 1; i < rulep->r_week; ++i) {
|
||||
if (d + DAYSPERWEEK >= mon_lengths[leapyear][rulep->r_mon - 1]) {
|
||||
break;
|
||||
}
|
||||
d += DAYSPERWEEK;
|
||||
}
|
||||
|
||||
/*
|
||||
** "d" is the day-of-month (zero-origin) of the day we want.
|
||||
*/
|
||||
value += d * SECSPERDAY;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
** "value" is the Epoch-relative time of 00:00:00 UTC on the day in
|
||||
** question. To get the Epoch-relative time of the specified local
|
||||
** time on that day, add the transition time and the current offset
|
||||
** from UTC.
|
||||
*/
|
||||
return value + rulep->r_time + offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a pointer into a time zone string, scan until a character that is not
|
||||
* a valid character in a zone name is found. Return a pointer to that
|
||||
* character.
|
||||
*/
|
||||
|
||||
static const char *getzname (const char *s)
|
||||
{
|
||||
char c;
|
||||
while ((c = *s) != '\0' && !isdigit(c) && c != ',' && c != '-' && c != '+'){
|
||||
++s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a pointer into an extended time zone string, scan until the ending
|
||||
* delimiter of the zone name is located. Return a pointer to the delimiter.
|
||||
*
|
||||
* As with getzname above, the legal character set is actually quite
|
||||
* restricted, with other characters producing undefined results.
|
||||
* We don't do any checking here; checking is done later in common-case code.
|
||||
*/
|
||||
|
||||
static const char *getqzname (const char *strp, const int delim)
|
||||
{
|
||||
int c;
|
||||
while ((c = *strp) != '\0' && c != delim) {
|
||||
++strp;
|
||||
}
|
||||
return strp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a pointer into a time zone string, extract a number from that string.
|
||||
* Check that the number is within a specified range; if it is not, return
|
||||
* NULL.
|
||||
* Otherwise, return a pointer to the first character not part of the number.
|
||||
*/
|
||||
|
||||
static const char *getnum (const char *strp, int *nump, int min, int max)
|
||||
{
|
||||
char c;
|
||||
int num;
|
||||
|
||||
if (strp == NULL || !isdigit ((c = *strp))) {
|
||||
return NULL;
|
||||
}
|
||||
num = 0;
|
||||
do {
|
||||
num = num * 10 + (c - '0');
|
||||
if (num > max) {
|
||||
return NULL; /* illegal value */
|
||||
}
|
||||
c = *++strp;
|
||||
} while (isdigit (c));
|
||||
if (num < min) {
|
||||
return NULL; /* illegal value */
|
||||
}
|
||||
*nump = num;
|
||||
return strp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a pointer into a time zone string, extract a number of seconds,
|
||||
* in hh[:mm[:ss]] form, from the string.
|
||||
* If any error occurs, return NULL.
|
||||
* Otherwise, return a pointer to the first character not part of the number
|
||||
* of seconds.
|
||||
*/
|
||||
|
||||
static const char *getsecs (const char *strp, long *secsp)
|
||||
{
|
||||
int num;
|
||||
|
||||
/*
|
||||
* `HOURSPERDAY * DAYSPERWEEK - 1' allows quasi-Posix rules like
|
||||
* "M10.4.6/26", which does not conform to Posix,
|
||||
* but which specifies the equivalent of
|
||||
* ``02:00 on the first Sunday on or after 23 Oct''.
|
||||
*/
|
||||
strp = getnum (strp, &num, 0, HOURSPERDAY * DAYSPERWEEK - 1);
|
||||
if (strp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
*secsp = num * (long) SECSPERHOUR;
|
||||
if (*strp == ':') {
|
||||
++strp;
|
||||
strp = getnum (strp, &num, 0, MINSPERHOUR - 1);
|
||||
if (strp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
*secsp += num * SECSPERMIN;
|
||||
if (*strp == ':') {
|
||||
++strp;
|
||||
/* `SECSPERMIN' allows for leap seconds. */
|
||||
strp = getnum (strp, &num, 0, SECSPERMIN);
|
||||
if (strp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
*secsp += num;
|
||||
}
|
||||
}
|
||||
return strp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a pointer into a time zone string, extract an offset, in
|
||||
* [+-]hh[:mm[:ss]] form, from the string.
|
||||
* If any error occurs, return NULL.
|
||||
* Otherwise, return a pointer to the first character not part of the time.
|
||||
*/
|
||||
|
||||
static const char *getoffset (const char *strp, long *offsetp)
|
||||
{
|
||||
int neg = 0;
|
||||
|
||||
if (*strp == '-') {
|
||||
neg = 1;
|
||||
++strp;
|
||||
} else if (*strp == '+') {
|
||||
++strp;
|
||||
}
|
||||
strp = getsecs (strp, offsetp);
|
||||
if (strp == NULL) {
|
||||
return NULL; /* illegal time */
|
||||
}
|
||||
if (neg) {
|
||||
*offsetp = -*offsetp;
|
||||
}
|
||||
return strp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse (optionally extended) timezone name. Return pointer to
|
||||
* (undelimited) timezone name in tzn and length of same in len.
|
||||
* Return pointer to first character *after* name, NULL on error.
|
||||
* Factored from original tzparse function.
|
||||
*/
|
||||
static const char *
|
||||
egettzname (const char *strp, size_t *len, const char **tzn)
|
||||
{
|
||||
*tzn = strp;
|
||||
if (*strp == '<') {
|
||||
strp++;
|
||||
*tzn = strp;
|
||||
strp = getqzname (strp, '>');
|
||||
if (*strp != '>') {
|
||||
return NULL;
|
||||
}
|
||||
*len = strp - *tzn;
|
||||
strp++;
|
||||
} else {
|
||||
strp = getzname (strp);
|
||||
*len = strp - *tzn;
|
||||
}
|
||||
return strp;
|
||||
}
|
||||
|
||||
/*
|
||||
** Given a pointer into a time zone string, extract a rule in the form
|
||||
** date[/time]. See POSIX section 8 for the format of "date" and "time".
|
||||
** If a valid rule is not found, return NULL.
|
||||
** Otherwise, return a pointer to the first character not part of the rule.
|
||||
*/
|
||||
|
||||
static const char *getrule (const char *strp, struct tzrule *rulep)
|
||||
{
|
||||
if (*strp == 'J') {
|
||||
/*
|
||||
* Julian day.
|
||||
*/
|
||||
rulep->r_type = JULIAN_DAY;
|
||||
++strp;
|
||||
strp = getnum (strp, &rulep->r_day, 1, DAYSPERNYEAR);
|
||||
} else if (*strp == 'M') {
|
||||
/*
|
||||
* Month, week, day.
|
||||
*/
|
||||
rulep->r_type = MONTH_NTH_DAY_OF_WEEK;
|
||||
++strp;
|
||||
strp = getnum (strp, &rulep->r_mon, 1, MONSPERYEAR);
|
||||
if (strp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (*strp++ != '.') {
|
||||
return NULL;
|
||||
}
|
||||
strp = getnum (strp, &rulep->r_week, 1, 5);
|
||||
if (strp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (*strp++ != '.') {
|
||||
return NULL;
|
||||
}
|
||||
strp = getnum (strp, &rulep->r_day, 0, DAYSPERWEEK - 1);
|
||||
} else if (isdigit (*strp)) {
|
||||
/*
|
||||
* Day of year.
|
||||
*/
|
||||
rulep->r_type = DAY_OF_YEAR;
|
||||
strp = getnum (strp, &rulep->r_day, 0, DAYSPERLYEAR - 1);
|
||||
} else {
|
||||
return NULL; /* invalid format */
|
||||
}
|
||||
if (strp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (*strp == '/') {
|
||||
/*
|
||||
* Time specified.
|
||||
*/
|
||||
++strp;
|
||||
strp = getsecs (strp, &rulep->r_time);
|
||||
} else {
|
||||
rulep->r_time = 2 * SECSPERHOUR; /* default = 2:00:00 */
|
||||
}
|
||||
return strp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse POSIX section 8 TZ string.
|
||||
* We keep the misnomer "name" for the timezone string.
|
||||
*/
|
||||
int tzparse (const char *name, struct tzoffset_info *tzo)
|
||||
{
|
||||
const char *stdname;
|
||||
const char *dstname;
|
||||
size_t stdlen;
|
||||
size_t dstlen;
|
||||
long stdoffset;
|
||||
long dstoffset;
|
||||
|
||||
dstname = NULL;
|
||||
stdname = name;
|
||||
name = egettzname (name, &stdlen, &stdname);
|
||||
if (name == NULL || *name == '\0') {
|
||||
return -1;
|
||||
}
|
||||
name = getoffset (name, &stdoffset);
|
||||
if (name == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (*name != '\0') {
|
||||
name = egettzname (name, &dstlen, &dstname);
|
||||
if (name == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (*name != '\0' && *name != ',' && *name != ';') {
|
||||
name = getoffset (name, &dstoffset);
|
||||
if (name == NULL) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
dstoffset = stdoffset - SECSPERHOUR;
|
||||
}
|
||||
if (*name == '\0') {
|
||||
name = TZDEFRULESTRING;
|
||||
}
|
||||
if (*name == ',' || *name == ';') {
|
||||
struct tzrule start;
|
||||
struct tzrule end;
|
||||
++name;
|
||||
if ((name = getrule (name, &start)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (*name++ != ',') {
|
||||
return -1;
|
||||
}
|
||||
if ((name = getrule (name, &end)) == NULL) {
|
||||
return -1;
|
||||
}
|
||||
if (*name != '\0') {
|
||||
return -1;
|
||||
}
|
||||
if (save_tznames (stdname, dstname, stdlen, dstlen, tzo) != 0) {
|
||||
return -1;
|
||||
}
|
||||
tzo->start = start;
|
||||
tzo->end = end;
|
||||
tzo->stdoffset = stdoffset;
|
||||
tzo->dstoffset = dstoffset;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
/* only standard time, no DST */
|
||||
if (save_tznames (stdname, NULL, stdlen, 0, tzo) != 0) {
|
||||
return -1;
|
||||
}
|
||||
tzo->stdoffset = stdoffset;
|
||||
tzo->dstoffset = stdoffset;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Provide a single static timezone which is used by localtime et.al.
|
||||
*/
|
||||
int set_tz (const char *tzstring)
|
||||
{
|
||||
return tzparse (tzstring, &localtime_tzoffset);
|
||||
}
|
||||
|
||||
static size_t lensecs (long seconds)
|
||||
{
|
||||
size_t len = 1;
|
||||
long secs = abs (seconds);
|
||||
if (seconds < 0) {
|
||||
len++;
|
||||
}
|
||||
if (secs / 3600 > 9) {
|
||||
len++;
|
||||
}
|
||||
if (secs % 3600) {
|
||||
len += 3;
|
||||
if (secs % 60) {
|
||||
len += 3;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get length of string resulting from serializing rule.
|
||||
*/
|
||||
static size_t lenrule (const struct tzrule *rule)
|
||||
{
|
||||
size_t len = 0;
|
||||
if (rule->r_type == JULIAN_DAY) {
|
||||
len++;
|
||||
}
|
||||
if (rule->r_type == JULIAN_DAY || rule->r_type == DAY_OF_YEAR) {
|
||||
len++;
|
||||
if (rule->r_day > 9) {
|
||||
len++;
|
||||
if (rule->r_day > 99) {
|
||||
len++;
|
||||
}
|
||||
}
|
||||
} else if (rule->r_type == MONTH_NTH_DAY_OF_WEEK) {
|
||||
len++;
|
||||
len++;
|
||||
if (rule->r_mon > 9) {
|
||||
len++;
|
||||
}
|
||||
len += 4; /* dots and week/day */
|
||||
if (rule->r_time != 7200) {
|
||||
len++;
|
||||
len += lensecs (rule->r_time);
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int is_extended_name (const char *name)
|
||||
{
|
||||
int i;
|
||||
for (i=0; name [i]; i++) {
|
||||
if (isdigit (name [i]) || name [i] == '+' || name [i] == '-') {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get length of timezone string resulting from serializing tzo.
|
||||
*/
|
||||
static size_t len_tz_r (const struct tzoffset_info *tzo)
|
||||
{
|
||||
size_t len = 0;
|
||||
if (tzo->stdname == NULL) {
|
||||
return 0;
|
||||
}
|
||||
len = strlen (tzo->stdname);
|
||||
if (is_extended_name (tzo->stdname)) {
|
||||
len += 2;
|
||||
}
|
||||
len += lensecs (tzo->stdoffset);
|
||||
if (tzo->dstname) {
|
||||
len += strlen (tzo->dstname);
|
||||
if (is_extended_name (tzo->dstname)) {
|
||||
len += 2;
|
||||
}
|
||||
if (tzo->dstoffset - tzo->stdoffset != -3600) {
|
||||
len += lensecs (tzo->dstoffset);
|
||||
}
|
||||
len += 2; /* commas */
|
||||
len += lenrule (&tzo->start);
|
||||
len += lenrule (&tzo->end);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t len_tz (void)
|
||||
{
|
||||
return len_tz_r (&localtime_tzoffset);
|
||||
}
|
||||
|
||||
void appendsecs (char *buf, long minutes)
|
||||
{
|
||||
char *p = buf + strlen (buf);
|
||||
long min = abs (minutes);
|
||||
if (minutes < 0) {
|
||||
*p++ = '-';
|
||||
}
|
||||
if (min % 3600 == 0) {
|
||||
sprintf (p, "%ld", min / 3600);
|
||||
} else if (min % 60 == 0) {
|
||||
sprintf (p, "%ld:%ld", min / 3600, (min / 60) % 60);
|
||||
} else {
|
||||
sprintf (p, "%ld:%ld:%ld", min / 3600, (min / 60) % 60, min % 60);
|
||||
}
|
||||
}
|
||||
|
||||
void appendrule (char *buf, const struct tzrule *rule)
|
||||
{
|
||||
char *p = buf + strlen (buf);
|
||||
if (rule->r_type == JULIAN_DAY) {
|
||||
sprintf (p, "J%d", rule->r_day);
|
||||
} else if (rule->r_type == DAY_OF_YEAR) {
|
||||
sprintf (p, "%d", rule->r_day);
|
||||
} else if (rule->r_type == MONTH_NTH_DAY_OF_WEEK) {
|
||||
sprintf (p, "M%d.%d.%d", rule->r_mon, rule->r_week, rule->r_day);
|
||||
p = buf + strlen (buf);
|
||||
if (rule->r_time != 7200) {
|
||||
*p++ = '/';
|
||||
*p = '\0';
|
||||
appendsecs (p, rule->r_time);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char *get_tz (char *buf, size_t buflen)
|
||||
{
|
||||
const struct tzoffset_info *tzo = &localtime_tzoffset;
|
||||
|
||||
if (tzo->stdname == NULL || len_tz_r (tzo) > buflen) {
|
||||
return NULL;
|
||||
}
|
||||
if (is_extended_name (tzo->stdname)) {
|
||||
sprintf (buf, "<%s>", tzo->stdname);
|
||||
} else {
|
||||
strcpy (buf, tzo->stdname);
|
||||
}
|
||||
appendsecs (buf, tzo->stdoffset);
|
||||
if (tzo->dstname != NULL) {
|
||||
if (is_extended_name (tzo->dstname)) {
|
||||
sprintf (buf + strlen (buf), "<%s>", tzo->dstname);
|
||||
} else {
|
||||
strcat (buf, tzo->dstname);
|
||||
}
|
||||
if (tzo->dstoffset - tzo->stdoffset != -3600) {
|
||||
appendsecs (buf, tzo->dstoffset);
|
||||
}
|
||||
strcat (buf, ",");
|
||||
appendrule (buf, &tzo->start);
|
||||
strcat (buf, ",");
|
||||
appendrule (buf, &tzo->end);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
||||
/** @} */
|
||||
|
|
|
@ -19,6 +19,18 @@
|
|||
#ifndef time_h
|
||||
#define time_h
|
||||
|
||||
#ifdef LOCAL_COMPILE
|
||||
#define tm l_tm
|
||||
#define timeval l_timeval
|
||||
#define time_t l_time_t
|
||||
#define gmtime l_gmtime
|
||||
#define localtime l_localtime
|
||||
#define gettimeofday l_gettimeofday
|
||||
#define clock_seconds() 1
|
||||
#define clock_time() 1
|
||||
#endif
|
||||
|
||||
#define DEFAULT_TIMEZONE "CET-1CEST,M3.5.0,M10.5.0/3"
|
||||
|
||||
typedef signed long long time_t;
|
||||
typedef signed long suseconds_t;
|
||||
|
@ -27,16 +39,19 @@ typedef signed long suseconds_t;
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* tm_gmtoff and tm_zone are BSD additions */
|
||||
struct tm {
|
||||
uint32_t tm_year; /* year */
|
||||
uint16_t tm_yday; /* day in the year */
|
||||
uint8_t tm_sec; /* seconds */
|
||||
uint8_t tm_min; /* minutes */
|
||||
uint8_t tm_hour; /* hours */
|
||||
uint8_t tm_mday; /* day of the month */
|
||||
uint8_t tm_mon; /* month */
|
||||
uint8_t tm_wday; /* day of the week */
|
||||
uint8_t tm_isdst; /* daylight saving time */
|
||||
uint32_t tm_year; /* year */
|
||||
uint16_t tm_yday; /* day in the year */
|
||||
uint8_t tm_sec; /* seconds */
|
||||
uint8_t tm_min; /* minutes */
|
||||
uint8_t tm_hour; /* hours */
|
||||
uint8_t tm_mday; /* day of the month */
|
||||
uint8_t tm_mon; /* month */
|
||||
uint8_t tm_wday; /* day of the week */
|
||||
uint8_t tm_isdst; /* daylight saving time */
|
||||
int32_t tm_gmtoff; /* Seconds east of UTC */
|
||||
const char *tm_zone; /* Timezone abbreviation */
|
||||
};
|
||||
|
||||
struct timeval {
|
||||
|
@ -57,6 +72,22 @@ struct tm *localtime_r (const time_t *timep, struct tm *result);
|
|||
int gettimeofday (struct timeval *tv, struct timezone *tz);
|
||||
int settimeofday (const struct timeval *tv, const struct timezone *tz);
|
||||
|
||||
/*
|
||||
* Maximum length of all timezone names, this is much longer in UNIX
|
||||
* implementations but we have limited space here. Note that the length
|
||||
* includes a trailing \0 byte for each timezone name.
|
||||
*/
|
||||
#ifndef TZ_MAX_CHARS
|
||||
#define TZ_MAX_CHARS 16
|
||||
#endif
|
||||
|
||||
/* Maximum length of buffer to reserve for timezone string */
|
||||
#define MAXTZLEN (TZ_MAX_CHARS+9+2*(2+6+1+8)+1)
|
||||
|
||||
int set_tz (const char *tzstring);
|
||||
const char *get_tz (char *buffer, size_t buflen);
|
||||
size_t len_tz (void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "rest-engine.h"
|
||||
|
||||
extern resource_t res_timestamp;
|
||||
extern resource_t res_timezone;
|
||||
extern resource_t res_localtime;
|
||||
extern resource_t res_utc;
|
||||
|
||||
|
|
56
apps/time/tzparse.h
Normal file
56
apps/time/tzparse.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Timezone parsing
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Definitions for timezone parsing
|
||||
*
|
||||
* \author
|
||||
* Ralf Schlatterbeck <rsc@tux.runtux.com>
|
||||
*/
|
||||
|
||||
#ifndef tzparse_h
|
||||
#define tzparse_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Rule for DST switching
|
||||
*/
|
||||
struct tzrule {
|
||||
int r_type; /* type of rule--see below */
|
||||
int r_day; /* day number of rule */
|
||||
int r_week; /* week number of rule */
|
||||
int r_mon; /* month number of rule */
|
||||
long r_time; /* transition time of rule */
|
||||
};
|
||||
|
||||
#define JULIAN_DAY 0 /* Jn - Julian day */
|
||||
#define DAY_OF_YEAR 1 /* n - day of year */
|
||||
#define MONTH_NTH_DAY_OF_WEEK 2 /* Mm.n.d - month, week, day of week */
|
||||
|
||||
/*
|
||||
* Info about timezone offset handling.
|
||||
* We get at least a dstname and stdoffset, if no daylight saving is in
|
||||
* effect, dstname is NULL and no rule is filled in.
|
||||
*/
|
||||
struct tzoffset_info {
|
||||
const char *stdname;
|
||||
const char *dstname;
|
||||
long stdoffset;
|
||||
long dstoffset;
|
||||
struct tzrule start;
|
||||
struct tzrule end;
|
||||
char namebuf [TZ_MAX_CHARS];
|
||||
};
|
||||
|
||||
int tzparse (const char *name, struct tzoffset_info *tzo);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // tzparse_h
|
|
@ -131,6 +131,7 @@ PROCESS_THREAD(rest_server_example, ev, data)
|
|||
#endif
|
||||
|
||||
rest_activate_resource(&res_timestamp, "clock/timestamp");
|
||||
rest_activate_resource(&res_timezone, "clock/timezone");
|
||||
rest_activate_resource(&res_localtime, "clock/localtime");
|
||||
rest_activate_resource(&res_utc, "clock/utc");
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
time.h missing
|
Loading…
Reference in a new issue