/*- * Copyright (c) 2020 Carsten Sonne Larsen * 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 #include #include #include #include #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); } }