mirror of
https://github.com/pkoning2/decstuff.git
synced 2026-01-23 10:58:36 +00:00
277 lines
7.6 KiB
C
277 lines
7.6 KiB
C
/* Routines for using TZ files */
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "tzutil.h"
|
|
|
|
/* Define HERTZ at compile time to use a value other than the default 60 Hz */
|
|
#ifndef HERTZ
|
|
#define HERTZ 60
|
|
#endif
|
|
|
|
/* This may also be patched (instead of overriding it at compile time) */
|
|
const uint16_t hertz = HERTZ;
|
|
|
|
#define TZFILE "NTP$:TZ.DAT"
|
|
|
|
int32_t curt = 0, nextt = 0;
|
|
int32_t curoff = (int32_t) 13 * 60 * 60, nextoff = 0;
|
|
char curabbr[ABBRMAX + 1], nextabbr[ABBRMAX + 1];
|
|
char dtstr[DATELEN + 1 + RTIMELENX + 1 + TZLEN + 1];
|
|
|
|
static FILE *tz = NULL;
|
|
static struct fhdr hdr;
|
|
static struct ttinfo info;
|
|
|
|
/* Find the current timezone info for the UTC time given as argument. On exit,
|
|
current and next info are in global variables:
|
|
curt = start time of current rule
|
|
curoff = current offset in seconds
|
|
curabbr = current zone name
|
|
nextt = start time of next rule
|
|
nextoff = next offset
|
|
nextabbr= next name
|
|
If there is no next (for example, for the UTC zone), nextt is set to 2^31 - 1.
|
|
The return value is 1 (true) if new zone data was loaded, 0 if there was no
|
|
change from the previous call.
|
|
*/
|
|
int gettzinfo (int32_t now)
|
|
{
|
|
int i, timecnt, typecnt, charcnt;
|
|
int32_t t;
|
|
unsigned char curidx, nextidx;
|
|
|
|
/* Just return if we already have the info */
|
|
if (now >= curt && now < nextt) {
|
|
return 0;
|
|
}
|
|
|
|
if (tz == NULL) {
|
|
tz = fopen (TZFILE, "rb");
|
|
if (tz == NULL) {
|
|
perror ("open");
|
|
exit (4);
|
|
}
|
|
/* Read the zone file header */
|
|
fread (&hdr, sizeof (hdr), 1, tz);
|
|
/* Todo: check the magic value? */
|
|
hdr.tzh_timecnt = ntohl (hdr.tzh_timecnt);
|
|
hdr.tzh_typecnt = ntohl (hdr.tzh_typecnt);
|
|
hdr.tzh_charcnt = ntohl (hdr.tzh_charcnt);
|
|
} else {
|
|
/* Position to start of offsets vector */
|
|
fseek (tz, sizeof (struct fhdr), SEEK_SET);
|
|
}
|
|
curidx = 0;
|
|
nextidx = -1;
|
|
curt = 0;
|
|
nextt = 2147483647; /* max int32_t */
|
|
for (i = 0; i < hdr.tzh_timecnt; i++) {
|
|
fread (&t, sizeof (t), 1, tz);
|
|
t = ntohl (t);
|
|
if (now <= t) {
|
|
nextt = t;
|
|
nextidx = i;
|
|
break;
|
|
}
|
|
curt = t;
|
|
curidx = i;
|
|
}
|
|
fseek (tz, sizeof (struct fhdr) + 4 * hdr.tzh_timecnt + curidx, SEEK_SET);
|
|
fread (&curidx, 1, 1, tz);
|
|
if (nextidx > 0) {
|
|
fread (&nextidx, 1, 1, tz);
|
|
}
|
|
fseek (tz, sizeof (struct fhdr) + 5 * hdr.tzh_timecnt +
|
|
curidx * sizeof (struct ttinfo), SEEK_SET);
|
|
fread (&info, sizeof (struct ttinfo), 1, tz);
|
|
curoff = ntohl (info.tt_gmtoff);
|
|
fseek (tz, sizeof (struct fhdr) + 5 * hdr.tzh_timecnt +
|
|
hdr.tzh_typecnt * sizeof (struct ttinfo) + info.tt_abbrind, SEEK_SET);
|
|
fread (curabbr, sizeof (curabbr), 1, tz);
|
|
if (i < timecnt) {
|
|
/* There is a next */
|
|
fseek (tz, sizeof (struct fhdr) + 5 * hdr.tzh_timecnt +
|
|
nextidx * sizeof (struct ttinfo), SEEK_SET);
|
|
fread (&info, sizeof (struct ttinfo), 1, tz);
|
|
nextoff = ntohl (info.tt_gmtoff);
|
|
fseek (tz, sizeof (struct fhdr) + 5 * hdr.tzh_timecnt +
|
|
hdr.tzh_typecnt * sizeof (struct ttinfo) + info.tt_abbrind, SEEK_SET);
|
|
fread (nextabbr, sizeof (nextabbr), 1, tz);
|
|
} else {
|
|
/* No next, supply dummy values */
|
|
nextoff = -1;
|
|
nextabbr[0] = '\0';
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Same as gettzinfo but the argument is a local time. */
|
|
int getlocaltzinfo (int32_t lnow)
|
|
{
|
|
int32_t now;
|
|
int ret;
|
|
|
|
now = lnow - curoff;
|
|
ret = gettzinfo (now);
|
|
/* Check if we got the right rule. If lnow is very close to a
|
|
transition we may be off by one. */
|
|
now = lnow - curoff;
|
|
if (now < curt || now >= nextt) {
|
|
ret = gettzinfo (now);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* Convert 32 bit Unix style time, except that it is local, into RSTS date/time */
|
|
void mkrststime (int32_t time, struct rstsdt *dt)
|
|
{
|
|
int d, ylen;
|
|
unsigned int y;
|
|
int32_t t;
|
|
|
|
time = utol (time); /* UTC to local seconds */
|
|
d = time / 86400;
|
|
t = time % 86400;
|
|
dt->minutes = 1440 - t / 60;
|
|
dt->seconds = 60 - t % 60;
|
|
dt->ticks = hertz; /* Default to exact second */
|
|
/* the easiest way to get year and day-in-year is with a loop */
|
|
y = 0;
|
|
for (;;) {
|
|
ylen = 365 + ((y & 3) == 2);
|
|
if (d < ylen) {
|
|
break;
|
|
}
|
|
d -= ylen;
|
|
y++;
|
|
}
|
|
dt->date = (y * 1000) + d + 1;
|
|
}
|
|
|
|
/* Convert RSTS date/time to Unix time except that it is local, not UTC */
|
|
int32_t lctime (const struct rstsdt *dt)
|
|
{
|
|
unsigned short date;
|
|
int y, d;
|
|
|
|
date = dt->date - 1;
|
|
y = date / 1000;
|
|
d = date % 1000;
|
|
d += y * 365 + ((y + 1) >> 2);
|
|
return (uint32_t) d * 86400 + (uint32_t) (1440 - dt->minutes) * 60 + (60 - dt->seconds);
|
|
}
|
|
|
|
/* Modified from FLX 2.6 */
|
|
static const char months[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
|
static int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
|
|
|
/* Convert RSTS date into US style date string */
|
|
void cvtdate (const struct rstsdt *dt, char *buf)
|
|
{
|
|
uint16_t date = dt->date;
|
|
long day, yr;
|
|
int mon;
|
|
|
|
if (date == 0) { /* no date present */
|
|
strcpy (buf, " none");
|
|
return;
|
|
}
|
|
yr = (long) date / 1000 + 1970;
|
|
day = date % 1000;
|
|
if (yr & 3) {
|
|
days[1] = 28;
|
|
} else {
|
|
days[1] = 29;
|
|
}
|
|
for (mon = 0; mon < 12; mon++) {
|
|
if (day <= days[mon]) {
|
|
break;
|
|
}
|
|
day -= days[mon];
|
|
}
|
|
sprintf (buf, "%2ld-%3s-%04ld", day, months[mon], yr);
|
|
}
|
|
|
|
/* Convert RSTS timestamp (minutes to midnight) into am/pm style time string */
|
|
void cvttime (const struct rstsdt *dt, char *buf)
|
|
{
|
|
uint16_t time = dt->minutes;
|
|
int hour, min;
|
|
char m;
|
|
|
|
if (time == 0) { /* no time present */
|
|
strcpy (buf, " none");
|
|
return;
|
|
}
|
|
time = 1440 - time; /* now time since midnight */
|
|
hour = time / 60;
|
|
min = time % 60;
|
|
if (hour >= 12) {
|
|
hour -= 12;
|
|
m = 'p';
|
|
} else {
|
|
m = 'a';
|
|
}
|
|
if (hour == 0) {
|
|
hour = 12;
|
|
}
|
|
sprintf (buf, "%2d:%02d %1cm", hour, min, m);
|
|
}
|
|
|
|
/* Convert RSTS timestamp (minutes to midnight) into am/pm style time string
|
|
but with seconds and 2-digit fraction included */
|
|
void cvthms (const struct rstsdt *dt, char *buf)
|
|
{
|
|
uint16_t time = dt->minutes;
|
|
int hour, min, sec, ticks = dt->ticks;
|
|
char m;
|
|
|
|
if (time == 0) { /* no time present */
|
|
strcpy (buf, " none");
|
|
return;
|
|
}
|
|
time = 1440 - time; /* now time since midnight */
|
|
hour = time / 60;
|
|
min = time % 60;
|
|
if (hour >= 12) {
|
|
hour -= 12;
|
|
m = 'p';
|
|
} else {
|
|
m = 'a';
|
|
}
|
|
if (hour == 0) {
|
|
hour = 12;
|
|
}
|
|
sec = 60 - dt->seconds;
|
|
if (ticks != 0) {
|
|
ticks = hertz - ticks;
|
|
}
|
|
/* convert to centiseconds, properly rounded */
|
|
ticks = (ticks * 100 + hertz / 2) / hertz;
|
|
sprintf (buf, "%2d:%02d:%02d.%02d %1cm", hour, min, sec, ticks, m);
|
|
}
|
|
|
|
/* Convert current zone to a string, as "name (h:mm)" with the numeric
|
|
offset in parentheses. */
|
|
void cvttz (char *buf)
|
|
{
|
|
int hm = curoff / 60;
|
|
|
|
sprintf (buf, "%s (%d:%02d)", curabbr, hm / 60, hm % 60);
|
|
}
|
|
|
|
/* Convert RSTS time data to full date, time, and zone string */
|
|
void cvtdt (const struct rstsdt *dt)
|
|
{
|
|
cvtdate (dt, dtstr);
|
|
dtstr[DATELEN] = ' ';
|
|
cvthms (dt, dtstr + DATELEN + 1);
|
|
dtstr[DATELEN + 1 + RTIMELENX] = ' ';
|
|
cvttz (dtstr + DATELEN + 1 + RTIMELENX + 1);
|
|
}
|