-- File: Dates.mesa  last edit by:
  -- Smokey  on: Dec 2, 1980 4:20 PM
  -- Mark  on: Apr 19, 1979 10:27 AM
  -- Karlton  on: Nov 11, 1980 11:03 AM
  
DIRECTORY
  Date USING [Packed],
  Storage USING [EmptyString, String],
  String USING [
    AppendChar, EqualString, EquivalentString, StringBoundsFault, StringToNumber, UpperCase],
  System USING [gmtEpoch],
  Time USING [Append, Current, Invalid, Pack, Unpack, Unpacked];
  
Dates: PROGRAM
  IMPORTS Storage, String, Time EXPORTS Date =
  
  BEGIN
  
-- types

  BufferObject: TYPE = RECORD [
    p: CARDINAL,
    s: STRING];
  Buffer: TYPE = POINTER TO BufferObject;
  
-- exported procedures

  StringToPacked: PUBLIC PROCEDURE [s: STRING] RETURNS[tod: Date.Packed] =
    BEGIN
    buffer: BufferObject ← [0, s];
    RETURN[
      IF Storage.EmptyString[s] OR String.EquivalentString[s, " 5-Feb-37 22:28:15"L]
	THEN System.gmtEpoch
      ELSE GetDate[@buffer]]
    END;
    
  PackedToString: PUBLIC PROCEDURE [pt: Date.Packed] RETURNS[STRING] =
    BEGIN
    s: STRING ← NIL;
    IF pt = LAST[LONG CARDINAL] THEN RETURN[s];
    s ← Storage.String[20];
    Time.Append[s, Time.Unpack[pt]];
    RETURN[s]
    END;
    
-- local procedures

  GetDate: PROCEDURE [b: Buffer] RETURNS [Date.Packed] =
    BEGIN
    token: STRING ← [20];
    monthFound, thisIsMonth: BOOLEAN ← FALSE;
    index: CARDINAL[0..12);
    ambig1, ambig2: CARDINAL ← 0;
    number: CARDINAL;
    u: Time.Unpacked ← [0,0,0,0,0,0,0,0,FALSE];
    now: Time.Unpacked ← Time.Unpack[Time.Current[]];
    DO
      NextItem[b,token];
      IF token.length = 0 THEN EXIT;
      [thisIsMonth, index] ← IsMonth[token];
      IF thisIsMonth THEN
	BEGIN u.month ← index; monthFound ← TRUE; LOOP END;
      IF IsNumber[token] THEN
	BEGIN
	IF u.year > 0 THEN
	  BEGIN          -- working on the time of day 
	  number ← String.StringToNumber[token, 10];
	  u.hour ← number;
	  NextItem[b,token];
	  IF IsNumber[token] THEN  --  :s get tossed by NextItem routine
	    u.minute ← String.StringToNumber[token, 10];
	  NextItem[b,token];
	  IF IsNumber[token] THEN
	    BEGIN
	    u.second ← String.StringToNumber[token, 10];
	    NextItem[b,token];
	    END;
	  IF String.EqualString[token,"PM"L] AND u.hour < 12 THEN 
	    u.hour ← u.hour+12;
	  END
	ELSE
	  BEGIN
	  number ← String.StringToNumber[token, 10];
	  IF number > 32 THEN u.year ← number
	  ELSE IF number > 12 THEN u.day ← number
	  ELSE IF ambig1 = 0 THEN ambig1 ← number
	  ELSE ambig2 ← number;
	  END;
	END;
      ENDLOOP;
    IF ambig2 > 0 THEN
      BEGIN u.month ← ambig1 - 1; u.day ← ambig2; monthFound ← TRUE; END
    ELSE IF ambig1 > 0 THEN
      IF u.day = 0 THEN u.day ← ambig1
      ELSE IF u.month = 0 AND ~monthFound THEN
	BEGIN
	u.month ← ambig1 - 1;
	monthFound ← TRUE;
	END;
    IF u.day = 0 THEN u.day ← now.day;
    IF ~monthFound THEN u.month ← now.month;
    IF u.year = 0 THEN u.year ← now.year;
    IF u.year < 37 THEN u.year ← u.year + 2000
    ELSE IF u.year < 100 THEN u.year ← u.year + 1900;
    RETURN[Time.Pack[u, TRUE]]
    END;
    
  IsMonth: PROCEDURE [candidate: STRING] RETURNS [BOOLEAN, CARDINAL] =
    BEGIN
    Months: ARRAY [0..12) OF STRING = [
      "JANUARY"L,"FEBRUARY"L,"MARCH"L,"APRIL"L,"MAY"L,"JUNE"L,"JULY"L,
      "AUGUST"L,"SEPTEMBER"L,"OCTOBER"L,"NOVEMBER"L,"DECEMBER"L];
    i,j: CARDINAL;
    test: STRING;
    IF candidate.length = 0 THEN RETURN[FALSE, 0];
    IF candidate[0] NOT IN ['A..'Z] THEN RETURN[FALSE, 0];
    IF candidate.length >= 3 THEN
      FOR i IN [0..12) DO
	test ← Months[i];
	IF candidate.length <= test.length THEN
	  FOR j IN [0..candidate.length) DO
	    IF candidate[j] # test[j] THEN EXIT;
	    REPEAT FINISHED => RETURN[TRUE, i];
	    ENDLOOP;
	ENDLOOP;
    ERROR Time.Invalid;
    END;
    
  IsNumber: PROCEDURE [s: STRING] RETURNS [BOOLEAN] =
    BEGIN
    i: CARDINAL;
    IF s.length = 0 THEN RETURN[FALSE];
    FOR i IN [0..s.length) DO
      IF s[i] NOT IN ['0..'9] THEN RETURN[FALSE];
      ENDLOOP;
    RETURN[TRUE]
    END;
    
  NextItem: PROCEDURE [b: Buffer, token: STRING] =
    BEGIN OPEN String;
    c: CHARACTER;
    alpha, number: BOOLEAN ← FALSE;
    token.length ← 0;
    WHILE b.p < b.s.length DO 
      c ← b.s[b.p];
      b.p ← b.p+1;
      SELECT c FROM
	IN ['a..'z], IN ['A..'Z] =>
	  BEGIN
	  IF number THEN
	    BEGIN
	    b.p ← b.p-1;
	    EXIT
	    END;
	  AppendChar[token, UpperCase[c] ! String.StringBoundsFault => CONTINUE];
	  alpha ← TRUE;
	  END;
	IN ['0..'9] =>
	  BEGIN
	  IF alpha THEN
	    BEGIN
	    b.p ← b.p-1;
	    EXIT
	    END;
	  AppendChar[token, c ! String.StringBoundsFault => CONTINUE];
	  number ← TRUE;
	  END;
	ENDCASE => IF token.length # 0 THEN EXIT;
      ENDLOOP;
    RETURN
    END;
    
    
END.  -- Date.mesa