403 lines
13 KiB
C
403 lines
13 KiB
C
/*-
|
|
* Copyright (c) 2020 Carsten Sonne Larsen <cs@innolan.net>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
#include "locale.h"
|
|
#include "global.h"
|
|
#include "setting.h"
|
|
#include "conv.h"
|
|
#include "text.h"
|
|
#include "ptz.h"
|
|
#include "mem.h"
|
|
#include "tz.h"
|
|
|
|
#include <proto/dos.h>
|
|
#include <proto/timer.h>
|
|
#include <proto/locale.h>
|
|
#include <proto/utility.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#define SECONDSPERDAY 86400
|
|
|
|
static const char *WeekdayShort[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
|
|
static const char *WeekdayLong[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
|
|
static const char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
|
|
|
static void GetTimezoneCommon(char *text, struct PosixLocalTimezone *tz, long opt)
|
|
{
|
|
if (tz->abbreviation[0] == 'G' && tz->abbreviation[1] == 'M' && tz->abbreviation[2] == 'T')
|
|
{
|
|
if (tz->offset.minutes == 0)
|
|
SNPrintf(text, TIMEZONE_TEXT_LEN, "GMT%s%d", TimezoneSignChar(tz), tz->offset.hours);
|
|
else
|
|
SNPrintf(text, TIMEZONE_TEXT_LEN, "GMT%s%d:%02d",
|
|
TimezoneSignChar(tz), tz->offset.hours, tz->offset.minutes);
|
|
}
|
|
else if (opt == TZD_ZONE_IN_PARENS)
|
|
{
|
|
if (tz->offset.minutes == 0)
|
|
SNPrintf(text, TIMEZONE_TEXT_LEN, "%s (GMT%s%d)",
|
|
tz->abbreviation, TimezoneSignChar(tz), tz->offset.hours);
|
|
else
|
|
SNPrintf(text, TIMEZONE_TEXT_LEN, "%s (GMT%s%d:%02d)",
|
|
tz->abbreviation, TimezoneSignChar(tz), tz->offset.hours, tz->offset.minutes);
|
|
}
|
|
else if (opt == TZD_OFFSET_IN_PARENS)
|
|
{
|
|
if (tz->offset.minutes == 0)
|
|
SNPrintf(text, TIMEZONE_TEXT_LEN, "GMT%s%d (%s)",
|
|
TimezoneSignChar(tz), tz->offset.hours, tz->abbreviation);
|
|
else
|
|
SNPrintf(text, TIMEZONE_TEXT_LEN, "GMT%s%d:%02d (%s)",
|
|
TimezoneSignChar(tz), tz->offset.hours, tz->offset.minutes, tz->abbreviation);
|
|
}
|
|
}
|
|
|
|
void GetTimezoneText(char *text, long opt)
|
|
{
|
|
long offset = -GetUtcOffsetValue();
|
|
char *tzSign = (offset <= 0 ? "+" : "-");
|
|
|
|
switch (opt)
|
|
{
|
|
case TZD_SECONDS_AHEAD:
|
|
SNPrintf(text, TIMEZONE_TEXT_LEN, "%ld", offset);
|
|
break;
|
|
case TZD_MINUTES_AHEAD:
|
|
SNPrintf(text, TIMEZONE_TEXT_LEN, "%ld", offset / 60);
|
|
break;
|
|
case TZD_TZ_LONG:
|
|
if (Timezone->current.offset.minutes == 0)
|
|
SNPrintf(text, TIMEZONE_TEXT_LEN, "UTC%s%d", tzSign, Timezone->current.offset.hours);
|
|
else
|
|
SNPrintf(text, TIMEZONE_TEXT_LEN, "UTC%s%d:%02d",
|
|
tzSign, Timezone->current.offset.hours, Timezone->current.offset.minutes);
|
|
break;
|
|
case TZD_TZ_SHORT:
|
|
{
|
|
if (Timezone->current.offset.minutes == 0)
|
|
SNPrintf(text, TIMEZONE_TEXT_LEN, "%s%d", tzSign, Timezone->current.offset.hours);
|
|
else
|
|
SNPrintf(text, TIMEZONE_TEXT_LEN, "%s%d:%02d",
|
|
tzSign, Timezone->current.offset.hours, Timezone->current.offset.minutes);
|
|
}
|
|
break;
|
|
case TZD_ISO8601_NO_COLON:
|
|
SNPrintf(text, TIMEZONE_TEXT_LEN, "%s%02d%02d", TimezoneSignChar(&Timezone->current),
|
|
Timezone->current.offset.hours, Timezone->current.offset.minutes);
|
|
break;
|
|
case TZD_ISO8601_COLON:
|
|
SNPrintf(text, TIMEZONE_TEXT_LEN, "%s%02d:%02d", TimezoneSignChar(&Timezone->current),
|
|
Timezone->current.offset.hours, Timezone->current.offset.minutes);
|
|
break;
|
|
case TZD_ISO8601_SHORT:
|
|
if (offset % 60 == 0)
|
|
SNPrintf(text, TIMEZONE_TEXT_LEN, "%s%d",
|
|
TimezoneSignChar(&Timezone->current), Timezone->current.offset.hours);
|
|
else
|
|
SNPrintf(text, TIMEZONE_TEXT_LEN, "%s%d:%02d", TimezoneSignChar(&Timezone->current),
|
|
Timezone->current.offset.hours, Timezone->current.offset.minutes);
|
|
break;
|
|
case TZD_COMMON:
|
|
SNPrintf(text, TIMEZONE_TEXT_LEN, "GMT%s%02d:%02d", TimezoneSignChar(&Timezone->current),
|
|
Timezone->current.offset.hours, Timezone->current.offset.minutes);
|
|
break;
|
|
case TZD_ZONE_IN_PARENS:
|
|
GetTimezoneCommon(text, &Timezone->current, TZD_ZONE_IN_PARENS);
|
|
break;
|
|
case TZD_OFFSET_IN_PARENS:
|
|
GetTimezoneCommon(text, &Timezone->current, TZD_OFFSET_IN_PARENS);
|
|
break;
|
|
case TZD_STD:
|
|
GetTimezoneCommon(text, &Timezone->std, TZD_OFFSET_IN_PARENS);
|
|
break;
|
|
case TZD_DST:
|
|
GetTimezoneCommon(text, &Timezone->dst, TZD_OFFSET_IN_PARENS);
|
|
break;
|
|
default:
|
|
text = '\0';
|
|
break;
|
|
}
|
|
}
|
|
|
|
char *GetTimeText(ULONG time)
|
|
{
|
|
char *out = AllocStringSafe(128);
|
|
struct ClockData cd;
|
|
struct timeval tv;
|
|
ULONG t = time;
|
|
|
|
if (t == LOCAL_TIME_NOW)
|
|
{
|
|
GetSysTime(&tv);
|
|
t = (ULONG)tv.tv_secs;
|
|
}
|
|
|
|
Amiga2Date(t, &cd);
|
|
int weekday = (cd.wday == 0 ? 7 : cd.wday) - 1;
|
|
SNPrintf(out, 128, "%s %02ld %s %02ld:%02ld:%02ld %s %ld",
|
|
WeekdayLong[weekday],
|
|
(long)cd.mday,
|
|
Month[cd.month - 1],
|
|
(long)cd.hour,
|
|
(long)cd.min,
|
|
(long)cd.sec,
|
|
Timezone->current.abbreviation,
|
|
(long)cd.year);
|
|
|
|
return out;
|
|
}
|
|
|
|
static void Amiga2DateStamp(time_t *time, struct DateStamp *date)
|
|
{
|
|
long t, d, m, q;
|
|
|
|
t = *time;
|
|
d = t / SECONDSPERDAY;
|
|
m = (t - SECONDSPERDAY * d) / 60;
|
|
q = (t - SECONDSPERDAY * d - m * 60) * 50;
|
|
|
|
date->ds_Days = d;
|
|
date->ds_Minute = m;
|
|
date->ds_Tick = q;
|
|
}
|
|
|
|
static void DateTimeString(struct DateStamp *ds, char *time, char *date, char *day)
|
|
{
|
|
struct DateTime dt;
|
|
|
|
assert(ds != NULL);
|
|
assert(time != NULL);
|
|
assert(date != NULL);
|
|
assert(day != NULL);
|
|
|
|
dt.dat_Stamp = *ds;
|
|
dt.dat_Format = FORMAT_DOS;
|
|
dt.dat_Flags = 0;
|
|
dt.dat_StrDay = (void *)day;
|
|
dt.dat_StrDate = (void *)date;
|
|
dt.dat_StrTime = (void *)time;
|
|
|
|
DateToStr(&dt);
|
|
}
|
|
|
|
void Utc2DOS(struct timeval *utv, char *buffer, bool showUtc)
|
|
{
|
|
char timeString[20];
|
|
char dateString[20];
|
|
char dayString[20];
|
|
|
|
struct DateStamp ds;
|
|
struct timeval tv;
|
|
|
|
if (showUtc)
|
|
Unix2Amiga(utv, &tv);
|
|
else
|
|
UnixUtc2AmigaLocal(utv, &tv);
|
|
|
|
time_t time = tv.tv_secs;
|
|
Amiga2DateStamp(&time, &ds);
|
|
DateTimeString(&ds, timeString, dateString, dayString);
|
|
SNPrintf(buffer, 64, "%s %s %s", dayString, dateString, timeString);
|
|
}
|
|
|
|
void Utc2ARexxDate(struct timeval *utv, char *buffer, bool showUtc)
|
|
{
|
|
struct timeval tv;
|
|
struct ClockData cd;
|
|
|
|
if (showUtc)
|
|
Unix2Amiga(utv, &tv);
|
|
else
|
|
UnixUtc2AmigaLocal(utv, &tv);
|
|
|
|
Amiga2Date(tv.tv_secs, &cd);
|
|
SNPrintf(buffer, 10, "%04ld%02ld%02ld", (long)cd.year, (long)cd.month, (long)cd.mday);
|
|
}
|
|
|
|
void Utc2ARexxTime(struct timeval *utv, char *buffer, bool showUtc)
|
|
{
|
|
struct timeval tv;
|
|
|
|
if (showUtc)
|
|
Unix2Amiga(utv, &tv);
|
|
else
|
|
UnixUtc2AmigaLocal(utv, &tv);
|
|
|
|
long time = tv.tv_secs % (24 * 60 * 60);
|
|
LongToStr(time, buffer);
|
|
}
|
|
|
|
void Utc2ASCII(struct timeval *utv, char *buffer, bool showUtc)
|
|
{
|
|
struct timeval tv;
|
|
struct ClockData cd;
|
|
|
|
if (showUtc)
|
|
Unix2Amiga(utv, &tv);
|
|
else
|
|
UnixUtc2AmigaLocal(utv, &tv);
|
|
|
|
Amiga2Date(tv.tv_secs, &cd);
|
|
SNPrintf(buffer, DATETIME_TEXT_LEN, ASCIITEMPLATE,
|
|
WeekdayShort[cd.wday - 1], Month[cd.month - 1], (long)cd.mday,
|
|
(long)cd.hour, (long)cd.min, (long)cd.sec, (long)cd.year);
|
|
}
|
|
|
|
void Utc2RFC850(struct timeval *utv, char *buffer)
|
|
{
|
|
struct timeval tv;
|
|
struct ClockData cd;
|
|
|
|
Unix2Amiga(utv, &tv);
|
|
Amiga2Date(tv.tv_secs, &cd);
|
|
SNPrintf(buffer, DATETIME_TEXT_LEN, RFC850TEMPLATE,
|
|
WeekdayLong[cd.wday - 1], (long)cd.mday,
|
|
Month[cd.month - 1], (long)cd.year % 100,
|
|
(long)cd.hour, (long)cd.min, (long)cd.sec);
|
|
}
|
|
|
|
void Utc2RFC1123(struct timeval *utv, char *buffer)
|
|
{
|
|
struct timeval tv;
|
|
struct ClockData cd;
|
|
|
|
Unix2Amiga(utv, &tv);
|
|
Amiga2Date(tv.tv_secs, &cd);
|
|
SNPrintf(buffer, DATETIME_TEXT_LEN, RFC1123TEMPLATE,
|
|
WeekdayShort[cd.wday - 1], (long)cd.mday,
|
|
Month[cd.month - 1], (long)cd.year,
|
|
(long)cd.hour, (long)cd.min, (long)cd.sec);
|
|
}
|
|
|
|
void Utc2RFC2822(struct timeval *utv, char *buffer, bool showUtc)
|
|
{
|
|
struct timeval tv;
|
|
struct ClockData cd;
|
|
|
|
if (showUtc)
|
|
{
|
|
Unix2Amiga(utv, &tv);
|
|
Amiga2Date(tv.tv_secs, &cd);
|
|
SNPrintf(buffer, DATETIME_TEXT_LEN, RFC2822TEMPLATE,
|
|
(long)cd.mday, Month[cd.month - 1], (long)cd.year,
|
|
(long)cd.hour, (long)cd.min, (long)cd.sec,
|
|
"+", 0L, 0L);
|
|
}
|
|
else
|
|
{
|
|
UnixUtc2AmigaLocal(utv, &tv);
|
|
Amiga2Date(tv.tv_secs, &cd);
|
|
SNPrintf(buffer, DATETIME_TEXT_LEN, RFC2822TEMPLATE,
|
|
(long)cd.mday, Month[cd.month - 1], (long)cd.year,
|
|
(long)cd.hour, (long)cd.min, (long)cd.sec,
|
|
TimezoneSignChar(&Timezone->current),
|
|
(long)Timezone->current.offset.hours,
|
|
(long)Timezone->current.offset.minutes);
|
|
}
|
|
}
|
|
|
|
void Utc2RFC3339(struct timeval *utv, char *buffer, bool showUtc)
|
|
{
|
|
struct timeval tv;
|
|
struct ClockData cd;
|
|
|
|
if (showUtc)
|
|
{
|
|
Unix2Amiga(utv, &tv);
|
|
Amiga2Date(tv.tv_secs, &cd);
|
|
SNPrintf(buffer, DATETIME_TEXT_LEN, RFC3339TEMPLATEUTC,
|
|
(long)cd.year, (long)cd.month, (long)cd.mday,
|
|
(long)cd.hour, (long)cd.min, (long)cd.sec,
|
|
(long)(tv.tv_micro / 10000));
|
|
}
|
|
else
|
|
{
|
|
UnixUtc2AmigaLocal(utv, &tv);
|
|
Amiga2Date(tv.tv_secs, &cd);
|
|
SNPrintf(buffer, DATETIME_TEXT_LEN, RFC3339TEMPLATELOC,
|
|
(long)cd.year, (long)cd.month, (long)cd.mday,
|
|
(long)cd.hour, (long)cd.min, (long)cd.sec,
|
|
(long)(tv.tv_micro / 10000),
|
|
TimezoneSignChar(&Timezone->current),
|
|
(long)Timezone->current.offset.hours,
|
|
(long)Timezone->current.offset.minutes);
|
|
}
|
|
}
|
|
|
|
void Utc2ISO8601(struct timeval *utv, char *buffer, bool showUtc)
|
|
{
|
|
struct timeval tv;
|
|
struct ClockData cd;
|
|
|
|
if (showUtc)
|
|
{
|
|
Unix2Amiga(utv, &tv);
|
|
Amiga2Date(tv.tv_secs, &cd);
|
|
SNPrintf(buffer, DATETIME_TEXT_LEN, ISO8601TEMPLATEUTC,
|
|
(long)cd.year, (long)cd.month, (long)cd.mday,
|
|
(long)cd.hour, (long)cd.min, (long)cd.sec);
|
|
}
|
|
else
|
|
{
|
|
UnixUtc2AmigaLocal(utv, &tv);
|
|
Amiga2Date(tv.tv_secs, &cd);
|
|
SNPrintf(buffer, DATETIME_TEXT_LEN, ISO8601TEMPLATELOC,
|
|
(long)cd.year, (long)cd.month, (long)cd.mday,
|
|
(long)cd.hour, (long)cd.min, (long)cd.sec,
|
|
TimezoneSignChar(&Timezone->current),
|
|
(long)Timezone->current.offset.hours,
|
|
(long)Timezone->current.offset.minutes);
|
|
}
|
|
}
|
|
|
|
void LogTransitionMap(void)
|
|
{
|
|
struct PosixTransitionTime *transition;
|
|
struct DateStamp ds;
|
|
time_t next;
|
|
char timeString[20];
|
|
char dateString[20];
|
|
char dayString[20];
|
|
int i;
|
|
|
|
for (i = 0; i < 138; i++)
|
|
{
|
|
transition = &Timezone->transitions[i];
|
|
next = transition->time;
|
|
Amiga2DateStamp(&next, &ds);
|
|
DateTimeString(&ds, timeString, dateString, dayString);
|
|
LogNoticeMsg("Timezone",
|
|
"Changing (%010ld) to %s time on %s %s at %s %s",
|
|
(long)next,
|
|
transition->isdst ? tzDstAbbr : tzStdAbbr,
|
|
dayString, dateString, timeString,
|
|
transition->isdst
|
|
? Timezone->std.abbreviation
|
|
: Timezone->dst.abbreviation);
|
|
}
|
|
}
|