| (* SysClock.mod implement the ISO SysClock specification. |
| |
| Copyright (C) 2009-2025 Free Software Foundation, Inc. |
| Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>. |
| |
| This file is part of GNU Modula-2. |
| |
| GNU Modula-2 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, or (at your option) |
| any later version. |
| |
| GNU Modula-2 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. |
| |
| Under Section 7 of GPL version 3, you are granted additional |
| permissions described in the GCC Runtime Library Exception, version |
| 3.1, as published by the Free Software Foundation. |
| |
| You should have received a copy of the GNU General Public License and |
| a copy of the GCC Runtime Library Exception along with this program; |
| see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
| <http://www.gnu.org/licenses/>. *) |
| |
| IMPLEMENTATION MODULE SysClock ; |
| |
| FROM wrapclock IMPORT timespec, timezone, isdst, InitTimespec, KillTimespec, |
| GetTimespec, SetTimespec, GetTimeRealtime, SetTimeRealtime ; |
| |
| FROM libc IMPORT printf ; |
| |
| IMPORT Args ; |
| |
| CONST |
| Debugging = FALSE ; |
| |
| VAR |
| canget, |
| canset, |
| known : BOOLEAN ; |
| |
| |
| (* |
| determineAccess - test to see whether we can get and set |
| the time. |
| *) |
| |
| PROCEDURE determineAccess ; |
| VAR |
| ts: timespec ; |
| BEGIN |
| IF NOT known |
| THEN |
| ts := InitTimespec () ; |
| canget := GetTimeRealtime (ts) = 0 ; |
| canset := canget AND (SetTimeRealtime (ts) = 0) ; |
| ts := KillTimespec (ts) ; |
| known := TRUE |
| END |
| END determineAccess ; |
| |
| |
| PROCEDURE CanGetClock () : BOOLEAN ; |
| (* Tests if the clock can be read *) |
| BEGIN |
| determineAccess ; |
| RETURN canget |
| END CanGetClock ; |
| |
| |
| PROCEDURE CanSetClock () : BOOLEAN ; |
| (* Tests if the clock can be set *) |
| BEGIN |
| determineAccess ; |
| RETURN canset |
| END CanSetClock ; |
| |
| |
| PROCEDURE IsValidDateTime (userData: DateTime) : BOOLEAN ; |
| (* Tests if the value of userData is a valid *) |
| BEGIN |
| WITH userData DO |
| CASE month OF |
| |
| 1: | |
| 2: IF ((year MOD 4=0) AND (year MOD 100#0)) OR (year MOD 400=0) |
| THEN |
| RETURN day<=29 |
| ELSE |
| RETURN day<=28 |
| END | |
| 3: | |
| 4: RETURN day<=30 | |
| 5: | |
| 6: RETURN day<=30 | |
| 7: | |
| 8: | |
| 9: RETURN day<=30 | |
| 10: | |
| 11: RETURN day<=30 | |
| 12: |
| |
| END |
| END ; |
| RETURN( TRUE ) |
| END IsValidDateTime ; |
| |
| |
| (* |
| DivMod - returns seconds MOD modulus. It also divides seconds by modulus. |
| *) |
| |
| PROCEDURE DivMod (VAR seconds: LONGCARD; modulus: LONGCARD) : LONGCARD ; |
| VAR |
| result: LONGCARD ; |
| BEGIN |
| result := seconds MOD modulus ; |
| seconds := seconds DIV modulus ; |
| RETURN result |
| END DivMod ; |
| |
| |
| (* |
| daysInYear - return the number of days in year up to month/day. |
| *) |
| |
| PROCEDURE daysInYear (day, month, year: LONGCARD) : LONGCARD ; |
| BEGIN |
| WHILE month > 1 DO |
| INC (day, daysInMonth (year, month)) ; |
| DEC (month) |
| END ; |
| RETURN day |
| END daysInYear ; |
| |
| |
| (* |
| ExtractDate - extracts the year, month, day from secs. days is the |
| total days since 1970. |
| *) |
| |
| PROCEDURE ExtractDate (days: LONGCARD; |
| VAR year: CARDINAL; VAR month: Month; VAR day: Day) ; |
| VAR |
| testMonth, |
| testYear : CARDINAL ; |
| monthOfDays, |
| yearOfDays : LONGCARD ; |
| BEGIN |
| testYear := 1970 ; |
| LOOP |
| yearOfDays := daysInYear (31, 12, testYear) ; |
| IF days < yearOfDays |
| THEN |
| year := testYear ; |
| testMonth := 1 ; |
| LOOP |
| monthOfDays := daysInMonth (year, testMonth) ; |
| IF days < monthOfDays |
| THEN |
| day := VAL (Day, days) + MIN (Day) ; |
| month := VAL (Month, testMonth) ; |
| RETURN |
| END ; |
| DEC (days, monthOfDays) ; |
| INC (testMonth) |
| END |
| ELSE |
| DEC (days, yearOfDays) ; |
| INC (testYear) |
| END |
| END |
| END ExtractDate ; |
| |
| |
| (* |
| EpochTime - assigns all fields of userData to 0 or FALSE. |
| *) |
| |
| PROCEDURE EpochTime (VAR userData: DateTime) ; |
| BEGIN |
| WITH userData DO |
| second := 0 ; |
| minute := 0 ; |
| hour := 0 ; |
| year := 0 ; |
| month := 0 ; |
| day := 0 ; |
| fractions := 0 ; |
| zone := 0 ; |
| summerTimeFlag := FALSE |
| END |
| END EpochTime ; |
| |
| |
| PROCEDURE GetClock (VAR userData: DateTime) ; |
| (* Assigns local date and time of the day to userData *) |
| VAR |
| ts : timespec ; |
| nano, sec: LONGCARD ; |
| offset : LONGINT ; |
| BEGIN |
| IF CanGetClock () |
| THEN |
| ts := InitTimespec () ; |
| IF GetTimeRealtime (ts) = 0 |
| THEN |
| IF GetTimespec (ts, sec, nano) = 1 |
| THEN |
| offset := timezone () ; |
| IF Debugging |
| THEN |
| printf ("getclock = %ld\n", sec) |
| END ; |
| sec := VAL (LONGINT, sec) + offset ; |
| IF Debugging |
| THEN |
| printf ("getclock = %ld\n", sec) |
| END ; |
| WITH userData DO |
| (* Here we keep dividing sec by max seconds, minutes, hours |
| to convert sec into total days since epoch. *) |
| second := VAL (Sec, DivMod (sec, MAX (Sec) + 1)) ; |
| minute := VAL (Min, DivMod (sec, MAX (Min) + 1)) ; |
| hour := VAL (Hour, DivMod (sec, MAX (Hour) + 1)) ; |
| ExtractDate (sec, year, month, day) ; |
| fractions := nano DIV ((1000 * 1000 * 1000) DIV maxSecondParts) ; |
| zone := - (offset DIV 60) ; |
| summerTimeFlag := (isdst () = 1) |
| END |
| ELSE |
| EpochTime (userData) |
| END |
| ELSE |
| EpochTime (userData) |
| END ; |
| ts := KillTimespec (ts) |
| END |
| END GetClock ; |
| |
| |
| (* |
| daysInMonth - returns how many days there are in a month. |
| *) |
| |
| PROCEDURE daysInMonth (year, month: CARDINAL) : LONGCARD ; |
| BEGIN |
| CASE month OF |
| |
| 1: | |
| 2: IF ((year MOD 4=0) AND (year MOD 100#0)) OR (year MOD 400=0) |
| THEN |
| RETURN 29 |
| ELSE |
| RETURN 28 |
| END | |
| 3: | |
| 4: RETURN 30 | |
| 5: | |
| 6: RETURN 30 | |
| 7: | |
| 8: | |
| 9: RETURN 30 | |
| 10: | |
| 11: RETURN 30 | |
| 12: | |
| |
| END ; |
| RETURN 31 |
| END daysInMonth ; |
| |
| |
| (* |
| totalYear - return the sum of all days prior to year from the epoch. |
| *) |
| |
| PROCEDURE totalYear (year: LONGCARD) : LONGCARD ; |
| VAR |
| lastYear, |
| result : LONGCARD ; |
| BEGIN |
| lastYear := 1970 ; |
| result := 0 ; |
| WHILE lastYear < year DO |
| INC (result, daysInYear (31, 12, lastYear)) ; |
| INC (lastYear) |
| END ; |
| RETURN result |
| END totalYear ; |
| |
| |
| (* |
| totalSeconds - returns the total seconds |
| *) |
| |
| PROCEDURE totalSeconds (second, minute, hour, |
| day, month, year: LONGCARD) : LONGCARD ; |
| VAR |
| result: LONGCARD ; |
| BEGIN |
| result := second |
| + minute * (MAX (Sec) + 1) |
| + hour * ((MAX (Min) + 1) * (MAX (Sec) + 1)) |
| + ((daysInYear (day, month, year) + totalYear (year)) |
| * ((MAX (Hour) + 1) * ((MAX (Min) + 1) * (MAX (Sec) + 1)))) ; |
| RETURN result |
| END totalSeconds ; |
| |
| |
| PROCEDURE SetClock (userData: DateTime); |
| VAR |
| ts : timespec ; |
| nano, sec: LONGCARD ; |
| offset : LONGINT ; |
| BEGIN |
| IF Debugging |
| THEN |
| sec := totalSeconds (userData.second, userData.minute, userData.hour, |
| VAL (CARDINAL, userData.day) - MIN (Day), |
| userData.month, userData.year) ; |
| printf ("setclock = %ld\n", sec); |
| offset := timezone () ; |
| sec := VAL (LONGINT, sec) - offset ; |
| printf ("setclock = %ld\n", sec); |
| END ; |
| IF CanSetClock () |
| THEN |
| ts := InitTimespec () ; |
| nano := VAL (LONGCARD, userData.fractions * 1000) ; |
| sec := totalSeconds (userData.second, userData.minute, userData.hour, |
| VAL (CARDINAL, userData.day) - MIN (Day), |
| userData.month, userData.year) ; |
| offset := timezone () ; |
| sec := VAL (LONGINT, sec) - offset ; |
| IF SetTimespec (ts, sec, nano) = 1 |
| THEN |
| IF SetTimeRealtime (ts) = 0 |
| THEN |
| END |
| END ; |
| ts := KillTimespec (ts) |
| END |
| END SetClock ; |
| |
| |
| BEGIN |
| known := FALSE ; |
| canset := FALSE ; |
| canget := FALSE |
| END SysClock. |