/* time.pk - Time-related definitions for GNU poke.  */

/* Copyright (C) 2019, 2021, 2022 John Darrington.  */

/* This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

type Timespec = struct
{
  int<64> sec;
  int<64> nsec;
};

fun gettimeofday = Timespec:
{
  var time = gettime;
  return Timespec {sec = time[0], nsec = time[1]};
}

fun ptime = (uint<64> seconds) void:
{
    fun days_in_year = (int year) int:
    {
      if (year % 400 == 0) return 366;
      if (year % 100 == 0) return 365;
      if (year % 4 == 0) return 366;
      return 365;
    }

    fun days_in_month = (int year, int month) int:
    {
      if (month < 0)
	raise E_out_of_bounds;
      if (month > 11)
	raise E_out_of_bounds;

      /* Deal with February.  */
      if (month == 1)
	{
	  if (days_in_year (year) == 366)
	    return 29;
	  else
	    return 28;
	}

      /* 30 days hath Sept, Apr, June & Nov ...  */
      if (month in [8, 3, 5, 10])
        return 30;

      return 31;
    }

    var month_names = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
                       "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

    var year = 1970, month = 0, day = 0, hour = 0, minute = 0;
    while (seconds >= 0)
      {
	if (seconds < 24 * 60 * 60)
	  {
	    hour = seconds / 3600;
	    seconds = seconds % 3600;
	    minute = seconds / 60;
	    seconds = seconds % 60;
	    break;
	  }
	else if (seconds < days_in_month (year, month) * 24 * 60 * 60)
	  {
	    seconds = seconds - 24 * 60 * 60;
	    day++;
	  }
	else if (seconds < days_in_year (year) * 24 * 60 * 60)
	  {
	    seconds = seconds - days_in_month (year, month) * 24 * 60 * 60;
	    month++;
	  }
	else
	  {
	    seconds = seconds - days_in_year (year) * 24 * 60 * 60;
	    year++;
	  }
      }

    printf ("%<date-time:%i32d-%s-%i32d %i32d:%i32d:%i32d%>",
	    year, month_names[month], day + 1,
	    hour, minute, seconds);
}

type POSIX_Time32 = struct
{
  uint<32>  seconds;

  method _print = void:
  {
    print "#<";
    ptime (seconds);
    print ">";
  }
};

type POSIX_Time64 = struct
{
  uint<64>  seconds;

  method _print = void:
  {
    print "#<";
    ptime (seconds);
    print ">";
  }
};
