m3core/src/time/POSIX/DatePosixC.c
#include "m3core.h"
#if __GNUC__ >= 4
#pragma GCC visibility push(hidden)
#endif
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __CYGWIN__
#define M3_TIMEZONE _timezone
#define M3_DAYLIGHT _daylight
#else
#define M3_TIMEZONE timezone
#define M3_DAYLIGHT daylight
#endif
/* OSF/1 is BSD at least if you define _OSF_SOURCE, else POSIX. */
#if defined(__CYGWIN__) || defined(__hpux) || defined(__sun) || defined(__INTERIX)
#define DATE_POSIX
#else
#define DATE_BSD
#endif
#define Local 0
#define UTC 1
#ifdef __osf__
/* This works whether or not _OSF_SOURCE is defined. The headers say:
ifdef _OSF_SOURCE
long tm_gmtoff;
char *tm_zone;
define __tm_gmtoff tm_gmtoff
define __tm_zone tm_zone
else
long __tm_gmtoff;
char *__tm_zone;
#endif
*/
#define m3_tm_gmtoff __tm_gmtoff
#define m3_tm_zone __tm_zone
#else
#define m3_tm_gmtoff tm_gmtoff
#define m3_tm_zone tm_zone
#endif
#ifdef _TIME64_T
static time64_t TimePosix__ToSeconds(LONGREAL/*Time.T*/ t)
#else
static time_t TimePosix__ToSeconds(LONGREAL/*Time.T*/ t)
#endif
{
double n = { 0 };
modf(t, &n);
return n;
}
void
__cdecl
DatePosix__FromTime(double t, const ptrdiff_t* pzone, Date_t* date, TEXT unknown, TEXT gmt)
{
struct tm* tm = { 0 };
#ifdef _TIME64_T
time64_t sec = TimePosix__ToSeconds(t);
#else
time_t sec = TimePosix__ToSeconds(t);
#endif
ptrdiff_t zone = (pzone ? *pzone : Local);
ZeroMemory(date, sizeof(*date));
assert(zone == Local || zone == UTC);
#ifdef _TIME64_T
tm = ((zone == Local) ? localtime64(&sec) : gmtime64(&sec));
#else
tm = ((zone == Local) ? localtime(&sec) : gmtime(&sec));
#endif
assert(tm != NULL);
date->year = tm->tm_year + 1900;
date->month = tm->tm_mon;
date->day = tm->tm_mday;
date->hour = tm->tm_hour;
date->minute = tm->tm_min;
date->second = tm->tm_sec;
date->weekDay = tm->tm_wday;
#ifdef DATE_BSD
/* The "tm->tm_gmtoff" field is seconds *east* of GMT, whereas
* the "date.offset" field is seconds *west* of GMT, so a
* negation is necessary.
*/
date->offset = -tm->m3_tm_gmtoff;
date->zone = M3toC__CopyStoT(tm->m3_tm_zone);
#else
if (zone == Local)
{
if (tm->tm_isdst == 0)
{
date->offset = M3_TIMEZONE;
date->zone = M3toC__CopyStoT(tzname[0]);
}
else if (tm->tm_isdst > 0 && M3_DAYLIGHT)
{
#ifdef __sun
date->offset = altzone;
#else
date->offset = M3_TIMEZONE - 3600;
#endif
date->zone = M3toC__CopyStoT(tzname[1]);
}
else
{
date->offset = 0;
date->zone = unknown;
}
}
else
{
date->offset = 0;
date->zone = gmt;
}
#endif
}
double
__cdecl
DatePosix__ToTime(const Date_t* date)
{
struct tm tm;
double t = { 0 };
#ifdef DATE_BSD
const unsigned SecsPerHour = 60 * 60;
#ifdef _TIME64_T
time64_t now = { 0 };
#else
time_t now = { 0 };
#endif
struct tm* local_now = { 0 };
#endif
/* prepare call to mktime(3) */
ZERO_MEMORY(tm);
tm.tm_sec = date->second;
tm.tm_min = date->minute;
tm.tm_hour = date->hour;
tm.tm_mday = date->day;
tm.tm_mon = date->month;
tm.tm_year = date->year - 1900;
/* tm.tm_wday ignored */
tm.tm_isdst = 0; /* tell mktime that DST is not in effect */
/* tm_zone, tm_gmtoff ignored */
#ifdef _TIME64_T
t = mktime64(&tm);
#else
t = mktime(&tm);
#endif
#ifdef DATE_BSD
if (t == -1)
goto Exit;
/* adjust result to reflect "date->offset" */
#ifdef _TIME64_T
time64(&now);
local_now = localtime64(&now);
#else
time(&now);
local_now = localtime(&now);
#endif
assert(local_now != NULL);
if (local_now->tm_isdst > 0)
/* decrement the local time zone by one hour if DST is in effect */
local_now->m3_tm_gmtoff -= SecsPerHour;
/* As above, we must negate "date->offset" to account for the
opposite sense of that field compared to Unix. */
t -= ((-date->offset) - local_now->m3_tm_gmtoff);
Exit:
#endif
return t;
}
void
__cdecl
DatePosix__TypeCheck(const Date_t* d, WORD_T sizeof_DateT)
{
assert(sizeof(Date_t) == sizeof_DateT);
assert(d->year == 1);
assert(d->month == 2);
assert(d->day == 3);
assert(d->hour == 4);
assert(d->minute == 5);
assert(d->second == 6);
assert(d->offset == 7);
assert(d->zone == (TEXT)8);
assert(d->weekDay == 9);
}
#ifdef __cplusplus
} /* extern C */
#endif