AmiTimeKeeper/locale.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);
}
}