UNSAFE MODULEDateBsd EXPORTSDate ; IMPORT Time, M3toC, Utime, TimePosix; VAR mu := NEW(MUTEX);
Protect the static storage used byUtime.localtime
andUtime.gmtime
(see the localtime(3) manpage).
REVEAL TimeZone = BRANDED "Date.TimeZone" REF INTEGER; PROCEDUREFromTime (t: Time.T; z: TimeZone := NIL): T = VAR date : T; tv : Utime.struct_timeval; tm : Utime.struct_tm_star; BEGIN tv := TimePosix.ToUtime(t); LOCK mu DO IF (z = NIL) OR (z^ = Local^) THEN tm := Utime.localtime(ADR(tv.tv_sec)); ELSIF z^ = UTC^ THEN tm := Utime.gmtime(ADR(tv.tv_sec)); ELSE (* unknown timezone *) <* ASSERT FALSE *> END; date.year := tm.tm_year + 1900; date.month := VAL(tm.tm_mon, Month); date.day := tm.tm_mday; date.hour := tm.tm_hour; date.minute := tm.tm_min; date.second := tm.tm_sec; date.weekDay := VAL(tm.tm_wday, WeekDay); (* The "tm.tm_gmtoff" field is seconds *east* of GMT, whereas the "date.offset" field is seconds *west* of GMT, so a negation is necessary. *) date.offset := - (tm.tm_gmtoff); date.zone := M3toC.CopyStoT (tm.tm_zone); END; RETURN date; END FromTime; PROCEDUREToTime (READONLY d: T): Time.T RAISES {Error} =
This function uses mktime(3) to convert aUtime.struct_tm
into aUtime.time_t
, which is then converted viaFLOAT
to aTime.T
. The mktime(3) function ignores thetm_gmtoff
field of its argument, so we account for the timezone informationd.offset
by determining the offset of any local time and adjusting the result by the difference betweend.offset
and the local offset.
CONST SecsPerHour = 60 * 60; VAR tm: Utime.struct_tm; time, now: Utime.time_t; local_now: Utime.struct_tm_star; t: Time.T; BEGIN (* prepare call to mktime(3) *) tm.tm_sec := d.second; tm.tm_min := d.minute; tm.tm_hour := d.hour; tm.tm_mday := d.day; tm.tm_mon := ORD(d.month); tm.tm_year := d.year - 1900; (* tm.tm_wday ignored *) tm.tm_isdst := 0; (* tell mktime that DST is not in effect *) (* tm_zone, tm_gmtoff ignored *) time := Utime.mktime(ADR(tm)); IF time = -1 THEN RAISE Error END; (* adjust result to reflect "d.offset" *) EVAL Utime.time(ADR(now)); local_now := Utime.localtime(ADR(now)); IF local_now.tm_isdst > 0 THEN (* decrement the local time zone by one hour if DST is in effect *) DEC(local_now.tm_gmtoff, 1 * SecsPerHour) END; (* As above, we must negate "d.offset" to account for the opposite sense of that field compared to Unix. *) DEC(time, (-d.offset) - local_now.tm_gmtoff); (* convert to a "Time.T" *) t := FLOAT(time, LONGREAL); RETURN t; END ToTime; BEGIN Local := NEW(TimeZone); Local^ := 0; UTC := NEW(TimeZone); UTC^ := 1; END DateBsd.