m3core/src/time/POSIX/DateBsd.m3


 Copyright (C) 1994, Digital Equipment Corporation        
 All rights reserved.                                     
 See the file COPYRIGHT for a full description.           
                                                          
 Last modified on Thu May 15 13:54:02 PDT 1997 by heydon  
      modified on Fri Oct 28 14:12:53 PDT 1994 by kalsow  
      modified on Thu Jan 28 10:45:24 PST 1993 by mjordan 
      modified on Fri Dec  4 17:35:53 PST 1992 by mcjones 

UNSAFE MODULE DateBsd EXPORTS Date;

IMPORT Time, M3toC, Utime, TimePosix;

VAR mu := NEW(MUTEX);
Protect the static storage used by Utime.localtime and Utime.gmtime (see the localtime(3) manpage).

REVEAL TimeZone = BRANDED "Date.TimeZone" REF INTEGER;

PROCEDURE FromTime(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;

PROCEDURE ToTime(READONLY d: T): Time.T RAISES {Error} =
This function uses mktime(3) to convert a Utime.struct_tm into a Utime.time_t, which is then converted via FLOAT to a Time.T. The mktime(3) function ignores the tm_gmtoff field of its argument, so we account for the timezone information d.offset by determining the offset of any local time and adjusting the result by the difference between d.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.

interface Utime is in: