with GNATCOLL.Utils;          use GNATCOLL.Utils;
with Ada.Calendar.Formatting; use Ada.Calendar;
with Ada.Exceptions;          use Ada.Exceptions;
with Ada.Text_IO;             use Ada.Text_IO;

procedure Run is
   procedure Assert (T1                   : String;
                     Expected_UTC_Year    : Year_Number := 1901;
                     Expected_UTC_Month   : Month_Number := 1;
                     Expected_UTC_Day     : Day_Number := 1;
                     Expected_UTC_Seconds : Day_Duration := 0.0);
   --  Compare two dates. Use 1901 to indicate No_Time

   procedure Assert (T1                   : String;
                     Expected_UTC_Year    : Year_Number := 1901;
                     Expected_UTC_Month   : Month_Number := 1;
                     Expected_UTC_Day     : Day_Number := 1;
                     Expected_UTC_Seconds : Day_Duration := 0.0)
   is
      T1_1 : Time;
      Y : Year_Number;
      M : Month_Number;
      D : Day_Number;
      S : Day_Duration;
      Leap_Second : Boolean;
      Expected : Time;
   begin
      T1_1 := Time_Value (T1);

      if Expected_UTC_Year = 1901 then
         Expected := No_Time;
      else
         Expected := Ada.Calendar.Formatting.Time_Of
            (Expected_UTC_Year, Expected_UTC_Month, Expected_UTC_Day,
             Seconds   => Expected_UTC_Seconds,
             Time_Zone => 0);
      end if;

      if T1_1 /= Expected then
         Ada.Calendar.Formatting.Split
            (T1_1, Year => Y, Month => M, Day => D, Seconds => S,
             Leap_Second => Leap_Second,
             Time_Zone => 0);

         Put_Line ("Error while parsing '" & T1 & "'");
         Put_Line
            ("   Exp: " &
             Expected_UTC_Year'Img & '/' &
             Expected_UTC_Month'Img & '/' &
             Expected_UTC_Day'Img & ' ' &
             Expected_UTC_Seconds'Img);
         Put_Line
            ("   Got: " & Y'Img & '/' & M'Img & '/' & D'Img & ' ' & S'Img);
      end if;

      if Expected /= No_Time
        and then Formatting.Time_Of
                   (Expected_UTC_Year, Expected_UTC_Month, Expected_UTC_Day)
         /= Truncate (Expected)
      then
         Put_Line ("Error on truncation '" & T1 & "'" & Expected_UTC_Year'Img
                   & Expected_UTC_Month'Img & Expected_UTC_Day'Img);
      end if;

   exception
      when E : others =>
         Put_Line ("Unexpected exception " & Exception_Information (E));
         Put_Line ("while parsing '" & T1 & "'");
   end Assert;

begin
   --  When this test has run, the local timezone has been set to GMT+2
   --  by setting the "TZ" environment variable.

   Assert ("1973-01-01",   1973, 01, 01, 0.0);
   Assert ("Jan 03, 1973", 1973, 01, 03, 0.0);
   Assert ("20061219",     2006, 12, 19, 0.0);

   --  Mail format
   Assert ("Tue, 19 Dec 2006 13:59:05+00",
           2006, 12, 19, 13.0 * 3600.0 + 59.0 * 60.0 + 5.0);

   --  Postgres format
   Assert ("1973-01-03 12:00:00+01",
           1973, 01, 03, 11.0 * 3600.0);
   Assert ("2008-07-04 16:14:31.730447+02",
           2008, 07, 04, 14.0 * 3600.0 + 14.0 * 60.0 + 31.0);
   Assert ("2009-08-04 16:15:23-02:30",
           2009, 08, 04, 18.0 * 3600.0 + 45.0 * 60.0 + 23.0);
   Assert ("2016-02-12 03:35:23+0430",
           2016, 02, 11, 23.0 * 3600.0 + 5.0 * 60.0 + 23.0);

   --  Iso format
   Assert ("1973-01-03T12:00:00Z", 1973, 01, 03, 12.0 * 3600.0);
   Assert ("1973-01-03T00:00:00Z", 1973, 01, 03, 0.0);

   --  Invalid date
   Assert ("2005-15-18");

   --  Time zones
   Assert ("2000-07-25 12:58:00+02",
           2000, 7, 25, 10.0 * 3600.0 + 58.0 * 60.0);
   Assert ("2000-07-25 12:58:00-02",
           2000, 7, 25, 14.0 * 3600.0 + 58.0 * 60.0);

   --  The following are troublesome: gnat.calendar converts based on the
   --  current timezone (+02:00 in Paris), and therefore we end up with a
   --  date the previous day if we are not careful.
   Assert ("2000-07-25 00:00:00", 2000, 07, 25, 0.0);
   Assert ("2000-07-25 00:00:00+00", 2000, 07, 25, 0.0);

end Run;
