aboutsummaryrefslogtreecommitdiffstats
path: root/lib/psitime.cc
blob: 95ba6da62109ca61592c59a1e74fb77ffa40f0e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include "psitime.h"
#include <stdlib.h>

PsiTime::PsiTime(psi_timeval *_ptv, psi_timezone *_ptz) {
	if (_ptv != 0L)
		ptv = *_ptv;
	if (_ptz != 0L) {
		ptz = *_ptz;
		ptzValid = true;
	} else
		ptzValid = false;
	/* get our own timezone */
	gettimeofday(NULL, &utz);
	psi2unix();
}

PsiTime::~PsiTime() {
}

void PsiTime::setUnixTime(struct timeval *_utv) {
	if (_utv != 0L)
		utv = *_utv;
	unix2psi();
}

void PsiTime::setUnixNow(void) {
	gettimeofday(&utv, &utz);
	unix2psi();
}


void PsiTime::setPsiTime(psi_timeval *_ptv) {
	if (_ptv != 0L)
		ptv = *_ptv;
	psi2unix();
}

void PsiTime::setPsiZone(psi_timezone *_ptz) {
	if (_ptz != 0L) {
		ptz = *_ptz;
		ptzValid = true;
	}
	psi2unix();
}

struct timeval &PsiTime::getTimeval(void) {
	return utv;
}

time_t PsiTime::getTime(void) {
	return utv.tv_sec;
}

psi_timeval &PsiTime::getPsiTimeval(void) {
	return ptv;
}

ostream &operator<<(ostream &s, const PsiTime &t) {
	const char *fmt = "%c";
	char buf[100];
	strftime(buf, sizeof(buf), fmt, localtime(&t.utv.tv_sec));
	s << buf;
	return s;
}

/**
 * The difference between
 * EPOC epoch (01.01.0001 00:00:00)
 * and Unix epoch (01.01.1970 00:00:00)
 * in microseconds.
 */
#define EPOCH_DIFF 0x00dcddb30f2f8000ULL

static unsigned long long
evalOffset(psi_timezone ptz, bool valid) {
	unsigned long long offset = 0;

	if (valid) {
		offset = ptz.utc_offset;
		if ((ptz.dst_zones & 0x40000000) || (ptz.dst_zones & ptz.home_zone))
			offset += 3600;
	} else {
		const char *offstr = getenv("PSI_TZ");
		if (offstr != 0L) {
			char *err = 0L;
			offset = strtoul(offstr, &err, 0);
			if (err != 0L)
				offset = 0;
		}
	}
	offset *= 1000000;
	return offset;
}

void PsiTime::psi2unix(void) {
	unsigned long long micro = ptv.tv_high;
	micro = (micro << 32) | ptv.tv_low;

	/* Substract Psion's idea of UTC offset */
	micro -= evalOffset(ptz, ptzValid);
	micro -= EPOCH_DIFF;

	utv.tv_sec = micro / 1000000;
	utv.tv_usec = micro % 1000000;
}

void PsiTime::unix2psi(void) {
	unsigned long long micro = utv.tv_sec * 1000000 + utv.tv_usec;

	/* Add Psion's idea of UTC offset */
	micro += EPOCH_DIFF;
	micro += evalOffset(ptz, ptzValid);

	ptv.tv_low = micro & 0x0ffffffff;
	ptv.tv_high = (micro >> 32) & 0x0ffffffff;
}