include "enable_io.s7i";
const type: time is new object struct
var integer: year is 0;
var integer: month is 1;
var integer: day is 1;
var integer: hour is 0;
var integer: minute is 0;
var integer: second is 0;
var integer: micro_second is 0;
var integer: timeZone is 0;
var boolean: daylightSavingTime is FALSE;
end struct;
const proc: GET_TIME (inout integer: year, inout integer: month, inout integer: day,
inout integer: hour, inout integer: minute, inout integer: second,
inout integer: micro_second, inout integer: timeZone,
inout boolean: daylightSavingTime) is action "TIM_NOW";
const proc: AWAIT_TIME (in integer: year, in integer: month, in integer: day,
in integer: hour, in integer: minute, in integer: second,
in integer: micro_second, in integer: timeZone) is action "TIM_AWAIT";
const proc: FROM_TIMESTAMP (in integer: timestamp, inout integer: year,
inout integer: month, inout integer: day, inout integer: hour,
inout integer: minute, inout integer: second, inout integer: micro_second,
inout integer: timeZone, inout boolean: daylightSavingTime) is action "TIM_FROM_TIMESTAMP";
const proc: SET_LOCAL_TZ (in integer: year, in integer: month, in integer: day,
in integer: hour, in integer: minute, in integer: second,
inout integer: timeZone, inout boolean: daylightSavingTime) is action "TIM_SET_LOCAL_TZ";
const func boolean: isLeapYear (in integer: year) is
return (year rem 4 = 0 and year rem 100 <> 0) or year rem 400 = 0;
const func integer: daysInYear (in integer: year) is func
result
var integer: days is 0;
begin
if isLeapYear(year) then
days := 366;
else
days := 365;
end if;
end func;
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;
const func integer: daysInMonth (in time: date) is
return daysInMonth(date.year, date.month);
const func string: strDate (in time: aTime) is
return aTime.year lpad0 4 <& "-" <&
aTime.month lpad0 2 <& "-" <&
aTime.day lpad0 2;
const func string: strTime (in time: aTime) is func
result
var string: isoTime is "";
begin
isoTime := aTime.hour lpad0 2 <& ":" <&
aTime.minute lpad0 2 <& ":" <&
aTime.second lpad0 2;
if aTime.micro_second <> 0 then
isoTime &:= "." <& aTime.micro_second lpad0 6;
end if;
end func;
const func string: strTimeZone (in time: aTime) is func
result
var string: timeZone is "UTC";
begin
if aTime.timeZone <> 0 then
if aTime.timeZone > 0 then
timeZone &:= "+";
end if;
timeZone &:= str(aTime.timeZone div 60);
if aTime.timeZone rem 60 <> 0 then
timeZone &:= ":" & str(abs(aTime.timeZone) rem 60);
end if;
end if;
if aTime.daylightSavingTime then
timeZone &:= " (DST)";
end if;
end func;
const func string: str_yyyy_mm_dd (in time: aTime, in string: delimiter) is
return aTime.year lpad0 4 <& delimiter <&
aTime.month lpad0 2 <& delimiter <&
aTime.day lpad0 2;
const func string: str_yy_mm_dd (in time: aTime, in string: delimiter) is
return aTime.year rem 100 lpad0 2 <& delimiter <&
aTime.month lpad0 2 <& delimiter <&
aTime.day lpad0 2;
const func string: str_mm_dd_yyyy (in time: aTime, in string: delimiter) is
return aTime.month lpad0 2 <& delimiter <&
aTime.day lpad0 2 <& delimiter <&
aTime.year lpad0 4;
const func string: str_mm_dd_yy (in time: aTime, in string: delimiter) is
return aTime.month lpad0 2 <& delimiter <&
aTime.day lpad0 2 <& delimiter <&
aTime.year rem 100 lpad0 2;
const func string: str_dd_mm_yyyy (in time: aTime, in string: delimiter) is
return aTime.day lpad0 2 <& delimiter <&
aTime.month lpad0 2 <& delimiter <&
aTime.year lpad0 4;
const func string: str_dd_mm_yy (in time: aTime, in string: delimiter) is
return aTime.day lpad0 2 <& delimiter <&
aTime.month lpad0 2 <& delimiter <&
aTime.year rem 100 lpad0 2;
const func string: str_d_m_yyyy (in time: aTime, in string: delimiter) is
return str(aTime.day) <& delimiter <&
str(aTime.month) <& delimiter <&
aTime.year lpad0 4;
const func string: str_d_m_yy (in time: aTime, in string: delimiter) is
return str(aTime.day) <& delimiter <&
str(aTime.month) <& delimiter <&
aTime.year rem 100 lpad0 2;
const func string: str_hh_mm (in time: aTime, in string: delimiter) is
return aTime.hour lpad0 2 <& delimiter <&
aTime.minute lpad0 2;
const func string: str_hh_mm_ss (in time: aTime, in string: delimiter) is
return aTime.hour lpad0 2 <& delimiter <&
aTime.minute lpad0 2 <& delimiter <&
aTime.second lpad0 2;
const func string: strDateTime (in time: aTime) is
return aTime.year lpad0 4 <& "-" <&
aTime.month lpad0 2 <& "-" <&
aTime.day lpad0 2 <& " " <&
aTime.hour lpad0 2 <& ":" <&
aTime.minute lpad0 2 <& ":" <&
aTime.second lpad0 2;
const func string: str (in time: aTime) is
return strDate(aTime) <& " " <&
strTime(aTime) <& " " <&
strTimeZone(aTime);
const func string: literal (in time: aTime) is
return "time(" & literal(str(aTime)) & ")";
const func time: time (in var string: stri) is func
result
var time: aTime is time.value;
local
var boolean: checkForTimeZone is FALSE;
begin
if stri[1] = '-' then
stri := stri[2 ..];
aTime.year := -integer(getint(stri));
else
aTime.year := integer(getint(stri));
end if;
if stri <> "" and stri[1] = '-' then
stri := stri[2 ..];
aTime.month := integer(getint(stri));
if stri <> "" and stri[1] = '-' then
stri := stri[2 ..];
aTime.day := integer(getint(stri));
if stri <> "" and (stri[1] = ' ' or stri[1] = 'T') then
stri := stri[2 ..];
aTime.hour := integer(getint(stri));
if stri <> "" and stri[1] = ':' then
stri := stri[2 ..];
aTime.minute := integer(getint(stri));
if stri <> "" and stri[1] = ':' then
stri := stri[2 ..];
aTime.second := integer(getint(stri));
if stri <> "" and stri[1] = '.' then
stri := stri[2 ..];
aTime.micro_second := integer((getint(stri) & "00000X")[.. 6]);
end if;
end if;
end if;
checkForTimeZone := TRUE;
end if;
end if;
elsif stri <> "" and stri[1] = ':' then
stri := stri[2 ..];
aTime.hour := aTime.year;
aTime.year := 0;
aTime.minute := integer(getint(stri));
if stri <> "" and stri[1] = ':' then
stri := stri[2 ..];
aTime.second := integer(getint(stri));
if stri <> "" and stri[1] = '.' then
stri := stri[2 ..];
aTime.micro_second := integer((getint(stri) & "00000X")[.. 6]);
end if;
end if;
checkForTimeZone := TRUE;
end if;
if checkForTimeZone then
if startsWith(stri, " UTC") then
stri := stri[5 ..];
end if;
if stri <> "" then
if stri[1] = '+' then
stri := stri[2 ..];
aTime.timeZone := integer(getint(stri)) * 60;
if stri <> "" and stri[1] = ':' then
stri := stri[2 ..];
aTime.timeZone +:= integer(getint(stri));
end if;
elsif stri[1] = '-' then
stri := stri[2 ..];
aTime.timeZone := -integer(getint(stri)) * 60;
if stri <> "" and stri[1] = ':' then
stri := stri[2 ..];
aTime.timeZone -:= integer(getint(stri));
end if;
end if;
end if;
if stri = " (DST)" then
aTime.daylightSavingTime := TRUE;
stri := "";
end if;
end if;
if stri <> "" then
raise RANGE_ERROR;
end if;
if aTime.month < 1 or aTime.month > 12 or
aTime.day < 1 or aTime.day > daysInMonth(aTime.year, aTime.month) or
aTime.hour < 0 or aTime.hour > 23 or
aTime.minute < 0 or aTime.minute > 59 or
aTime.second < 0 or aTime.second > 59 then
raise RANGE_ERROR;
end if;
end func;
const func time: (attr time) parse (in string: stri) is
return time(stri);
enable_io(time);
const func boolean: (in time: aTime1) = (in time: aTime2) is
return
aTime1.year = aTime2.year and
aTime1.month = aTime2.month and
aTime1.day = aTime2.day and
aTime1.hour = aTime2.hour and
aTime1.minute = aTime2.minute and
aTime1.second = aTime2.second and
aTime1.micro_second = aTime2.micro_second;
const func boolean: (in time: aTime1) <> (in time: aTime2) is
return
aTime1.year <> aTime2.year or
aTime1.month <> aTime2.month or
aTime1.day <> aTime2.day or
aTime1.hour <> aTime2.hour or
aTime1.minute <> aTime2.minute or
aTime1.second <> aTime2.second or
aTime1.micro_second <> aTime2.micro_second;
const func boolean: (in time: aTime1) <= (in time: aTime2) is func
result
var boolean: isLessEqual is FALSE;
begin
if aTime1.year < aTime2.year then
isLessEqual := TRUE;
elsif aTime1.year = aTime2.year then
if aTime1.month < aTime2.month then
isLessEqual := TRUE;
elsif aTime1.month = aTime2.month then
if aTime1.day < aTime2.day then
isLessEqual := TRUE;
elsif aTime1.day = aTime2.day then
if aTime1.hour < aTime2.hour then
isLessEqual := TRUE;
elsif aTime1.hour = aTime2.hour then
if aTime1.minute < aTime2.minute then
isLessEqual := TRUE;
elsif aTime1.minute = aTime2.minute then
if aTime1.second < aTime2.second then
isLessEqual := TRUE;
elsif aTime1.second = aTime2.second then
if aTime1.micro_second <= aTime2.micro_second then
isLessEqual := TRUE;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end func;
const func boolean: (in time: aTime1) >= (in time: aTime2) is func
result
var boolean: isGreaterEqual is FALSE;
begin
if aTime1.year > aTime2.year then
isGreaterEqual := TRUE;
elsif aTime1.year = aTime2.year then
if aTime1.month > aTime2.month then
isGreaterEqual := TRUE;
elsif aTime1.month = aTime2.month then
if aTime1.day > aTime2.day then
isGreaterEqual := TRUE;
elsif aTime1.day = aTime2.day then
if aTime1.hour > aTime2.hour then
isGreaterEqual := TRUE;
elsif aTime1.hour = aTime2.hour then
if aTime1.minute > aTime2.minute then
isGreaterEqual := TRUE;
elsif aTime1.minute = aTime2.minute then
if aTime1.second > aTime2.second then
isGreaterEqual := TRUE;
elsif aTime1.second = aTime2.second then
if aTime1.micro_second >= aTime2.micro_second then
isGreaterEqual := TRUE;
end if;
end if;
end if;
end if;
end if;
end if;
end if;
end func;
const func boolean: (in time: aTime1) < (in time: aTime2) is
return not aTime1 >= aTime2;
const func boolean: (in time: aTime1) > (in time: aTime2) is
return not aTime1 <= aTime2;
const func integer: compare (in time: aTime1, in time: aTime2) is func
result
var integer: signumValue is 0;
begin
if aTime1.year < aTime2.year then
signumValue := -1;
elsif aTime1.year > aTime2.year then
signumValue := 1;
elsif aTime1.month < aTime2.month then
signumValue := -1;
elsif aTime1.month > aTime2.month then
signumValue := 1;
elsif aTime1.day < aTime2.day then
signumValue := -1;
elsif aTime1.day > aTime2.day then
signumValue := 1;
elsif aTime1.hour < aTime2.hour then
signumValue := -1;
elsif aTime1.hour > aTime2.hour then
signumValue := 1;
elsif aTime1.minute < aTime2.minute then
signumValue := -1;
elsif aTime1.minute > aTime2.minute then
signumValue := 1;
elsif aTime1.second < aTime2.second then
signumValue := -1;
elsif aTime1.second > aTime2.second then
signumValue := 1;
elsif aTime1.micro_second < aTime2.micro_second then
signumValue := -1;
elsif aTime1.micro_second > aTime2.micro_second then
signumValue := 1;
end if;
end func;
const func integer: hashCode (in time: aTime) is
return (aTime.year << 6) + (aTime.month << 5) + (aTime.day << 4) +
(aTime.hour << 3) + (aTime.minute << 2) + (aTime.second << 1) +
aTime.micro_second;
const func time: truncToSecond (in time: aTime) is func
result
var time: truncatedTime is time.value;
begin
truncatedTime := aTime;
truncatedTime.micro_second := 0;
end func;
const func time: truncToMinute (in time: aTime) is func
result
var time: truncatedTime is time.value;
begin
truncatedTime := aTime;
truncatedTime.second := 0;
truncatedTime.micro_second := 0;
end func;
const func time: truncToHour (in time: aTime) is func
result
var time: truncatedTime is time.value;
begin
truncatedTime := aTime;
truncatedTime.minute := 0;
truncatedTime.second := 0;
truncatedTime.micro_second := 0;
end func;
const func time: truncToDay (in time: aTime) is func
result
var time: truncatedTime is time.value;
begin
truncatedTime := aTime;
truncatedTime.hour := 0;
truncatedTime.minute := 0;
truncatedTime.second := 0;
truncatedTime.micro_second := 0;
end func;
const func time: truncToMonth (in time: aTime) is func
result
var time: truncatedTime is time.value;
begin
truncatedTime := aTime;
truncatedTime.day := 1;
truncatedTime.hour := 0;
truncatedTime.minute := 0;
truncatedTime.second := 0;
truncatedTime.micro_second := 0;
end func;
const func time: truncToYear (in time: aTime) is func
result
var time: truncatedTime is time.value;
begin
truncatedTime := aTime;
truncatedTime.month := 1;
truncatedTime.day := 1;
truncatedTime.hour := 0;
truncatedTime.minute := 0;
truncatedTime.second := 0;
truncatedTime.micro_second := 0;
end func;
const func integer: dayOfWeek (in time: aDate) is func
result
var integer: weekday is 0;
local
var integer: year is 0;
var integer: month is 0;
begin
year := aDate.year;
month := aDate.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 + aDate.day) mod 7);
end func;
const func integer: dayOfYear (in time: aDate) is func
result
var integer: dayOfYear is 0;
begin
if isLeapYear(aDate.year) then
dayOfYear := [](0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335)[aDate.month] + aDate.day;
else
dayOfYear := [](0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334)[aDate.month] + aDate.day;
end if;
end func;
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: aDate) is
return weekOfYear(aDate.year, dayOfYear(aDate));
const func integer: weekDateYear (in time: aDate) is func
result
var integer: weekDateYear is 0;
local
var integer: weekNumber is 0;
begin
weekNumber := weekOfYear(aDate.year, dayOfYear(aDate));
if weekNumber <= 0 then
weekDateYear := pred(aDate.year);
elsif weekNumber >= 53 and aDate.day >= 29 and
weekOfYear(succ(aDate.year), 31 - aDate.day) = 1 then
weekDateYear := succ(aDate.year);
else
weekDateYear := aDate.year;
end if;
end func;
const func integer: weekDateWeek (in time: aDate) is func
result
var integer: weekNumber is 0;
begin
weekNumber := weekOfYear(aDate.year, dayOfYear(aDate));
if weekNumber <= 0 then
weekNumber := weekOfYear(pred(aDate.year), 366);
elsif weekNumber >= 53 and aDate.day >= 29 and
weekOfYear(succ(aDate.year), 31 - aDate.day) = 1 then
weekNumber := 1;
end if;
end func;
const proc: NORMALIZE (inout time: tim) is func
local
var integer: monthLength is 0;
begin
tim.second +:= tim.micro_second mdiv 1000000;
tim.micro_second := tim.micro_second mod 1000000;
tim.minute +:= tim.second mdiv 60;
tim.second := tim.second mod 60;
tim.hour +:= tim.minute mdiv 60;
tim.minute := tim.minute mod 60;
tim.day +:= tim.hour mdiv 24;
tim.hour := tim.hour mod 24;
tim.year +:= pred(tim.month) mdiv 12;
tim.month := succ(pred(tim.month) mod 12);
monthLength := daysInMonth(tim.year, tim.month);
while tim.day > monthLength do
tim.day := tim.day - monthLength;
if tim.month < 12 then
incr(tim.month);
else
tim.month := 1;
incr(tim.year);
end if;
monthLength := daysInMonth(tim.year, tim.month);
end while;
while tim.day < 1 do
if tim.month > 1 then
decr(tim.month);
else
tim.month := 12;
decr(tim.year);
end if;
tim.day := tim.day + daysInMonth(tim.year, tim.month);
end while;
end func;
const func time: toUTC (in time: aTime) is func
result
var time: timeInUTC is time.value;
begin
timeInUTC := aTime;
timeInUTC.minute -:= aTime.timeZone;
timeInUTC.timeZone := 0;
timeInUTC.daylightSavingTime := FALSE;
NORMALIZE(timeInUTC);
end func;
const func time: toLocalTZ (in time: aTime) is func
result
var time: localTime is time.value;
begin
if aTime.timeZone = 0 then
localTime := aTime;
else
localTime := toUTC(aTime);
end if;
SET_LOCAL_TZ(localTime.year, localTime.month, localTime.day,
localTime.hour, localTime.minute, localTime.second,
localTime.timeZone, localTime.daylightSavingTime);
localTime.minute +:= localTime.timeZone;
NORMALIZE(localTime);
end func;
const func time: setLocalTZ (in time: aTime) is func
result
var time: localTime is time.value;
begin
localTime := aTime;
SET_LOCAL_TZ(localTime.year, localTime.month, localTime.day,
localTime.hour, localTime.minute, localTime.second,
localTime.timeZone, localTime.daylightSavingTime);
end func;
const func integer: julianDayNumber (in time: aDate) is func
result
var integer: julianDayNumber is 0;
local
var integer: year is 0;
var integer: month is 0;
begin
year := aDate.year;
month := aDate.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 +
aDate.day - 32075;
end func;
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;
const func integer: timestamp1970 (in time: aTime) is func
result
var integer: seconds is 0;
local
const integer: DAYS_FROM_0_TO_1970 is 719162;
var integer: yearBefore is 0;
begin
yearBefore := pred(aTime.year);
seconds := (((yearBefore * 365 +
yearBefore mdiv 4 -
yearBefore mdiv 100 +
yearBefore mdiv 400 -
DAYS_FROM_0_TO_1970 +
pred(dayOfYear(aTime))) * 24 +
aTime.hour) * 60 +
aTime.minute) * 60 +
aTime.second -
aTime.timeZone * 60;
end func;
const func time: timestamp1970ToTime (in integer: timestamp) is func
result
var time: aTime is time.value;
begin
FROM_TIMESTAMP(timestamp, aTime.year, aTime.month, aTime.day,
aTime.hour, aTime.minute, aTime.second,
aTime.micro_second, aTime.timeZone,
aTime.daylightSavingTime);
end func;
const func integer: timestamp1601 (in time: aTime) is func
result
var integer: nanosecs100 is 0;
local
const integer: SECONDS_1601_1970 is 11644473600;
const integer: NANOSECS100_TICK is 10000000;
begin
nanosecs100 := (timestamp1970(aTime) + SECONDS_1601_1970) * NANOSECS100_TICK +
aTime.micro_second * 10;
end func;
const func time: timestamp1601ToTime (in integer: timestamp) is func
result
var time: aTime is time.value;
local
const integer: SECONDS_1601_1970 is 11644473600;
const integer: NANOSECS100_TICK is 10000000;
var integer: utcSeconds is 0;
begin
utcSeconds := timestamp mdiv NANOSECS100_TICK - SECONDS_1601_1970;
aTime := timestamp1970ToTime(utcSeconds);
aTime.micro_second := (timestamp mod NANOSECS100_TICK) div 10
end func;
const func time: rand (in time: low, in time: high) is func
result
var time: randomTime is time.value;
local
var integer: timestampLow is 0;
var integer: timestampHigh is 0;
begin
timestampLow := timestamp1970(low);
timestampHigh := timestamp1970(high);
randomTime := low;
randomTime.second +:= rand(timestampLow, timestampHigh) - timestampLow;
if randomTime.second = low.second then
if randomTime.second = high.second then
randomTime.micro_second := rand(low.micro_second, high.micro_second);
else
randomTime.micro_second := rand(low.micro_second, 999999);
end if;
elsif randomTime.second = high.second then
randomTime.micro_second := rand(0, high.micro_second);
else
randomTime.micro_second := rand(0, 999999);
end if;
NORMALIZE(randomTime);
end func;
const func time: time (NOW) is func
result
var time: localTime is time.value;
begin
GET_TIME(localTime.year, localTime.month, localTime.day,
localTime.hour, localTime.minute, localTime.second,
localTime.micro_second, localTime.timeZone,
localTime.daylightSavingTime);
end func;
const func time: time (in integer: year, in integer: month, in integer: day,
in integer: hour, in integer: minute, in integer: second) is func
result
var time: utcTime is time.value;
begin
utcTime.year := year;
utcTime.month := month;
utcTime.day := day;
utcTime.hour := hour;
utcTime.minute := minute;
utcTime.second := second;
end func;
const func time: time (in integer: year, in integer: month, in integer: day,
in integer: hour, in integer: minute, in integer: second,
in integer: micro_second) is func
result
var time: utcTime is time.value;
begin
utcTime.year := year;
utcTime.month := month;
utcTime.day := day;
utcTime.hour := hour;
utcTime.minute := minute;
utcTime.second := second;
utcTime.micro_second := micro_second;
end func;
const func time: timeInTimeZone (in integer: year, in integer: month, in integer: day,
in integer: hour, in integer: minute, in integer: second, in integer: timeZone) is func
result
var time: aTime is time.value;
begin
aTime.year := year;
aTime.month := month;
aTime.day := day;
aTime.hour := hour;
aTime.minute := minute;
aTime.second := second;
aTime.timeZone := timeZone;
end func;
const func time: date (in integer: year, in integer: month, in integer: day) is func
result
var time: aDate is time.value;
begin
aDate.year := year;
aDate.month := month;
aDate.day := day;
end func;
const proc: await (ref time: aTime) is func
begin
AWAIT_TIME(aTime.year, aTime.month, aTime.day,
aTime.hour, aTime.minute, aTime.second,
aTime.micro_second, aTime.timeZone);
end func;
DECLARE_TERNARY(time);