Algorithms
Date & Time
 previous   up   next 

Determine if a given year is a leap year

The function isLeapYear is part of the "time.s7i" library. It returns TRUE if the year is a leap year in the Gregorian calendar.

const func boolean: isLeapYear (in integer: year) is
  return (year rem 4 = 0 and year rem 100 <> 0) or year rem 400 = 0;

Compute the weekday

The function dayOfWeek is part of the "time.s7i" library. It delivers 1 for monday, 2 for tuesday, and so on up to 7 for sunday. This definition of weekday is according to the ISO 8601 week date.

const func integer: dayOfWeek (in time: aTime) is func
  result
    var integer: weekday is 0;
  local
    var integer: year is 0;
    var integer: month is 0;
  begin
    year := aTime.year;
    month := aTime.month;
    if month <= 2 then
      decr(year);
      month +:= 12;
    end if;
    weekday := succ(pred(year + year mdiv 4 - year mdiv 100 + year mdiv 400 +
        (31 * (month - 2)) mdiv 12 + aTime.day) mod 7);
  end func;

Compute the day of year

The function dayOfYear is part of the "time.s7i" library. It delivers 1 for the 1. of january.

const func integer: dayOfYear (in time: tim) is func
  result
    var integer: dayOfYear is 0;
  begin
    if isLeapYear(tim.year) then
      dayOfYear := [](0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335)[tim.month] + tim.day;
    else
      dayOfYear := [](0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334)[tim.month] + tim.day;
    end if;
  end func;

Compute the week number of a year

The function weekOfYear is part of the "time.s7i" library. According to ISO 8601: Week number 1 of every year contains the 4. of january. Since 1st to 3rd of january might be in the previous week there can be also a week number 0. This function computes week numbers from 0 to 53 for weeks belonging to the year of the given date.

const func integer: weekOfYear (in var integer: year, in integer: dayOfYear) is func
  result
    var integer: weekNumber is 0;
  local
    var integer: weekDayOfJan4 is 0;
  begin
    year := pred(year);
    weekDayOfJan4 := succ(pred(year + year mdiv 4 - year mdiv 100 + year mdiv 400 + 32) mod 7);
    weekNumber := (dayOfYear + weekDayOfJan4 - 5) mdiv 7 + 1;
  end func;

const func integer: weekOfYear (in time: tim) is
  return weekOfYear(tim.year, dayOfYear(tim));

Compute the year of the ISO 8601 week date

The function weekDateYear is part of the "time.s7i" library. Compute the year of the ISO 8601 week date. At the beginning and the end of an Gregorian calendar year there might be days which belong to a week of the previous or next year. For example 2005-01-01 is 2004-W53-6 and 2007-12-31 is 2008-W01-1. The year returned by this function is in the range 'pred(tim.year)' to 'succ(tim.year)'.

const func integer: weekDateYear (in time: tim) is func
  result
    var integer: weekDateYear is 0;
  local
    var integer: weekNum is 0;
  begin
    weekNum := weekOfYear(tim.year, dayOfYear(tim));
    if weekNum <= 0 then
      weekDateYear := pred(tim.year);
    elsif weekNum >= 53 and tim.day >= 29 and
        weekOfYear(succ(tim.year), 31 - tim.day) = 1 then
      weekDateYear := succ(tim.year);
    else
      weekDateYear := tim.year;
    end if;
  end func;

Compute the week of the ISO 8601 week date

The function weekDateWeek is part of the "time.s7i" library. Compute the week number of the ISO 8601 week date. At the beginning and the end of an Gregorian calendar year there might be days which belong to a week of the previous or next year. For example 2005-01-01 is 2004-W53-6 and 2007-12-31 is 2008-W01-1. The week number returned by this function is in the range 1 to 53.

const func integer: weekDateWeek (in time: tim) is func
  result
    var integer: weekNum is 0;
  begin
    weekNum := weekOfYear(tim.year, dayOfYear(tim));
    if weekNum <= 0 then
      weekNum := weekOfYear(pred(tim.year), 366);
    elsif weekNum >= 53 and tim.day >= 29 and
        weekOfYear(succ(tim.year), 31 - tim.day) = 1 then
      weekNum := 1;
    end if;
  end func;

Calculate the number of days in a month

The function daysInMonth is part of the "time.s7i" library.

const func integer: daysInMonth (in integer: year, in integer: month) is func
  result
    var integer: leng is 0;
  local
    const set of integer: monthsOfLength31 is {1, 3, 5, 7, 8, 10, 12};
  begin
    if month in monthsOfLength31 then
      leng := 31;
    else
      if month = 2 then
        if isLeapYear(year) then
          leng := 29;
        else
          leng := 28;
        end if;
      else
        leng := 30;
      end if;
    end if;
  end func;

Convert a time into a julian day number

The julian day number is the number of days that have elapsed since January 1, 4713 BC in the proleptic Julian calendar. The function julianDayNumber is part of the "time.s7i" library.

const func integer: julianDayNumber (in time: aTime) is func
  result
    var integer: julianDayNumber is 0;
  local
    var integer: year is 0;
    var integer: month is 0;
  begin
    year := aTime.year;
    month := aTime.month;
    if month <= 2 then
      decr(year);
      month +:= 12;
    end if;
    julianDayNumber := (1461 * (year + 4800)) mdiv 4 +
                       (367 * (month - 2)) mdiv 12 -
                       (3 * ((year + 4900) mdiv 100)) mdiv 4 +
                       aTime.day - 32075;
  end func;

Convert a julian day number into a time

The julian day number is the number of days that have elapsed since January 1, 4713 BC in the proleptic Julian calendar. The function julianDayNumToTime is part of the "time.s7i" library.

const func time: julianDayNumToTime (in integer: julianDayNumber) is func
  result
    var time: aTime is time.value;
  local
    var integer: l is 0;
    var integer: n is 0;
    var integer: i is 0;
    var integer: j is 0;
  begin
    l := julianDayNumber + 68569;
    n := (4 * l) mdiv 146097;
    l := l - (146097 * n + 3) mdiv 4;
    i := (4000 * (l + 1)) mdiv 1461001;
    l := l - (1461 * i) mdiv 4 + 31;
    j := (80 * l) mdiv 2447;
    aTime.day := l - (2447 * j) mdiv 80;
    l := j mdiv 11;
    aTime.month := j + 2 - (12 * l);
    aTime.year := 100 * (n - 49) + i + l;
  end func;

Compute the easter date for a given year

const func time: easterDate (in integer: year) is func
  result
    var time: easterDate is time.value;
  local
    var integer: H1 is 0;
    var integer: H2 is 0;
    var integer: M is 0;
    var integer: N is 0;
    var integer: a is 0;
    var integer: b is 0;
    var integer: c is 0;
    var integer: d is 0;
    var integer: e is 0;
    var integer: f is 0;
  begin
    H1 := year mdiv 100;
    H2 := year mdiv 400;
     M := 15 + H1 - H2 - (8 * H1 + 13) mdiv 25;
     N := 4 + H1 - H2;

    # Gauss formula:
     a := year mod 19;
     b := year mod 4;
     c := year mod 7;
     d := (19 * a + M) mod 30;
     e := (2 * b + 4 * c + 6 * d + N) mod 7;
     f := 22 + d + e;

    if f = 57 then
      f := 50;
    end if;
    if d = 28 and e = 6 and a > 10 then
      f := 49;
    end if;

    easterDate.year := year;
    if f <= 31 then
      easterDate.month := 3;
    else
      easterDate.month := 4;
      f -:= 31;
    end if;
    easterDate.day := f;
  end func;

Compute seconds since the Unix Epoch

Return a time expressed in seconds since the Unix Epoch. The Unix Epoch (1970-01-01 00:00:00 UTC) corresponds to 0. The function timestamp1970 is part of the "time.s7i" library.

const func integer: timestamp1970 (in time: aTime) is func
  result
    var integer: seconds is 0;
  local
    var integer: yearBefore is 0;
  begin
    yearBefore := pred(aTime.year);
    seconds := (((yearBefore * 365 +
                  yearBefore mdiv 4 -
                  yearBefore mdiv 100 +
                  yearBefore mdiv 400 -
               719162 +
               pred(dayOfYear(aTime))) * 24 +
               aTime.hour) * 60 +
               aTime.minute) * 60 +
               aTime.second -
               aTime.timeZone * 60;
  end func;

Write a calendar

Write a calendar to standard output.

$ include "seed7_05.s7i";
  include "time.s7i";

const func string: center (in string: stri, in integer: length) is
  return ("" lpad (length - length(stri)) div 2 <& stri) rpad length;

const proc: printCalendar (in integer: year, in integer: cols) is func
  local
    var time: date is time.value;
    var integer: dayOfWeek is 0;
    const array string: monthNames is [] ("January", "February", "March", "April", "May", "June",
        "July", "August", "September", "October", "November", "December");
    var array array string: monthTable is 12 times 9 times "";
    var string: str is "";
    var integer: month is 0;
    var integer: position is 0;
    var integer: row is 0;
    var integer: column is 0;
    var integer: line is 0;
  begin
    for month range 1 to 12 do
      monthTable[month][1] := " " & center(monthNames[month], 20);
      monthTable[month][2] := " Mo Tu We Th Fr Sa Su";
      date := date(year, month, 1);
      dayOfWeek := dayOfWeek(date);
      for position range 1 to 43 do
        if position >= dayOfWeek and position - dayOfWeek < daysInMonth(date.year, date.month) then
          str := succ(position - dayOfWeek) lpad 3;
        else
          str := "" lpad 3;
        end if;
        monthTable[month][3 + pred(position) div 7] &:= str;
      end for;
    end for;
    writeln(center(str(year),cols * 24 + 4));
    writeln;
    for row range 1 to succ(11 div cols) do
      for line range 1 to 9 do
        for column range 1 to cols do
          if pred(row) * cols + column <= 12 then
            write("   " & monthTable[pred(row) * cols + column][line]);
          end if;
        end for;
        writeln;
      end for;
    end for;
  end func;

const proc: main is func
  begin
    printCalendar(1969, 3);
  end func;

 previous   up   next