# be2f474dad25fe0a139975300fca5ed12131c42e94fac64600d5482bf883ea0e Elan 2.0.0-alpha4 valid

main
  variable reply set to ""
  while not reply.upperCase().equals("Q")
    set reply to input("RETURN for time now or Unix time (positive integer) or Q to quit")
    if reply.equals("") then
      variable now set to divAsInt(clock(), 1000)
      call print(now)
      call print(getDate(now))
    else
      try
        variable td set to int(reply)
        if td >= 0 then
          call print(getDate(td))
        end if
      catch e as ElanRuntimeError
      end try
    end if
  end while
end main

function getDate(unixSecs as Int) returns String
  variable dt set to dateTime(unixSecs)
  variable hour set to dt.item_0
  variable minute set to dt.item_1
  variable second set to dt.item_2
  variable days set to dt.item_3
  variable year set to dt.item_4
  variable weekday set to dt.item_5
  variable z2 set to "00"
  variable h set to padLwithZero(hour)
  variable m set to padLwithZero(minute)
  variable s set to padLwithZero(second)
  variable startDays set to getStartDays()
  variable startDaysL set to startDaysList(year, startDays)
  variable month_day set to monthDay(startDaysL, (days mod startDays[12]))
  variable month set to month_day.item_0
  variable day set to month_day.item_1
  variable dayName set to getWeekdayName(weekday)
  variable d set to padLwithZero(day)
  variable monthName set to getMonthName(month)
  return $"{dayName}, {d} {monthName} {year} {h}:{m}:{s} UTC"
end function

test test_getDate
  assert getDate(1774946991) is "Tue, 31 Mar 2026 08:49:51 UTC"
end test

function dateTime(unixSecs as Int) returns (Int, Int, Int, Int, Int, Int)
  # get separate values from Unix time
  variable hour set to (divAsInt(divAsInt(unixSecs, 60), 60) mod 24)
  variable minute set to divAsInt(unixSecs, 60) mod 60
  variable second set to (unixSecs mod 60)
  # days and years from Unix epoch
  variable unixDay set to divAsInt(unixSecs, daySecs)
  variable years set to ((unixDay + 1)/365.24).floor()
  # this year and weekday
  variable year set to unixYear + years
  variable weekday set to (unixDay + unixWeekday) mod 7
  # day number (1-365 or 1-366) in this year
  variable day set to dayInYear(year, unixDay)
  return (hour, minute, second, day, year, weekday)
end function

test test_dateTime
  assert dateTime(1774946991) is (8, 49, 51, 90, 2026, 2)
end test

function dayInYear(year as Int, unixDays as Int) returns Int
  # unixDays start at 0, months, day numbers start at 1
  variable dayNumber set to unixDays + 1
  if year > unixYear then
    # discount previous nominal years
    set dayNumber to dayNumber - 365*(year - unixYear)
    # discount previous leap days
    for y in range(unixYear, year)
      if leap(y) then
        set dayNumber to dayNumber - 1
      end if
    end for
  end if
  return dayNumber
end function

test test_dayInYear
  assert dayInYear(2025, 1) is -20087
end test

function leap(year as Int) returns Boolean
  variable leapYear set to false
  if (year mod 4) is 0 then
    set leapYear to true
    if ((year mod 100) is 0) and ((divAsInt(year, 100) mod 4) isnt 0) then
      set leapYear to false
    end if
  end if
  return leapYear
end function

test test_leap
  assert leap(2025) is false
  assert leap(2024) is true
  assert leap(1900) is false
  assert leap(2000) is true
end test

function monthDay(startDays as List<of Int>, dayNumber as Int) returns (Int, Int)
  # get month (1-12) & day (1-31) from dayNumber (1-365 or 1-366)
  variable month set to 1
  variable day set to 0
  variable monthFound set to false
  while monthFound is false
    if (dayNumber < startDays[month]) then
      set monthFound to true
      set day to dayNumber - startDays[month - 1] + 1
    else
      set month to month + 1
    end if
  end while
  return (month, day)
end function

function startDaysList(year as Int, startDays as List<of Int>) returns List<of Int>
  # alter month start days if leap year
  variable startDaysL set to getStartDays()
  if leap(year) then
    for m in range(2, 13)
      set startDaysL to startDaysL.withSet(m, startDaysL[m] + 1)
    end for
  end if
  return startDaysL
end function

# Unix epoch is 1970.01.01 00:00:00 Thursday
constant unixYear set to 1970

constant unixWeekday set to 4

constant daySecs set to 86400

# day and month names, and day numbers
function getWeekdayName(weekDay as Int) returns String
  variable names set to ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
  return names[weekDay]
end function

test test_getWeekdayName
  assert getWeekdayName(0) is "Sun"
  assert getWeekdayName(6) is "Sat"
end test

function getMonthName(month as Int) returns String
  variable names set to ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
  return names[month - 1]
end function

test test_getMonthName
  assert getMonthName(1) is "Jan"
  assert getMonthName(12) is "Dec"
end test

function getStartDays() returns List<of Int>
  return [1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]
end function

function padLwithZero(i as Int) returns String
  return pad("L", "00", i.toString())
end function

test test_padLwithZero
  assert padLwithZero(1) is "01"
  assert padLwithZero(10) is "10"
  assert padLwithZero(0) is "00"
end test

function pad(d as String, p as String, s as String) returns String
  # d: L or R for pad left or right
  # p: output string pattern of pad characters and of length
  # s: input string
  variable sR set to s
  if p.length() > s.length() then
    if d.upperCase().equals("L") then
      variable ps set to p + s
      set sR to ps.subString(ps.length() - p.length(), ps.length())
    elif d.upperCase().equals("R") then
      set sR to (s + p).subString(0, p.length())
    end if
  end if
  return sR
end function
