From ea271416b32adbf037e66d42b12f28db7e5594a4 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Mon, 2 Dec 2019 21:15:52 +0100 Subject: [PATCH 01/66] Add time utilities Move getTime from System to Internal package and introduce a new package Time with function getTime returning a record. --- .../ConvertModelica_from_3.2.3_to_4.0.0.mos | 2 + Modelica/Utilities/Internal.mo | 94 ++++++++++++++++++- Modelica/Utilities/System.mo | 79 ---------------- Modelica/Utilities/Time.mo | 65 +++++++++++++ Modelica/Utilities/Types.mo | 42 +++++++++ Modelica/Utilities/package.order | 1 + ModelicaTest/Utilities.mo | 40 ++++---- ModelicaTestConversion4.mo | 21 +++++ 8 files changed, 248 insertions(+), 96 deletions(-) create mode 100644 Modelica/Utilities/Time.mo diff --git a/Modelica/Resources/Scripts/Conversion/ConvertModelica_from_3.2.3_to_4.0.0.mos b/Modelica/Resources/Scripts/Conversion/ConvertModelica_from_3.2.3_to_4.0.0.mos index 02bde6c6bd..e8dc46aed3 100644 --- a/Modelica/Resources/Scripts/Conversion/ConvertModelica_from_3.2.3_to_4.0.0.mos +++ b/Modelica/Resources/Scripts/Conversion/ConvertModelica_from_3.2.3_to_4.0.0.mos @@ -13,6 +13,8 @@ convertClass("Modelica.Fluid.Icons.BaseClassLibrary", "Modelica.Icons.BasesPackage"); convertClass("Modelica.Icons.TypeComplex", "Complex"); +convertClass("Modelica.Utilities.System.getTime", + "Modelica.Utilities.Internal.Time.getTime"); convertClass("Modelica.Mechanics.MultiBody.Sensors.Internal.ZeroForceAndTorque", "Modelica.Mechanics.MultiBody.Forces.Internal.ZeroForceAndTorque"); convertClass("Modelica.Mechanics.Rotational.Interfaces.PartialElementaryOneFlangeAndSupport", diff --git a/Modelica/Utilities/Internal.mo b/Modelica/Utilities/Internal.mo index d3ad75276a..6dc372aec1 100644 --- a/Modelica/Utilities/Internal.mo +++ b/Modelica/Utilities/Internal.mo @@ -227,7 +227,7 @@ Package Internal.FileSystem is an internal package that contain low level functions as interface to the file system. These functions should not be called directly in a scripting environment since more convenient functions are provided -in packages Files and Systems. +in packages Files and System.

Note, the functions in this package are direct interfaces to @@ -240,4 +240,96 @@ especially if the operating system supports Unicode characters.

")); end FileSystem; + + package Time "Internal package with external functions as interface to date and time" + extends Modelica.Icons.InternalPackage; + impure function getTime "Retrieve the local time (in the local time zone)" + extends Modelica.Icons.Function; + output Integer ms "Millisecond"; + output Integer sec "Second"; + output Integer min "Minute"; + output Integer hour "Hour"; + output Integer day "Day"; + output Integer mon "Month"; + output Integer year "Year"; + external "C" ModelicaInternal_getTime(ms,sec,min,hour,day,mon,year) + annotation(Library="ModelicaExternalC"); + annotation (Documentation(info=" +

Syntax

+
+(ms, sec, min, hour, day, mon, year) = Internal.Time.getTime();
+
+

Description

+

+Returns the local time at the time instant this function was called. +All returned values are of type Integer and have the following meaning: +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ArgumentRangeDescription
ms 0 .. 999Milli-seconds after seconds
sec 0 .. 59Seconds after minute
min 0 .. 59Minutes after hour
hour 0 .. 23Hours after midnight
day 1 .. 31Day of month
mon 1 .. 12Current month
year ≥ 2015Current year
+
+ +

Example

+
+(ms, sec, min, hour, mon, year) = getTime()   // = (281, 30, 13, 10, 15, 2, 2015)
+                                              // Feb. 15, 2015 at 10:13 after 30.281 s
+
+

Note

+

This function is impure!

+", revisions=" + + + + + + +
Date Description
June 22, 2015 + + +
+ + + Initial version implemented by + A. Klöckner, F. v.d. Linden, D. Zimmer, M. Otter.
+ DLR Institute of System Dynamics and Control +
+
+")); + end getTime; + annotation ( +Documentation(info=" +

+Package Internal.Time is an internal package that contains +low level functions as interface to date and time. +These functions should not be called directly in a scripting +environment since more convenient functions are provided +in package Time. +

+")); + end Time; end Internal; diff --git a/Modelica/Utilities/System.mo b/Modelica/Utilities/System.mo index 91a02c5a50..8f2d0c6a1c 100644 --- a/Modelica/Utilities/System.mo +++ b/Modelica/Utilities/System.mo @@ -49,85 +49,6 @@ external "C" ModelicaInternal_setenv(name, content, convertFromSlash) annotation ")); end setEnvironmentVariable; - impure function getTime "Retrieve the local time (in the local time zone)" - extends Modelica.Icons.Function; - output Integer ms "Millisecond"; - output Integer sec "Second"; - output Integer min "Minute"; - output Integer hour "Hour"; - output Integer day "Day"; - output Integer mon "Month"; - output Integer year "Year"; - external "C" ModelicaInternal_getTime(ms,sec,min,hour,day,mon,year) - annotation(IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaInternal.h\"", Library="ModelicaExternalC"); - annotation (Documentation(info=" -

Syntax

-
-(ms, sec, min, hour, day, mon, year) = System.getTime();
-
-

Description

-

-Returns the local time at the time instant this function was called. -All returned values are of type Integer and have the following meaning: -

- -
- - - - - - - - - - - - - - - - - - - - - - - - - -
ArgumentRangeDescription
ms 0 .. 999Milli-seconds after seconds
sec 0 .. 59Seconds after minute
min 0 .. 59Minutes after hour
hour 0 .. 23Hours after midnight
day 1 .. 31Day of month
mon 1 .. 12Current month
year ≥ 2015Current year
-
- -

Example

-
-(ms, sec, min, hour, day, mon, year) = getTime()   // = (281, 30, 13, 10, 15, 2, 2015)
-                                              // Feb. 15, 2015 at 10:13 after 30.281 s
-
-

Note

-

This function is impure!

-", revisions=" - - - - - - -
Date Description
June 22, 2015 - - -
- \"DLR - - Initial version implemented by - A. Klöckner, F. v.d. Linden, D. Zimmer, M. Otter.
- DLR Institute of System Dynamics and Control -
-
-")); - end getTime; - impure function getPid "Retrieve the current process id" extends Modelica.Icons.Function; output Integer pid "Process ID"; diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo new file mode 100644 index 0000000000..161250ae13 --- /dev/null +++ b/Modelica/Utilities/Time.mo @@ -0,0 +1,65 @@ +within Modelica.Utilities; +package Time "Functions to work with date and time" + extends Modelica.Icons.FunctionsPackage; + + impure function getTime "Retrieve the current time (in the local time zone)" + extends Modelica.Icons.Function; + output Types.TimeType now "Current time"; + algorithm + (now.ms, now.sec, now.min, now.hour, now.day, now.mon, now.year) := Internal.Time.getTime(); + annotation (Documentation(info=" +

Syntax

+
+(ms, sec, min, hour, day, mon, year) = System.getTime();
+
+

Description

+

+Returns the local time at the time instant this function was called. +All returned values are of type Integer and have the following meaning: +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ArgumentRangeDescription
ms 0 .. 999Milli-seconds after seconds
sec 0 .. 59Seconds after minute
min 0 .. 59Minutes after hour
hour 0 .. 23Hours after midnight
day 1 .. 31Day of month
mon 1 .. 12Current month
year ≥ 2015Current year
+
+ +

Example

+
+(ms, sec, min, hour, day, mon, year) = getTime()   // = (281, 30, 13, 10, 15, 2, 2015)
+                                              // Feb. 15, 2015 at 10:13 after 30.281 s
+
+

Note

+

This function is impure!

+")); + end getTime; + annotation ( +Documentation(info=" +

+This package contains functions to work with date and time. +

+")); +end Time; diff --git a/Modelica/Utilities/Types.mo b/Modelica/Utilities/Types.mo index 286f10a9ca..f020857354 100644 --- a/Modelica/Utilities/Types.mo +++ b/Modelica/Utilities/Types.mo @@ -35,6 +35,48 @@ package Types "Type definitions used in package Modelica.Utilities" ")); end TokenValue; + + record TimeType "Record containing date and time components" + extends Modelica.Icons.Record; + Integer ms(min=0, max=999) "Millisecond" annotation(absoluteValue=true); + Integer sec(min=0, max=61) "Second" annotation(absoluteValue=true); + Integer min(min=0, max=59) "Minute" annotation(absoluteValue=true); + Integer hour(min=0, max=23) "Hour" annotation(absoluteValue=true); + Integer day(min=1, max=31) "Day" annotation(absoluteValue=true); + Integer mon(min=1, max=12) "Month" annotation(absoluteValue=true); + Integer year "Year" annotation(absoluteValue=true); + annotation (Documentation(info=" +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ArgumentRangeDescription
ms 0 .. 999Milliseconds after second
sec 0 .. 61Seconds after minute
min 0 .. 59Minutes after hour
hour 0 .. 23Hours after midnight
day 1 .. 31Day of month
mon 1 .. 12Month
year Year
+
+")); + end TimeType; + annotation (Documentation(info="

This package contains type definitions used in Modelica.Utilities. diff --git a/Modelica/Utilities/package.order b/Modelica/Utilities/package.order index 7ec8cdc903..fdb1b9047b 100644 --- a/Modelica/Utilities/package.order +++ b/Modelica/Utilities/package.order @@ -4,5 +4,6 @@ Files Streams Strings System +Time Types Internal diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index 88d104cdf9..2daf8e73ff 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -379,33 +379,41 @@ extends Modelica.Icons.ExamplesPackage; "Filename where the log is stored"; output Boolean ok; protected - Integer ms; - Integer sec; - Integer min; - Integer hour; - Integer day; - Integer mon; - Integer year; Integer pid; algorithm Streams.print("... Test of Modelica.Utilities.System"); Streams.print("... Test of Modelica.Utilities.System", logFile); - (ms,sec,min,hour,day,mon,year) :=Modelica.Utilities.System.getTime(); - Streams.print(" ms = " + String(ms)); - Streams.print(" sec = " + String(sec)); - Streams.print(" min = " + String(min)); - Streams.print(" hour = " + String(hour)); - Streams.print(" day = " + String(day)); - Streams.print(" mon = " + String(mon)); - Streams.print(" year = " + String(year)); - pid :=Modelica.Utilities.System.getPid(); Streams.print(" pid = " + String(pid)); ok := true; end System; + function Time "Test functions of Modelica.Utilities.Time" + import Modelica.Utilities.Streams; + extends Modelica.Icons.Function; + input String logFile="ModelicaTestLog.txt" + "Filename where the log is stored"; + output Boolean ok; + protected + Modelica.Utilities.Types.TimeType now; + algorithm + Streams.print("... Test of Modelica.Utilities.Time"); + Streams.print("... Test of Modelica.Utilities.Time", logFile); + + now := Modelica.Utilities.Time.getTime(); + Streams.print(" ms = " + String(now.ms)); + Streams.print(" sec = " + String(now.sec)); + Streams.print(" min = " + String(now.min)); + Streams.print(" hour = " + String(now.hour)); + Streams.print(" day = " + String(now.day)); + Streams.print(" mon = " + String(now.mon)); + Streams.print(" year = " + String(now.year)); + + ok := true; + end Time; + function Internal "Test functions of Modelica.Utilities.Internal" extends Modelica.Icons.Function; import Modelica.Utilities.Internal.FileSystem; diff --git a/ModelicaTestConversion4.mo b/ModelicaTestConversion4.mo index b9da936dc1..03441928fe 100644 --- a/ModelicaTestConversion4.mo +++ b/ModelicaTestConversion4.mo @@ -3069,6 +3069,27 @@ Conversion test for #3247. +

+")); + end Issue3247; + end Utilities; + package Icons extends Modelica.Icons.ExamplesPackage; model Issue340 "Conversion test for #340" From ea7edc326ac29bf3844e10c75d9fc6323cce4345 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Mon, 2 Dec 2019 22:39:53 +0100 Subject: [PATCH 02/66] Add leap year utility functions --- Modelica/Utilities/Time.mo | 67 ++++++++++++++++++++++++++++++++++++++ ModelicaTest/Utilities.mo | 18 +++++++++- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 161250ae13..ca6675f920 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -56,6 +56,73 @@ All returned values are of type Integer and have the following meaning:

This function is impure!

")); end getTime; + + function isLeapYear "Check if a year is a leap year" + extends Modelica.Icons.Function; + input Integer year "Year"; + output Boolean isLeap "= true, if year is a leap year"; + algorithm + isLeap := (mod(year, 4) == 0 and mod(year, 100) <> 0) or mod(year, 400) == 0; + annotation (Documentation(info=" +

Syntax

+
+isLeap = Time.isLeapYear(year);
+
+

Description

+

+Checks if a given year is a leap year. +

+")); + end isLeapYear; + + function leapDays "Return the number of leap days in range" + extends Modelica.Icons.Function; + input Integer year1 "First year"; + input Integer year2 "Second year"; + output Integer days "Number of leap days in range [year1, year2 - 1]"; + protected + Integer d "Length of range"; + Integer y1; + Integer y2; + algorithm + d := year2 - year1; + if d == 0 then + days := 0; + return; + elseif d < 0 then + y1 := year2; + y2 := year1; + d := -d; + else + y1 := year1; + y2 := year2; + end if; + days := div(d - 1, 4) - div(d - 1, 100) + div(d - 1, 400); + if isLeapYear(y1) or isLeapYear(y2 - 1) then + days := days + 1; + end if; + if year1 > year2 then + days := -days; + end if; + annotation (Documentation(info=" +

Syntax

+
+days = Time.leapDays(year1, year2);
+
+

Description

+

+Returns the number of leap days in the range [year1, year2 - 1]. +In case of year1 > year2, the result is the negative number of leap days in range [year2, year1 - 1]. +

+

Example

+
+days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019]
+                            // for the years 2000, 2004, 2008, 2012 and 2016
+                            // excluding the second year 2020
+
+")); + end leapDays; + annotation ( Documentation(info="

diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index 2daf8e73ff..aaa34011fb 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -384,7 +384,7 @@ extends Modelica.Icons.ExamplesPackage; Streams.print("... Test of Modelica.Utilities.System"); Streams.print("... Test of Modelica.Utilities.System", logFile); - pid :=Modelica.Utilities.System.getPid(); + pid := Modelica.Utilities.System.getPid(); Streams.print(" pid = " + String(pid)); ok := true; @@ -411,6 +411,20 @@ extends Modelica.Icons.ExamplesPackage; Streams.print(" mon = " + String(now.mon)); Streams.print(" year = " + String(now.year)); + assert(not Modelica.Utilities.Time.isLeapYear(1900), "Time.isLeapYear failed"); + assert(Modelica.Utilities.Time.isLeapYear(2000), "Time.isLeapYear failed"); + assert(not Modelica.Utilities.Time.isLeapYear(2019), "Time.isLeapYear failed"); + assert(Modelica.Utilities.Time.isLeapYear(2020), "Time.isLeapYear failed"); + + assert(0 == Modelica.Utilities.Time.leapDays(2000, 2000), "Time.leapDays failed"); + assert(5 == Modelica.Utilities.Time.leapDays(2000, 2020), "Time.leapDays failed"); + assert(-5 == Modelica.Utilities.Time.leapDays(2020, 2000), "Time.leapDays failed"); + assert(5 == Modelica.Utilities.Time.leapDays(-2020, -2000), "Time.leapDays failed"); + assert(98 == Modelica.Utilities.Time.leapDays(1600, 2001), "Time.leapDays failed"); + assert(97 == Modelica.Utilities.Time.leapDays(1601, 2001), "Time.leapDays failed"); + assert(97 == Modelica.Utilities.Time.leapDays(1600, 2000), "Time.leapDays failed"); + assert(96 == Modelica.Utilities.Time.leapDays(1601, 2000), "Time.leapDays failed"); + ok := true; end Time; @@ -517,6 +531,8 @@ extends Modelica.Icons.ExamplesPackage; result := ModelicaTest.Utilities.Streams(logFile); result := ModelicaTest.Utilities.Files(logFile); result := ModelicaTest.Utilities.Internal(logFile); + result := ModelicaTest.Utilities.System(logFile); + result := ModelicaTest.Utilities.Time(logFile); ok := true; end testAll; From ca5c100a4e63e827bf950e26905fb4ddfd2ce902 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Thu, 5 Dec 2019 20:45:08 +0100 Subject: [PATCH 03/66] Rename TimeType components --- Modelica/Utilities/Time.mo | 2 +- Modelica/Utilities/Types.mo | 8 ++++---- ModelicaTest/Utilities.mo | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index ca6675f920..9a814400da 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -6,7 +6,7 @@ package Time "Functions to work with date and time" extends Modelica.Icons.Function; output Types.TimeType now "Current time"; algorithm - (now.ms, now.sec, now.min, now.hour, now.day, now.mon, now.year) := Internal.Time.getTime(); + (now.millisecond, now.second, now.minute, now.hour, now.day, now.month, now.year) := Internal.Time.getTime(); annotation (Documentation(info="

Syntax

diff --git a/Modelica/Utilities/Types.mo b/Modelica/Utilities/Types.mo
index f020857354..6616447632 100644
--- a/Modelica/Utilities/Types.mo
+++ b/Modelica/Utilities/Types.mo
@@ -38,12 +38,12 @@ package Types "Type definitions used in package Modelica.Utilities"
 
   record TimeType "Record containing date and time components"
     extends Modelica.Icons.Record;
-    Integer ms(min=0, max=999) "Millisecond" annotation(absoluteValue=true);
-    Integer sec(min=0, max=61) "Second" annotation(absoluteValue=true);
-    Integer min(min=0, max=59) "Minute" annotation(absoluteValue=true);
+    Integer millisecond(min=0, max=999) "Millisecond" annotation(absoluteValue=true);
+    Integer second(min=0, max=61) "Second" annotation(absoluteValue=true);
+    Integer minute(min=0, max=59) "Minute" annotation(absoluteValue=true);
     Integer hour(min=0, max=23) "Hour" annotation(absoluteValue=true);
     Integer day(min=1, max=31) "Day" annotation(absoluteValue=true);
-    Integer mon(min=1, max=12) "Month" annotation(absoluteValue=true);
+    Integer month(min=1, max=12) "Month" annotation(absoluteValue=true);
     Integer year "Year" annotation(absoluteValue=true);
     annotation (Documentation(info="
 
diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index aaa34011fb..39ce52c99c 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -403,12 +403,12 @@ extends Modelica.Icons.ExamplesPackage; Streams.print("... Test of Modelica.Utilities.Time", logFile); now := Modelica.Utilities.Time.getTime(); - Streams.print(" ms = " + String(now.ms)); - Streams.print(" sec = " + String(now.sec)); - Streams.print(" min = " + String(now.min)); + Streams.print(" ms = " + String(now.millisecond)); + Streams.print(" sec = " + String(now.second)); + Streams.print(" min = " + String(now.minute)); Streams.print(" hour = " + String(now.hour)); Streams.print(" day = " + String(now.day)); - Streams.print(" mon = " + String(now.mon)); + Streams.print(" mon = " + String(now.month)); Streams.print(" year = " + String(now.year)); assert(not Modelica.Utilities.Time.isLeapYear(1900), "Time.isLeapYear failed"); From c97323e7152ee493474a7f9d7aef41c49cb262fa Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Fri, 6 Dec 2019 13:47:04 +0100 Subject: [PATCH 04/66] Add day of week computation --- Modelica/Utilities/Internal.mo | 59 ++++++++++++++++++++++++++++++++++ Modelica/Utilities/Time.mo | 48 +++++++++++++++++++++++++++ ModelicaTest/Utilities.mo | 8 +++++ 3 files changed, 115 insertions(+) diff --git a/Modelica/Utilities/Internal.mo b/Modelica/Utilities/Internal.mo index 6dc372aec1..c766be1b9b 100644 --- a/Modelica/Utilities/Internal.mo +++ b/Modelica/Utilities/Internal.mo @@ -321,6 +321,65 @@ All returned values are of type Integer and have the following meaning: ")); end getTime; + + function dayOfWeek "Return day of week for given date" + extends Modelica.Icons.Function; + input Integer year "Year"; + input Integer mon=1 "Month"; + input Integer day=1 "Day of month"; + output Integer dow "Day of week: 0 = Sunday, ..., 6 = Saturday"; + protected + constant Integer t[:] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; + Integer y = year; + algorithm + assert(mon >= 1 and mon <= 12, "Month is out of range."); + if mon < 3 then + y := y - 1; + end if; + dow := mod(y + div(y, 4) - div(y, 100) + div(y, 400) + t[mon] + day, 7); + annotation (Documentation(info=" +

Syntax

+
+dow = Internal.Time.dayOfWeek(year, mon, day);
+
+

Description

+

+

+Returns the day of the week for a given date using Tomohiko Sakamoto's algorithm. +The returned Integer number of dow has the following meaning: +

+ +
+ + + + + + + + + + + + + + + + + +
Day of weekNumber
Sunday 0
Monday 1
Tuesday 2
Wednesday 3
Thursday 4
Friday 5
Saturday 6
+
+ +

Example

+
+dow = dayOfWeek(2019, 12, 6) // = 5
+                             // Dec. 06, 2019 (Saint Nicholas Day) is a Friday
+dow = dayOfWeek(2020)        // = 3
+                             // Jan. 01, 2020 (New Year's Day) is a Wednesday
+
+")); + end dayOfWeek; + annotation ( Documentation(info="

diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 9a814400da..bfb1393f90 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -57,6 +57,54 @@ All returned values are of type Integer and have the following meaning: ")); end getTime; + function dayOfWeek "Return day of week for given date" + extends Modelica.Icons.Function; + input Types.TimeType timeIn "Date"; + output Integer dow "Day of week: 0 = Sunday, ..., 6 = Saturday"; + algorithm + dow := Internal.Time.dayOfWeek(timeIn.year, timeIn.month, timeIn.day); + annotation (Documentation(info=" +

Syntax

+
+dow = Time.dayOfWeek(timeIn);
+
+

Description

+

+Returns the day of the week for a given date using Tomohiko Sakamoto's algorithm. +The returned Integer number of dow has the following meaning: +

+ +
+ + + + + + + + + + + + + + + + + +
Day of weekNumber
Sunday 0
Monday 1
Tuesday 2
Wednesday 3
Thursday 4
Friday 5
Saturday 6
+
+ +

Example

+
+now = getTime()      // = Modelica.Utilities.Types.TimeType(281, 30, 13, 10, 6, 12, 2019)
+                     // Dec. 06, 2019 at 10:13 after 30.281 s
+dow = dayOfWeek(now) // = 5
+                     // Dec. 06, 2019 (Saint Nicholas Day) is a Friday
+
+")); + end dayOfWeek; + function isLeapYear "Check if a year is a leap year" extends Modelica.Icons.Function; input Integer year "Year"; diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index 39ce52c99c..6acd39ad50 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -398,6 +398,8 @@ extends Modelica.Icons.ExamplesPackage; output Boolean ok; protected Modelica.Utilities.Types.TimeType now; + Integer dow "Day of week"; + constant String weekDays[:] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; algorithm Streams.print("... Test of Modelica.Utilities.Time"); Streams.print("... Test of Modelica.Utilities.Time", logFile); @@ -410,6 +412,12 @@ extends Modelica.Icons.ExamplesPackage; Streams.print(" day = " + String(now.day)); Streams.print(" mon = " + String(now.month)); Streams.print(" year = " + String(now.year)); + dow := Modelica.Utilities.Time.dayOfWeek(now); + Streams.print(" dow = " + weekDays[dow + 1]); + + dow := Modelica.Utilities.Time.dayOfWeek( + Modelica.Utilities.Types.TimeType(year=2019, month=12, day=6, hour=12, minute=0, second=0, millisecond=0)); + assert(5 == dow, "Time.dayOfWeek failed"); assert(not Modelica.Utilities.Time.isLeapYear(1900), "Time.isLeapYear failed"); assert(Modelica.Utilities.Time.isLeapYear(2000), "Time.isLeapYear failed"); From 39c293063cf7f00f8385ca919e2c8915c85ea77f Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Tue, 10 Dec 2019 22:07:28 +0100 Subject: [PATCH 05/66] Let dayOfWeek return Integer in range [1, 7] --- Modelica/Utilities/Internal.mo | 43 +++++++++++++++++++--------------- Modelica/Utilities/Time.mo | 11 +++++---- ModelicaTest/Utilities.mo | 9 ++++--- 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/Modelica/Utilities/Internal.mo b/Modelica/Utilities/Internal.mo index c766be1b9b..75ffb0c8db 100644 --- a/Modelica/Utilities/Internal.mo +++ b/Modelica/Utilities/Internal.mo @@ -322,21 +322,25 @@ All returned values are of type Integer and have the following meaning: ")); end getTime; - function dayOfWeek "Return day of week for given date" - extends Modelica.Icons.Function; - input Integer year "Year"; - input Integer mon=1 "Month"; - input Integer day=1 "Day of month"; - output Integer dow "Day of week: 0 = Sunday, ..., 6 = Saturday"; - protected - constant Integer t[:] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; - Integer y = year; - algorithm - assert(mon >= 1 and mon <= 12, "Month is out of range."); - if mon < 3 then - y := y - 1; - end if; - dow := mod(y + div(y, 4) - div(y, 100) + div(y, 400) + t[mon] + day, 7); + function dayOfWeek "Return day of week for given date" + extends Modelica.Icons.Function; + input Integer year "Year"; + input Integer mon=1 "Month"; + input Integer day=1 "Day of month"; + output Integer dow(min=1, max=7) "Day of week: 1 = Monday, ..., 6 = Saturday, 7 = Sunday"; + protected + constant Integer t[:] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; + Integer y = year; + algorithm + assert(mon >= 1 and mon <= 12, "Month is out of range."); + if mon < 3 then + y := y - 1; + end if; + dow := mod(y + div(y, 4) - div(y, 100) + div(y, 400) + t[mon] + day, 7); + // One-based indexing: Sunday is 7 + if dow == 0 then + dow := 7; + end if; annotation (Documentation(info="

Syntax

@@ -346,7 +350,7 @@ dow = Internal.Time.dayOfWeek(year, mon, day);
 

Returns the day of the week for a given date using Tomohiko Sakamoto's algorithm. -The returned Integer number of dow has the following meaning: +The returned Integer number of dow has the following meaning:

@@ -354,8 +358,6 @@ The returned Integer number of dow has the following meaning: Day of week Number -Sunday 0 - Monday 1 Tuesday 2 @@ -367,6 +369,9 @@ The returned Integer number of dow has the following meaning: Friday 5 Saturday 6 + +Sunday 7 +
@@ -378,7 +383,7 @@ dow = dayOfWeek(2020) // = 3 // Jan. 01, 2020 (New Year's Day) is a Wednesday
")); - end dayOfWeek; + end dayOfWeek; annotation ( Documentation(info=" diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index bfb1393f90..e7430d6883 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -60,7 +60,7 @@ All returned values are of type Integer and have the following meaning: function dayOfWeek "Return day of week for given date" extends Modelica.Icons.Function; input Types.TimeType timeIn "Date"; - output Integer dow "Day of week: 0 = Sunday, ..., 6 = Saturday"; + output Integer dow(min=1, max=7) "Day of week: 1 = Monday, ..., 6 = Saturday, 7 = Sunday"; algorithm dow := Internal.Time.dayOfWeek(timeIn.year, timeIn.month, timeIn.day); annotation (Documentation(info=" @@ -71,7 +71,7 @@ dow = Time.dayOfWeek(timeIn);

Description

Returns the day of the week for a given date using Tomohiko Sakamoto's algorithm. -The returned Integer number of dow has the following meaning: +The returned Integer number of dow has the following meaning:

@@ -79,8 +79,6 @@ The returned Integer number of dow has the following meaning: Day of week Number -Sunday 0 - Monday 1 Tuesday 2 @@ -92,6 +90,9 @@ The returned Integer number of dow has the following meaning: Friday 5 Saturday 6 + +Sunday 7 +
@@ -171,6 +172,8 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] ")); end leapDays; + final constant String weekDays[7] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"} "Array of week days"; + annotation ( Documentation(info="

diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index 6acd39ad50..544296080b 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -398,8 +398,7 @@ extends Modelica.Icons.ExamplesPackage; output Boolean ok; protected Modelica.Utilities.Types.TimeType now; - Integer dow "Day of week"; - constant String weekDays[:] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; + Integer dow(min=1, max=7) "Day of week"; algorithm Streams.print("... Test of Modelica.Utilities.Time"); Streams.print("... Test of Modelica.Utilities.Time", logFile); @@ -413,11 +412,11 @@ extends Modelica.Icons.ExamplesPackage; Streams.print(" mon = " + String(now.month)); Streams.print(" year = " + String(now.year)); dow := Modelica.Utilities.Time.dayOfWeek(now); - Streams.print(" dow = " + weekDays[dow + 1]); + Streams.print(" dow = " + Modelica.Utilities.Time.weekDays[dow]); dow := Modelica.Utilities.Time.dayOfWeek( - Modelica.Utilities.Types.TimeType(year=2019, month=12, day=6, hour=12, minute=0, second=0, millisecond=0)); - assert(5 == dow, "Time.dayOfWeek failed"); + Modelica.Utilities.Types.TimeType(year=2019, month=12, day=8, hour=12, minute=0, second=0, millisecond=0)); + assert(7 == dow, "Time.dayOfWeek failed"); assert(not Modelica.Utilities.Time.isLeapYear(1900), "Time.isLeapYear failed"); assert(Modelica.Utilities.Time.isLeapYear(2000), "Time.isLeapYear failed"); From f0a98710663b03d96d4b6ce268a76f0449e6e792 Mon Sep 17 00:00:00 2001 From: mkr7 Date: Wed, 8 Apr 2020 11:35:36 +0200 Subject: [PATCH 06/66] Add function to round to nearest integer --- Modelica/Math/nearestInteger.mo | 50 +++++++++++++++++++++++++++++++++ Modelica/Math/package.order | 1 + 2 files changed, 51 insertions(+) create mode 100644 Modelica/Math/nearestInteger.mo diff --git a/Modelica/Math/nearestInteger.mo b/Modelica/Math/nearestInteger.mo new file mode 100644 index 0000000000..6c5050c9e6 --- /dev/null +++ b/Modelica/Math/nearestInteger.mo @@ -0,0 +1,50 @@ +within Modelica.Math; +function nearestInteger "Convert real number to nearest integer value" + extends Modelica.Icons.Function; + + input Real r "Real number to convert to integer"; + output Integer i "Integer value, which is closest to the given real number"; + +algorithm + i :=if (r > 0) then integer(floor(r + 0.5)) else integer(ceil(r - 0.5)); + + annotation (Documentation(info=" + +

Syntax

+
+Math.nearestInteger(r);
+
+ +

Description

+

+ The input value \"r\" of type Real is converted to the closest Integer value \"i\", + using the round half away from zero rule with the equation: +

+
+i = integer( floor( r + 0.5 ) )  for  r > 0;
+i = integer(  ceil( r - 0.5 ) )  for  r < 0;
+
+ +

Example

+
+import Modelica.Math;
+Math.nearestInteger(0.4);                     // = 0
+Math.nearestInteger(0.5);                     // = 1
+Math.nearestInteger(-0.4);                    // = 0
+Math.nearestInteger(-0.5);                    // = -1
+Math.nearestInteger(0.3999999999999999+0.1);  // = 0
+Math.nearestInteger(1.39999999999999999+0.1); // = 1 (errorneous border case, see note below)
+
+ +

Note

+ +

+ This function does the same conversion as the block + RealToInteger. +

+

+ The underlying equation is simple, but not always correct. Due to floating point arithmetic some border cases + are not converted correct, like shown in the example above. +

+")); +end nearestInteger; diff --git a/Modelica/Math/package.order b/Modelica/Math/package.order index c1f671ddc0..2e18c9335a 100644 --- a/Modelica/Math/package.order +++ b/Modelica/Math/package.order @@ -10,6 +10,7 @@ FastFourierTransform Icons isEqual isPowerOf2 +nearestInteger sin cos tan From 7495e30f9f494c0f1ff1d9ef71f5a9a4138f0885 Mon Sep 17 00:00:00 2001 From: mkr7 Date: Thu, 9 Apr 2020 13:15:47 +0200 Subject: [PATCH 07/66] Add arrays for month and day names (full and abbreviated) --- Modelica/Utilities/Time.mo | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index e7430d6883..10c90acb7e 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -173,6 +173,9 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] end leapDays; final constant String weekDays[7] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"} "Array of week days"; + final constant String shortWeekDays[7] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"} "Array of abbreviated week days"; + final constant String months[12] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} "Array of month names"; + final constant String shortMonths[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"} "Array of abbreviated month names"; annotation ( Documentation(info=" From b5cd65d145167dd4b5a328a51a99595d49eb36e1 Mon Sep 17 00:00:00 2001 From: mkr7 Date: Tue, 7 Apr 2020 17:12:55 +0200 Subject: [PATCH 08/66] Integrate DateTime record from Testing lib ... with all dependencies on Testing and DymolaModels removed --- Modelica/Utilities/Time.mo | 349 +++++++++++++++++++++++++++++++++++++ 1 file changed, 349 insertions(+) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 10c90acb7e..efd386a63a 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -106,6 +106,14 @@ dow = dayOfWeek(now) // = 5 ")); end dayOfWeek; + function daysInYear "Get number of days in year" + extends Modelica.Icons.Function; + input Integer a "Year"; + output Integer days "Number of days in year a"; + algorithm + days := if isLeapYear(a) then 366 else 365; + end daysInYear; + function isLeapYear "Check if a year is a leap year" extends Modelica.Icons.Function; input Integer year "Year"; @@ -177,6 +185,347 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] final constant String months[12] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} "Array of month names"; final constant String shortMonths[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"} "Array of abbreviated month names"; + operator record DateTime + extends Modelica.Icons.Record; + + Integer a "Year"; + Integer mon "Month"; + Integer d "Day"; + Integer h "Hour"; + Integer min "Minute"; + Integer s "Second"; + Integer ms "Millisecond"; + + encapsulated operator 'constructor' + import Modelica.Utilities.Time.DateTime; + import Modelica.Icons; + extends Icons.FunctionsPackage; + + function fromReadable + extends Icons.Function; + + input Integer a "Year"; + input Integer mon "Month"; + input Integer d "Day"; + input Integer h "Hour"; + input Integer min "Minute"; + input Integer s "Second"; + input Integer ms "Millisecond"; + + output DateTime t(ms=ms, s=s, min=min, h=h, d=d, mon=mon, a=a); + + algorithm + + end fromReadable; + + function fromSystemTime "Get system time as elapsed seconds since custom epoch year" + import Modelica.Utilities.Internal.Time.getTime; + extends Icons.Function; + + output DateTime t; + + protected + Integer ms "Millisecond"; + Integer s "Second"; + Integer min "Minute"; + Integer h "Hour"; + Integer d "Day"; + Integer mon "Month"; + Integer a "Year"; + + algorithm + (ms, s, min, h, d, mon, a) := getTime(); + t :=DateTime(ms=ms, s=s, min=min, h=h, d=d, mon=mon, a=a); + + end fromSystemTime; + + function fromEpoch + import Modelica.Utilities.Internal.Time.getTime; + import Modelica.Math.nearestInteger; + import Modelica.Utilities.Time.isLeapYear; + import Modelica.Utilities.Time.daysInYear; + extends Icons.Function; + + input Real seconds "Elapsed seconds since epoch_year"; + input Integer epoch_year = 1970 "Reference year"; + output DateTime t; + + protected + Integer[2,12] days_passed = {{31,59,90,120,151,181,212,243,273,304,334,365}, + {31,60,91,121,152,182,213,244,274,305,335,366}}; + Real rem; + Integer days, day_of_year, counted_days; + Integer i, j; + + Integer ms "Millisecond"; + Integer s "Second"; + Integer min "Minute"; + Integer h "Hour"; + Integer d "Day"; + Integer mon "Month"; + Integer a "Year"; + + algorithm + + // get milliseconds + ms := nearestInteger(mod(abs(seconds),1)*1000); + + // get seconds + s :=nearestInteger(mod(seconds,60)); + + // get minutes + rem :=seconds - s; + min :=nearestInteger(mod(rem/60, 60)); + + // get hours + rem :=rem - min*60; + h :=nearestInteger(mod(rem/3600, 24)); + + // get number of days since epoch year + rem :=rem - h*3600; + days :=nearestInteger(rem/(24*3600)); + + // get year + if days >= 0 then + // time is after reference year: count how many years must pass from reference year until 'days' is reached + a :=epoch_year; + counted_days := 0; + while counted_days+daysInYear(a) <= days loop + counted_days := counted_days + daysInYear(a); + a :=a + 1; + end while; + else + // time is before reference year: count years downwards + a :=epoch_year - 1; + counted_days := if isLeapYear(a) then -366 else -365; + while counted_days > days loop + a :=a - 1; + counted_days := counted_days - daysInYear(a); + end while; + end if; + + // compute day in current year + day_of_year :=days - counted_days + 1; + + // get month + // use correct column depending on leap and regular year + j :=if isLeapYear(a) then 2 else 1; + for i in 1:12 loop + if days_passed[j,i] >= day_of_year then + mon :=i; + break; + end if; + end for; + + // get day + d :=if mon > 1 then day_of_year - days_passed[j,mon-1] else day_of_year; + + t :=DateTime(ms=ms, s=s, min=min, h=h, d=d, mon=mon, a=a); + + end fromEpoch; + end 'constructor'; + + encapsulated operator 'String' "Convert DateTime to string" + import Modelica.Utilities.Time.DateTime; + import Modelica.Utilities.Strings.replace; + import Modelica.Icons; + extends Icons.FunctionsPackage; + + function formated + extends Icons.Function; + + input DateTime t; + input String format = "%y-%mon-%d %H:%MIN:%S"; + output String s; + + protected + encapsulated function string0 + import Modelica.Utilities.Strings.replace; + input Integer s; + input Integer l; + output String s0; + algorithm + s0 :=replace(String(s, minimumLength=l, leftJustified=false), " ", "0"); + end string0; + + algorithm + s :=replace(format, "%y", string0(t.a, l=4)); + s :=replace(s, "%Y", string0(t.a, l=4)); + s :=replace(s, "%mon", String(t.mon)); + s :=replace(s, "%MON", string0(t.mon, l=2)); + s :=replace(s, "%d", String(t.d)); + s :=replace(s, "%D", string0(t.d, l=2)); + s :=replace(s, "%h", String(t.h)); + s :=replace(s, "%H", string0(t.h, l=2)); + s :=replace(s, "%min", String(t.min)); + s :=replace(s, "%MIN", string0(t.min, l=2)); + s :=replace(s, "%s", String(t.s)); + s :=replace(s, "%S", string0(t.s, l=2)); + s :=replace(s, "%ms", String(t.ms)); + s :=replace(s, "%MS", string0(t.ms, l=2)); + + end formated; + + end 'String'; + + encapsulated operator function '==' + import Modelica.Utilities.Time.DateTime; + import Modelica.Icons; + extends Icons.Function; + + input DateTime t1; + input DateTime t2; + output Boolean result "= t1 == t2"; + + algorithm + result := t1.a == t2.a and + t1.mon == t2.mon and + t1.d == t2.d and + t1.h == t2.h and + t1.min == t2.min and + t1.s == t2.s and + t1.ms == t2.ms; + + end '=='; + + encapsulated operator function '<>' + import Modelica.Utilities.Time.DateTime; + import Modelica.Icons; + extends Icons.Function; + + input DateTime t1; + input DateTime t2; + output Boolean result "= t1 <> t2"; + + algorithm + result := not t1 == t2; + end '<>'; + + encapsulated operator function '>' + import Modelica.Utilities.Time.DateTime; + import Modelica.Icons; + extends Icons.Function; + + input DateTime t1; + input DateTime t2; + output Boolean result "= t1 <> t2"; + + algorithm + if t1.a > t2.a then + result :=true; + else + if t1.a==t2.a and t1.mon > t2.mon then + result :=true; + else + if t1.mon==t2.mon and t1.d > t2.d then + result :=true; + else + if t1.d==t2.d and t1.h > t2.h then + result :=true; + else + if t1.h==t2.h and t1.min > t2.min then + result :=true; + else + if t1.min==t2.min and t1.s > t2.s then + result :=true; + else + if t1.s==t2.s and t1.ms > t2.ms then + result :=true; + else + result :=false; + end if; + end if; + end if; + end if; + end if; + end if; + end if; + end '>'; + + encapsulated operator function '>=' + import Modelica.Utilities.Time.DateTime; + import Modelica.Icons; + extends Icons.Function; + + input DateTime t1; + input DateTime t2; + output Boolean result "= t1 >= t2"; + + algorithm + result :=t1 == t2 or t1 > t2; + + end '>='; + + encapsulated operator function '<' + import Modelica.Utilities.Time.DateTime; + import Modelica.Icons; + extends Icons.Function; + + input DateTime t1; + input DateTime t2; + output Boolean result "= t1 < t2"; + + algorithm + result := not t1 == t2 and t2 > t1; + + end '<'; + + encapsulated operator function '<=' + import Modelica.Utilities.Time.DateTime; + import Modelica.Icons; + extends Icons.Function; + + input DateTime t1; + input DateTime t2; + output Boolean result "= t1 <= t2"; + + algorithm + result := t1 == t2 or t1 < t2; + + end '<='; + + encapsulated function epoch "Convert time to elapsed seconds since custom epoch year" + import Modelica.Utilities.Time.DateTime; + import Modelica.Utilities.Time.isLeapYear; + import Modelica.Icons; + extends Icons.Function; + + input DateTime t; + input Integer epoch_year = 1970 "Reference year"; + output Real seconds "Elapsed seconds since epoch_year"; + + protected + Integer[2,12] days_passed = {{0,31,59,90,120,151,181,212,243,273,304,334}, + {0,31,60,91,121,152,182,213,244,274,305,335}}; + Integer leap_days, day_of_year; + Integer leap_years_til_epoch; + Integer j; + + // Aux variables for shorter access in code + Integer ms=t.ms "Millisecond"; + Integer s=t.s "Second"; + Integer min=t.min "Minute"; + Integer h=t.h "Hour"; + Integer d=t.d "Day"; + Integer mon=t.mon "Month"; + Integer a=t.a "Year"; + + algorithm + // get leap years from year 0 until the chosen epoch year + leap_years_til_epoch :=integer(epoch_year/4) - integer((epoch_year)/100) + integer((epoch_year)/400); + + // get leap days of passed years since epoch year + leap_days :=integer((a-1)/4) - integer((a-1)/100) + integer((a-1)/400) - leap_years_til_epoch; + + // get current day of year and consider leap day if current year is leap year and february has passed + day_of_year :=d + days_passed[j,mon]; + j := if isLeapYear(a) then 2 else 1; + + seconds :=ms/1000 + s + 60*(min + 60*(h + 24*(day_of_year-1 + leap_days + 365*(a-epoch_year)))); + + end epoch; + + end DateTime; annotation ( Documentation(info="

From cf42eab171ce98fbf5ab4fed387ffe4d8c56c58e Mon Sep 17 00:00:00 2001 From: mkr7 Date: Tue, 7 Apr 2020 17:43:05 +0200 Subject: [PATCH 09/66] Integrate Duration record from Testing lib ... with all dependencies on Testing and DymolaModels removed --- Modelica/Utilities/Time.mo | 446 +++++++++++++++++++++++++++++++++++++ 1 file changed, 446 insertions(+) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index efd386a63a..f27e2b8707 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -526,6 +526,452 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] end epoch; end DateTime; + + operator record Duration + extends Modelica.Icons.Record; + + Integer d "Days"; + Integer h "Hours"; + Integer min "Minutes"; + Integer s "Seconds"; + Integer ms "Milliseconds"; + + encapsulated operator 'constructor' + import Modelica.Utilities.Time.Duration; + import Modelica.Icons; + extends Icons.FunctionsPackage; + + function fromInput + extends Icons.Function; + + input Integer d=0 "Days"; + input Integer h=0 "Hours"; + input Integer min=0 "Minutes"; + input Integer s=0 "Seconds"; + input Integer ms=0 "Milliseconds"; + output Duration t(d=d, h=h, min=min, s=s, ms=ms); + algorithm + end fromInput; + + function fromDateTimes + import Modelica.Utilities.Time.DateTime; + import Modelica.Math.nearestInteger; + extends Icons.Function; + + input DateTime t1 "Start time"; + input DateTime t2 "End time"; + output Duration t; + + protected + Real diff; + Integer sign_; + Real e1, e2; + DateTime t_tmp; + Integer d "Elapsed days"; + Integer h "Elapsed hours"; + Integer min "Elapsed minutes"; + Integer s "Elapsed seconds"; + Integer ms "Elapsed milliseconds"; + + algorithm + t_tmp :=t2; + + e1 :=DateTime.epoch(t1, epoch_year=t1.a); + e2 :=DateTime.epoch(t_tmp, epoch_year=t1.a); + + diff :=abs(e2 - e1); + sign_ :=sign(e2 - e1); + + d := integer(diff/(24*3600)); + h := integer(diff/3600-d*24); + min := integer((diff-(d*24+h)*3600)/60); + s := integer(diff - (d*24+h)*3600 - min*60); + ms := nearestInteger(rem(diff,1)*1000); + + h := sign_*h; + min := sign_*min; + s := sign_*s; + ms := sign_*ms; + + t :=Duration(d=d, h=h, min=min, s=s, ms=ms); + + end fromDateTimes; + + function fromSeconds "Create duration record from seconds, rounding to the third decimal" + extends Icons.Function; + import Modelica.Math.nearestInteger; + + input Real seconds "Duration in seconds. Decimal place is converted to ms"; + output Duration t "Duration with input converted to s and ms"; + + protected + Integer d, h, min, s, ms; + Integer carryover; + + algorithm + + ms := nearestInteger(rem(seconds, 1) * 1000); + + s :=integer(div(seconds, 1)); + carryover :=div(s, 60); + s := rem(s, 60); + + min :=carryover; + carryover :=div(min, 60); + min :=rem(min, 60); + + h :=carryover; + carryover :=div(h, 24); + h := rem(h, 24); + + d := carryover; + + t :=Duration( + d=d, + h=h, + min=min, + s=s, + ms=ms); + + end fromSeconds; + end 'constructor'; + + encapsulated operator 'String' "Convert Duration to string" + import Modelica.Utilities.Time.Duration; + import Modelica.Utilities.Strings.replace; + import Modelica.Icons; + extends Icons.FunctionsPackage; + + function formated "Convert duration to string, using string replace" + import Modelica.Utilities.Strings.contains; + extends Icons.Function; + + input Duration t; + input String format = "%dd %hh %minmin %ss %msms"; + output String s; + + protected + Duration t2; + + encapsulated function string0 + import Modelica.Utilities.Strings.replace; + input Integer s; + input Integer l; + output String s0; + algorithm + s0 :=replace(String(s, minimumLength=l, leftJustified=false), " ", "0"); + end string0; + + algorithm + + t2 :=t; + + if not contains(format, "%d") and not contains(format, "%D") then + t2.h :=t2.h + t2.d*24; + end if; + + if not contains(format, "%h") and not contains(format, "%H") then + t2.min :=t2.min + t2.h*60; + end if; + + if not contains(format, "%min") and not contains(format, "%MIN") then + t2.s :=t2.s + t2.min*60; + end if; + + if not contains(format, "%s") and not contains(format, "%S") then + t2.ms :=t2.ms + t2.s*1000; + end if; + + s :=replace(format, "%d", String(t2.d)); + s :=replace(s, "%D", string0(t2.d, l=2)); + s :=replace(s, "%h", String(t2.h)); + s :=replace(s, "%H", string0(t2.h, l=2)); + s :=replace(s, "%min", String(t2.min)); + s :=replace(s, "%MIN", string0(t2.min, l=2)); + s :=replace(s, "%s", String(t2.s)); + s :=replace(s, "%S", string0(t2.s, l=2)); + s :=replace(s, "%ms", String(t2.ms)); + s :=replace(s, "%MS", string0(t2.ms, l=3)); + + end formated; + + end 'String'; + + encapsulated operator function '==' + import Modelica.Utilities.Time.Duration; + import Modelica.Icons; + extends Icons.Function; + + input Duration t1; + input Duration t2; + output Boolean result "= t1 == t2"; + + protected + Duration t1_norm = Duration.normalize(t1); + Duration t2_norm = Duration.normalize(t2); + + algorithm + result := t1_norm.d == t2_norm.d and + t1_norm.h == t2_norm.h and + t1_norm.min == t2_norm.min and + t1_norm.s == t2_norm.s and + t1_norm.ms == t2_norm.ms; + + end '=='; + + encapsulated operator function '<>' + import Modelica.Utilities.Time.Duration; + import Modelica.Icons; + extends Icons.Function; + + input Duration t1; + input Duration t2; + output Boolean result "= t1 <> t2"; + + algorithm + result := not t1 == t2; + end '<>'; + + encapsulated operator function '>' + import Modelica.Utilities.Time.Duration; + import Modelica.Icons; + extends Icons.Function; + + input Duration t1; + input Duration t2; + output Boolean result "= t1 <> t2"; + + algorithm + result :=Duration.inSeconds(t1) > Duration.inSeconds(t2); + end '>'; + + encapsulated operator function '>=' + import Modelica.Utilities.Time.Duration; + import Modelica.Icons; + extends Icons.Function; + + input Duration t1; + input Duration t2; + output Boolean result "= t1 >= t2"; + + algorithm + result :=t1 == t2 or t1 > t2; + + end '>='; + + encapsulated operator function '<' + import Modelica.Utilities.Time.Duration; + import Modelica.Icons; + extends Icons.Function; + + input Duration t1; + input Duration t2; + output Boolean result "= t1 < t2"; + + algorithm + result := not t1 == t2 and t2 > t1; + + end '<'; + + encapsulated operator function '<=' + import Modelica.Utilities.Time.Duration; + import Modelica.Icons; + extends Icons.Function; + + input Duration t1; + input Duration t2; + output Boolean result "= t1 <= t2"; + + algorithm + result := t1 == t2 or t1 < t2; + + end '<='; + + encapsulated operator function '+' + import Modelica.Utilities.Time.Duration; + import Modelica.Icons; + extends Icons.Function; + + input Duration t1; + input Duration t2; + output Duration result "= t1 + t2"; + + algorithm + result := Duration.normalize(Duration( + d=t1.d + t2.d, + h=t1.h + t2.h, + min=t1.min + t2.min, + s=t1.s + t2.s, + ms=t1.ms + t2.ms)); + + end '+'; + + encapsulated operator '-' "Unary and binary minus" + import Modelica.Utilities.Time.Duration; + import Modelica.Icons; + + extends Icons.FunctionsPackage; + + function subtract "Subtract two durations element wise" + extends Icons.Function; + + input Duration t1; + input Duration t2; + output Duration result "= t1 - t2"; + + algorithm + result := Duration.normalize(Duration( + d=t1.d - t2.d, + h=t1.h - t2.h, + min=t1.min - t2.min, + s=t1.s - t2.s, + ms=t1.ms - t2.ms)); + + end subtract; + + function negate "Unary minus (multiply duration values by -1)" + extends Icons.Function; + + input Duration t; + output Duration result "= -t1"; + + algorithm + result := Duration( + d=-t.d, + h=-t.h, + min=-t.min, + s=-t.s, + ms=-t.ms); + + end negate; + end '-'; + + encapsulated operator '*' "Multiplication" + import Modelica.Utilities.Time.Duration; + import Modelica.Icons; + + extends Icons.FunctionsPackage; + + function multiply1 "Multiply a duration with a real (by converting the duration to seconds)" + extends Icons.Function; + + input Duration t; + input Real r; + output Duration result "= inSeconds(t)*r"; + + algorithm + result := Duration(seconds=Duration.inSeconds(t)*r); + + end multiply1; + + function multiply2 "Multiply a duration with a real (by converting the duration to seconds)" + extends Icons.Function; + + input Real r; + input Duration t; + output Duration result "= inSeconds(t)*r"; + + algorithm + result := Duration(seconds=r*Duration.inSeconds(t)); + + end multiply2; + end '*'; + + encapsulated operator '/' "Divide a duration by a real" + import Modelica.Utilities.Time.Duration; + import Modelica.Icons; + + extends Icons.FunctionsPackage; + + function divide "Divide a duration by a real. The first ms value can vary by 1 (due to rounding in the fromSeconds constructor)" + extends Icons.Function; + + input Duration t; + input Real r; + output Duration result "= inSeconds(t)/r"; + + algorithm + result := Duration(seconds=Duration.inSeconds(t)/r); + + end divide; + + end '/'; + + encapsulated operator function '0' "Zero-element of addition (= Duration())" + import Modelica.Utilities.Time.Duration; + import Modelica.Icons; + extends Icons.Function; + + output Duration result "= Duration()"; + + algorithm + result := Duration(d=0, h=0, min=0, s=0, ms=0); + + end '0'; + + encapsulated function asVector "Return duration as vector {d, h, min, s, ms}" + import Modelica.Utilities.Time.Duration; + import Modelica.Icons; + extends Icons.Function; + + input Duration t "Value to convert"; + output Integer[5] t_vec "Elapsed time as vector {d, h, min, s, ms}"; + + algorithm + + t_vec :={t.d, t.h, t.min, t.s, t.ms}; + + end asVector; + + encapsulated function avg "Return average duration for a vector of durations" + import Modelica.Utilities.Time.Duration; + import Modelica.Icons; + extends Icons.Function; + + input Duration t_vec[:]; + output Duration t_avg "Average duration"; + + protected + Integer n = size(t_vec, 1); + Real seconds = 0; + + algorithm + for i in 1:n loop + seconds := seconds + Duration.inSeconds(t_vec[i]); + end for; + + t_avg := Duration(seconds=seconds/n); + + end avg; + + encapsulated function inSeconds "Convert Duration to seconds" + import Modelica.Utilities.Time.Duration; + import Modelica.Icons; + extends Icons.Function; + + input Duration t; + output Real seconds "Elapsed seconds"; + + algorithm + seconds :=t.ms/1000 + t.s + 60*(t.min + 60*(t.h + 24*t.d)); + + end inSeconds; + + encapsulated function normalize "Reformat duration with usual maximum values for ms, s, min and h and carryover to next bigger unit" + import Modelica.Utilities.Time.Duration; + import Modelica.Icons; + extends Icons.Function; + + input Duration t "Duration"; + output Duration t_norm "Normalized duration"; + + algorithm + + t_norm :=Duration(seconds=Duration.inSeconds(t)); + + end normalize; + + end Duration; annotation ( Documentation(info="

From d33b4613edfcff663efa903a4316956d484a1f05 Mon Sep 17 00:00:00 2001 From: mkr7 Date: Wed, 8 Apr 2020 13:08:49 +0200 Subject: [PATCH 10/66] Rename variables in DateTime (s -> second, ...) --- Modelica/Utilities/Time.mo | 192 ++++++++++++++++++------------------- 1 file changed, 96 insertions(+), 96 deletions(-) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index f27e2b8707..a62e38dbfb 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -188,13 +188,13 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] operator record DateTime extends Modelica.Icons.Record; - Integer a "Year"; - Integer mon "Month"; - Integer d "Day"; - Integer h "Hour"; - Integer min "Minute"; - Integer s "Second"; - Integer ms "Millisecond"; + Integer millisecond(min=0, max=999) "Millisecond" annotation(absoluteValue=true); + Integer second(min=0, max=61) "Second" annotation(absoluteValue=true); + Integer minute(min=0, max=59) "Minute" annotation(absoluteValue=true); + Integer hour(min=0, max=23) "Hour" annotation(absoluteValue=true); + Integer day(min=1, max=31) "Day" annotation(absoluteValue=true); + Integer month(min=1, max=12) "Month" annotation(absoluteValue=true); + Integer year "Year" annotation(absoluteValue=true); encapsulated operator 'constructor' import Modelica.Utilities.Time.DateTime; @@ -204,15 +204,15 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] function fromReadable extends Icons.Function; - input Integer a "Year"; - input Integer mon "Month"; - input Integer d "Day"; - input Integer h "Hour"; - input Integer min "Minute"; - input Integer s "Second"; - input Integer ms "Millisecond"; + input Integer year "Year"; + input Integer month "Month"; + input Integer day "Day"; + input Integer hour "Hour"; + input Integer minute "Minute"; + input Integer second "Second"; + input Integer millisecond "Millisecond"; - output DateTime t(ms=ms, s=s, min=min, h=h, d=d, mon=mon, a=a); + output DateTime t(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year); algorithm @@ -225,17 +225,17 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] output DateTime t; protected - Integer ms "Millisecond"; - Integer s "Second"; - Integer min "Minute"; - Integer h "Hour"; - Integer d "Day"; - Integer mon "Month"; - Integer a "Year"; + Integer millisecond "Millisecond"; + Integer second "Second"; + Integer minute "Minute"; + Integer hour "Hour"; + Integer day "Day"; + Integer month "Month"; + Integer year "Year"; algorithm - (ms, s, min, h, d, mon, a) := getTime(); - t :=DateTime(ms=ms, s=s, min=min, h=h, d=d, mon=mon, a=a); + (millisecond, second, minute, hour, day, month, year) := getTime(); + t :=DateTime(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year); end fromSystemTime; @@ -257,50 +257,50 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] Integer days, day_of_year, counted_days; Integer i, j; - Integer ms "Millisecond"; - Integer s "Second"; - Integer min "Minute"; - Integer h "Hour"; - Integer d "Day"; - Integer mon "Month"; - Integer a "Year"; + Integer millisecond "Millisecond"; + Integer second "Second"; + Integer minute "Minute"; + Integer hour "Hour"; + Integer day "Day"; + Integer month "Month"; + Integer year "Year"; algorithm // get milliseconds - ms := nearestInteger(mod(abs(seconds),1)*1000); + millisecond := nearestInteger(mod(abs(seconds),1)*1000); // get seconds - s :=nearestInteger(mod(seconds,60)); + second :=nearestInteger(mod(seconds,60)); // get minutes - rem :=seconds - s; - min :=nearestInteger(mod(rem/60, 60)); + rem :=seconds - second; + minute :=nearestInteger(mod(rem/60, 60)); // get hours - rem :=rem - min*60; - h :=nearestInteger(mod(rem/3600, 24)); + rem :=rem - minute*60; + hour :=nearestInteger(mod(rem/3600, 24)); // get number of days since epoch year - rem :=rem - h*3600; + rem :=rem - hour*3600; days :=nearestInteger(rem/(24*3600)); // get year if days >= 0 then // time is after reference year: count how many years must pass from reference year until 'days' is reached - a :=epoch_year; + year :=epoch_year; counted_days := 0; - while counted_days+daysInYear(a) <= days loop - counted_days := counted_days + daysInYear(a); - a :=a + 1; + while counted_days+daysInYear(year) <= days loop + counted_days := counted_days + daysInYear(year); + year :=year + 1; end while; else // time is before reference year: count years downwards - a :=epoch_year - 1; - counted_days := if isLeapYear(a) then -366 else -365; + year :=epoch_year - 1; + counted_days := if isLeapYear(year) then -366 else -365; while counted_days > days loop - a :=a - 1; - counted_days := counted_days - daysInYear(a); + year :=year - 1; + counted_days := counted_days - daysInYear(year); end while; end if; @@ -309,18 +309,18 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] // get month // use correct column depending on leap and regular year - j :=if isLeapYear(a) then 2 else 1; + j :=if isLeapYear(year) then 2 else 1; for i in 1:12 loop if days_passed[j,i] >= day_of_year then - mon :=i; + month :=i; break; end if; end for; // get day - d :=if mon > 1 then day_of_year - days_passed[j,mon-1] else day_of_year; + day :=if month > 1 then day_of_year - days_passed[j,month-1] else day_of_year; - t :=DateTime(ms=ms, s=s, min=min, h=h, d=d, mon=mon, a=a); + t :=DateTime(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year); end fromEpoch; end 'constructor'; @@ -335,34 +335,34 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] extends Icons.Function; input DateTime t; - input String format = "%y-%mon-%d %H:%MIN:%S"; - output String s; + input String format = "%Y-%MON-%D %H:%MIN:%S"; + output String str; protected - encapsulated function string0 + encapsulated function string0 "Create string with minimum length, filled with 0" import Modelica.Utilities.Strings.replace; - input Integer s; + input Integer i; input Integer l; output String s0; algorithm - s0 :=replace(String(s, minimumLength=l, leftJustified=false), " ", "0"); + s0 :=replace(String(i, minimumLength=l, leftJustified=false), " ", "0"); end string0; algorithm - s :=replace(format, "%y", string0(t.a, l=4)); - s :=replace(s, "%Y", string0(t.a, l=4)); - s :=replace(s, "%mon", String(t.mon)); - s :=replace(s, "%MON", string0(t.mon, l=2)); - s :=replace(s, "%d", String(t.d)); - s :=replace(s, "%D", string0(t.d, l=2)); - s :=replace(s, "%h", String(t.h)); - s :=replace(s, "%H", string0(t.h, l=2)); - s :=replace(s, "%min", String(t.min)); - s :=replace(s, "%MIN", string0(t.min, l=2)); - s :=replace(s, "%s", String(t.s)); - s :=replace(s, "%S", string0(t.s, l=2)); - s :=replace(s, "%ms", String(t.ms)); - s :=replace(s, "%MS", string0(t.ms, l=2)); + str :=replace(format, "%year", String( t.year)); + str :=replace(str, "%Y", string0(t.year, l=4)); + str :=replace(str, "%month", String( t.month)); + str :=replace(str, "%MON", string0(t.month, l=2)); + str :=replace(str, "%day", String( t.day)); + str :=replace(str, "%D", string0(t.day, l=2)); + str :=replace(str, "%hour", String( t.hour)); + str :=replace(str, "%H", string0(t.hour, l=2)); + str :=replace(str, "%minute", String( t.minute)); + str :=replace(str, "%MIN", string0(t.minute, l=2)); + str :=replace(str, "%second", String( t.second)); + str :=replace(str, "%S", string0(t.second, l=2)); + str :=replace(str, "%millisecond", String( t.millisecond)); + str :=replace(str, "%MS", string0(t.millisecond, l=3)); end formated; @@ -378,13 +378,13 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] output Boolean result "= t1 == t2"; algorithm - result := t1.a == t2.a and - t1.mon == t2.mon and - t1.d == t2.d and - t1.h == t2.h and - t1.min == t2.min and - t1.s == t2.s and - t1.ms == t2.ms; + result := t1.year == t2.year and + t1.month == t2.month and + t1.day == t2.day and + t1.hour == t2.hour and + t1.minute == t2.minute and + t1.second == t2.second and + t1.millisecond == t2.millisecond; end '=='; @@ -411,25 +411,25 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] output Boolean result "= t1 <> t2"; algorithm - if t1.a > t2.a then + if t1.year > t2.year then result :=true; else - if t1.a==t2.a and t1.mon > t2.mon then + if t1.year==t2.year and t1.month > t2.month then result :=true; else - if t1.mon==t2.mon and t1.d > t2.d then + if t1.month==t2.month and t1.day > t2.day then result :=true; else - if t1.d==t2.d and t1.h > t2.h then + if t1.day==t2.day and t1.hour > t2.hour then result :=true; else - if t1.h==t2.h and t1.min > t2.min then + if t1.hour==t2.hour and t1.minute > t2.minute then result :=true; else - if t1.min==t2.min and t1.s > t2.s then + if t1.minute==t2.minute and t1.second > t2.second then result :=true; else - if t1.s==t2.s and t1.ms > t2.ms then + if t1.second==t2.second and t1.millisecond > t2.millisecond then result :=true; else result :=false; @@ -502,26 +502,26 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] Integer j; // Aux variables for shorter access in code - Integer ms=t.ms "Millisecond"; - Integer s=t.s "Second"; - Integer min=t.min "Minute"; - Integer h=t.h "Hour"; - Integer d=t.d "Day"; - Integer mon=t.mon "Month"; - Integer a=t.a "Year"; + Integer millisecond=t.millisecond "Millisecond"; + Integer second=t.second "Second"; + Integer minute=t.minute "Minute"; + Integer hour=t.hour "Hour"; + Integer day=t.day "Day"; + Integer month=t.month "Month"; + Integer year=t.year "Year"; algorithm // get leap years from year 0 until the chosen epoch year leap_years_til_epoch :=integer(epoch_year/4) - integer((epoch_year)/100) + integer((epoch_year)/400); // get leap days of passed years since epoch year - leap_days :=integer((a-1)/4) - integer((a-1)/100) + integer((a-1)/400) - leap_years_til_epoch; + leap_days :=integer((year-1)/4) - integer((year-1)/100) + integer((year-1)/400) - leap_years_til_epoch; // get current day of year and consider leap day if current year is leap year and february has passed - day_of_year :=d + days_passed[j,mon]; - j := if isLeapYear(a) then 2 else 1; + j := if isLeapYear(year) then 2 else 1; + day_of_year :=day + days_passed[j,month]; - seconds :=ms/1000 + s + 60*(min + 60*(h + 24*(day_of_year-1 + leap_days + 365*(a-epoch_year)))); + seconds :=millisecond/1000 + second + 60*(minute + 60*(hour + 24*(day_of_year-1 + leap_days + 365*(year-epoch_year)))); end epoch; @@ -576,8 +576,8 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] algorithm t_tmp :=t2; - e1 :=DateTime.epoch(t1, epoch_year=t1.a); - e2 :=DateTime.epoch(t_tmp, epoch_year=t1.a); + e1 :=DateTime.epoch(t1, epoch_year=t1.year); + e2 :=DateTime.epoch(t_tmp, epoch_year=t1.year); diff :=abs(e2 - e1); sign_ :=sign(e2 - e1); From a7fdfb31d4a49442b38b177e186b003d22c3c9a6 Mon Sep 17 00:00:00 2001 From: mkr7 Date: Wed, 8 Apr 2020 13:41:10 +0200 Subject: [PATCH 11/66] Rename seconds to totalSeconds in Duration --- Modelica/Utilities/Time.mo | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index a62e38dbfb..d86cedd4f0 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -601,7 +601,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] extends Icons.Function; import Modelica.Math.nearestInteger; - input Real seconds "Duration in seconds. Decimal place is converted to ms"; + input Real totalSeconds "Duration in seconds. Decimal place is converted to ms"; output Duration t "Duration with input converted to s and ms"; protected @@ -610,9 +610,9 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] algorithm - ms := nearestInteger(rem(seconds, 1) * 1000); + ms := nearestInteger(rem(totalSeconds, 1) * 1000); - s :=integer(div(seconds, 1)); + s :=integer(div(totalSeconds, 1)); carryover :=div(s, 60); s := rem(s, 60); @@ -860,7 +860,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] output Duration result "= inSeconds(t)*r"; algorithm - result := Duration(seconds=Duration.inSeconds(t)*r); + result := Duration(totalSeconds=Duration.inSeconds(t)*r); end multiply1; @@ -872,7 +872,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] output Duration result "= inSeconds(t)*r"; algorithm - result := Duration(seconds=r*Duration.inSeconds(t)); + result := Duration(totalSeconds=r*Duration.inSeconds(t)); end multiply2; end '*'; @@ -891,7 +891,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] output Duration result "= inSeconds(t)/r"; algorithm - result := Duration(seconds=Duration.inSeconds(t)/r); + result := Duration(totalSeconds=Duration.inSeconds(t)/r); end divide; @@ -933,27 +933,27 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] protected Integer n = size(t_vec, 1); - Real seconds = 0; + Real totalSeconds = 0; algorithm for i in 1:n loop - seconds := seconds + Duration.inSeconds(t_vec[i]); + totalSeconds := totalSeconds + Duration.inSeconds(t_vec[i]); end for; - t_avg := Duration(seconds=seconds/n); + t_avg := Duration(totalSeconds=totalSeconds/n); end avg; - encapsulated function inSeconds "Convert Duration to seconds" + encapsulated function inSeconds "Convert Duration to total amount of seconds" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.Function; input Duration t; - output Real seconds "Elapsed seconds"; + output Real totalSeconds "Elapsed seconds"; algorithm - seconds :=t.ms/1000 + t.s + 60*(t.min + 60*(t.h + 24*t.d)); + totalSeconds :=t.ms/1000 + t.s + 60*(t.min + 60*(t.h + 24*t.d)); end inSeconds; @@ -967,7 +967,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] algorithm - t_norm :=Duration(seconds=Duration.inSeconds(t)); + t_norm :=Duration(totalSeconds=Duration.inSeconds(t)); end normalize; From 0feec0ec2130cf5eb7ac8337d965ebacaf590ad5 Mon Sep 17 00:00:00 2001 From: mkr7 Date: Wed, 8 Apr 2020 13:45:54 +0200 Subject: [PATCH 12/66] Rename variables in Duration (s -> seconds, ...) --- Modelica/Utilities/Time.mo | 190 ++++++++++++++++++------------------- 1 file changed, 95 insertions(+), 95 deletions(-) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index d86cedd4f0..3d4a7d6892 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -530,11 +530,11 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] operator record Duration extends Modelica.Icons.Record; - Integer d "Days"; - Integer h "Hours"; - Integer min "Minutes"; - Integer s "Seconds"; - Integer ms "Milliseconds"; + Integer days "Days"; + Integer hours "Hours"; + Integer minutes "Minutes"; + Integer seconds "Seconds"; + Integer milliseconds "Milliseconds"; encapsulated operator 'constructor' import Modelica.Utilities.Time.Duration; @@ -544,12 +544,12 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] function fromInput extends Icons.Function; - input Integer d=0 "Days"; - input Integer h=0 "Hours"; - input Integer min=0 "Minutes"; - input Integer s=0 "Seconds"; - input Integer ms=0 "Milliseconds"; - output Duration t(d=d, h=h, min=min, s=s, ms=ms); + input Integer days=0 "Days"; + input Integer hours=0 "Hours"; + input Integer minutes=0 "Minutes"; + input Integer seconds=0 "Seconds"; + input Integer milliseconds=0 "Milliseconds"; + output Duration t(days=days, hours=hours, minutes=minutes, seconds=seconds, milliseconds=milliseconds); algorithm end fromInput; @@ -567,11 +567,11 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] Integer sign_; Real e1, e2; DateTime t_tmp; - Integer d "Elapsed days"; - Integer h "Elapsed hours"; - Integer min "Elapsed minutes"; - Integer s "Elapsed seconds"; - Integer ms "Elapsed milliseconds"; + Integer days "Elapsed days"; + Integer hours "Elapsed hours"; + Integer minutes "Elapsed minutes"; + Integer seconds "Elapsed seconds"; + Integer milliseconds "Elapsed milliseconds"; algorithm t_tmp :=t2; @@ -582,18 +582,18 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] diff :=abs(e2 - e1); sign_ :=sign(e2 - e1); - d := integer(diff/(24*3600)); - h := integer(diff/3600-d*24); - min := integer((diff-(d*24+h)*3600)/60); - s := integer(diff - (d*24+h)*3600 - min*60); - ms := nearestInteger(rem(diff,1)*1000); + days := integer(diff/(24*3600)); + hours := integer(diff/3600-days*24); + minutes := integer((diff-(days*24+hours)*3600)/60); + seconds := integer(diff - (days*24+hours)*3600 - minutes*60); + milliseconds := nearestInteger(rem(diff,1)*1000); - h := sign_*h; - min := sign_*min; - s := sign_*s; - ms := sign_*ms; + hours := sign_*hours; + minutes := sign_*minutes; + seconds := sign_*seconds; + milliseconds := sign_*milliseconds; - t :=Duration(d=d, h=h, min=min, s=s, ms=ms); + t :=Duration(days=days, hours=hours, minutes=minutes, seconds=seconds, milliseconds=milliseconds); end fromDateTimes; @@ -601,37 +601,37 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] extends Icons.Function; import Modelica.Math.nearestInteger; - input Real totalSeconds "Duration in seconds. Decimal place is converted to ms"; - output Duration t "Duration with input converted to s and ms"; + input Real totalSeconds "Duration in seconds. Decimal place is converted to milliseconds"; + output Duration t "Duration with input converted to seconds and milliseconds"; protected - Integer d, h, min, s, ms; + Integer days, hours, minutes, seconds, milliseconds; Integer carryover; algorithm - ms := nearestInteger(rem(totalSeconds, 1) * 1000); + milliseconds := nearestInteger(rem(totalSeconds, 1) * 1000); - s :=integer(div(totalSeconds, 1)); - carryover :=div(s, 60); - s := rem(s, 60); + seconds :=integer(div(totalSeconds, 1)); + carryover :=div(seconds, 60); + seconds := rem(seconds, 60); - min :=carryover; - carryover :=div(min, 60); - min :=rem(min, 60); + minutes :=carryover; + carryover :=div(minutes, 60); + minutes :=rem(minutes, 60); - h :=carryover; - carryover :=div(h, 24); - h := rem(h, 24); + hours :=carryover; + carryover :=div(hours, 24); + hours := rem(hours, 24); - d := carryover; + days := carryover; t :=Duration( - d=d, - h=h, - min=min, - s=s, - ms=ms); + days=days, + hours=hours, + minutes=minutes, + seconds=seconds, + milliseconds=milliseconds); end fromSeconds; end 'constructor'; @@ -647,51 +647,51 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] extends Icons.Function; input Duration t; - input String format = "%dd %hh %minmin %ss %msms"; - output String s; + input String format = "%daysd %hoursh %minutesmin %secondss %millisecondsms"; + output String str; protected Duration t2; - encapsulated function string0 + encapsulated function string0 "Create string with minimum length, filled with 0" import Modelica.Utilities.Strings.replace; - input Integer s; + input Integer i; input Integer l; output String s0; algorithm - s0 :=replace(String(s, minimumLength=l, leftJustified=false), " ", "0"); + s0 :=replace(String(i, minimumLength=l, leftJustified=false), " ", "0"); end string0; algorithm t2 :=t; - if not contains(format, "%d") and not contains(format, "%D") then - t2.h :=t2.h + t2.d*24; + if not contains(format, "%days") and not contains(format, "%D") then + t2.hours :=t2.hours + t2.days*24; end if; - if not contains(format, "%h") and not contains(format, "%H") then - t2.min :=t2.min + t2.h*60; + if not contains(format, "%hours") and not contains(format, "%H") then + t2.minutes :=t2.minutes + t2.hours*60; end if; - if not contains(format, "%min") and not contains(format, "%MIN") then - t2.s :=t2.s + t2.min*60; + if not contains(format, "%minutes") and not contains(format, "%MIN") then + t2.seconds :=t2.seconds + t2.minutes*60; end if; - if not contains(format, "%s") and not contains(format, "%S") then - t2.ms :=t2.ms + t2.s*1000; + if not contains(format, "%seconds") and not contains(format, "%S") then + t2.milliseconds :=t2.milliseconds + t2.seconds*1000; end if; - s :=replace(format, "%d", String(t2.d)); - s :=replace(s, "%D", string0(t2.d, l=2)); - s :=replace(s, "%h", String(t2.h)); - s :=replace(s, "%H", string0(t2.h, l=2)); - s :=replace(s, "%min", String(t2.min)); - s :=replace(s, "%MIN", string0(t2.min, l=2)); - s :=replace(s, "%s", String(t2.s)); - s :=replace(s, "%S", string0(t2.s, l=2)); - s :=replace(s, "%ms", String(t2.ms)); - s :=replace(s, "%MS", string0(t2.ms, l=3)); + str :=replace(format, "%days", String( t2.days)); + str :=replace(str, "%D", string0(t2.days, l=2)); + str :=replace(str, "%hours", String( t2.hours)); + str :=replace(str, "%H", string0(t2.hours, l=2)); + str :=replace(str, "%minutes", String( t2.minutes)); + str :=replace(str, "%MIN", string0(t2.minutes, l=2)); + str :=replace(str, "%seconds", String( t2.seconds)); + str :=replace(str, "%S", string0(t2.seconds, l=2)); + str :=replace(str, "%milliseconds", String( t2.milliseconds)); + str :=replace(str, "%MS", string0(t2.milliseconds, l=3)); end formated; @@ -711,11 +711,11 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] Duration t2_norm = Duration.normalize(t2); algorithm - result := t1_norm.d == t2_norm.d and - t1_norm.h == t2_norm.h and - t1_norm.min == t2_norm.min and - t1_norm.s == t2_norm.s and - t1_norm.ms == t2_norm.ms; + result := t1_norm.days == t2_norm.days and + t1_norm.hours == t2_norm.hours and + t1_norm.minutes == t2_norm.minutes and + t1_norm.seconds == t2_norm.seconds and + t1_norm.milliseconds == t2_norm.milliseconds; end '=='; @@ -798,11 +798,11 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] algorithm result := Duration.normalize(Duration( - d=t1.d + t2.d, - h=t1.h + t2.h, - min=t1.min + t2.min, - s=t1.s + t2.s, - ms=t1.ms + t2.ms)); + days=t1.days + t2.days, + hours=t1.hours + t2.hours, + minutes=t1.minutes + t2.minutes, + seconds=t1.seconds + t2.seconds, + milliseconds=t1.milliseconds + t2.milliseconds)); end '+'; @@ -821,11 +821,11 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] algorithm result := Duration.normalize(Duration( - d=t1.d - t2.d, - h=t1.h - t2.h, - min=t1.min - t2.min, - s=t1.s - t2.s, - ms=t1.ms - t2.ms)); + days=t1.days - t2.days, + hours=t1.hours - t2.hours, + minutes=t1.minutes - t2.minutes, + seconds=t1.seconds - t2.seconds, + milliseconds=t1.milliseconds - t2.milliseconds)); end subtract; @@ -837,11 +837,11 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] algorithm result := Duration( - d=-t.d, - h=-t.h, - min=-t.min, - s=-t.s, - ms=-t.ms); + days=-t.days, + hours=-t.hours, + minutes=-t.minutes, + seconds=-t.seconds, + milliseconds=-t.milliseconds); end negate; end '-'; @@ -883,7 +883,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] extends Icons.FunctionsPackage; - function divide "Divide a duration by a real. The first ms value can vary by 1 (due to rounding in the fromSeconds constructor)" + function divide "Divide a duration by a real. The first milliseconds value can vary by 1 (due to rounding in the fromSeconds constructor)" extends Icons.Function; input Duration t; @@ -905,21 +905,21 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] output Duration result "= Duration()"; algorithm - result := Duration(d=0, h=0, min=0, s=0, ms=0); + result := Duration(days=0, hours=0, minutes=0, seconds=0, milliseconds=0); end '0'; - encapsulated function asVector "Return duration as vector {d, h, min, s, ms}" + encapsulated function asVector "Return duration as vector {days, hours, minutes, seconds, milliseconds}" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.Function; input Duration t "Value to convert"; - output Integer[5] t_vec "Elapsed time as vector {d, h, min, s, ms}"; + output Integer[5] t_vec "Elapsed time as vector {days, hours, minutes, seconds, milliseconds}"; algorithm - t_vec :={t.d, t.h, t.min, t.s, t.ms}; + t_vec :={t.days, t.hours, t.minutes, t.seconds, t.milliseconds}; end asVector; @@ -953,11 +953,11 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] output Real totalSeconds "Elapsed seconds"; algorithm - totalSeconds :=t.ms/1000 + t.s + 60*(t.min + 60*(t.h + 24*t.d)); + totalSeconds :=t.milliseconds/1000 + t.seconds + 60*(t.minutes + 60*(t.hours + 24*t.days)); end inSeconds; - encapsulated function normalize "Reformat duration with usual maximum values for ms, s, min and h and carryover to next bigger unit" + encapsulated function normalize "Reformat duration with usual maximum values for milliseconds, seconds, minutes and hours and carryover to next bigger unit" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.Function; From 2fe519e9db3fa1c3cef63e8b6cbb38f52292d5d4 Mon Sep 17 00:00:00 2001 From: mkr7 Date: Wed, 8 Apr 2020 17:57:30 +0200 Subject: [PATCH 13/66] Support '-' for DateTime --- Modelica/Utilities/Time.mo | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 3d4a7d6892..820668a2eb 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -484,6 +484,28 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] end '<='; + encapsulated operator '-' "Unary and binary minus" + import Modelica.Utilities.Time.DateTime; + import Modelica.Icons; + + extends Icons.FunctionsPackage; + + function subtract "Subtract two durations element wise" + extends Icons.Function; + + import Modelica.Utilities.Time.Duration; + + input DateTime t1; + input DateTime t2; + output Duration result "= t1 - t2"; + + algorithm + result := Duration.'constructor'.fromDateTimes(t2, t1); + + end subtract; + + end '-'; + encapsulated function epoch "Convert time to elapsed seconds since custom epoch year" import Modelica.Utilities.Time.DateTime; import Modelica.Utilities.Time.isLeapYear; From c4fbfc071f12446dafbbf3119825feafbc07c093 Mon Sep 17 00:00:00 2001 From: mkr7 Date: Thu, 9 Apr 2020 14:26:55 +0200 Subject: [PATCH 14/66] Add DateTime.now() --- Modelica/Utilities/Time.mo | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 820668a2eb..df19baf986 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -547,6 +547,13 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] end epoch; + encapsulated function now "Get current system date and time as DateTime" + import Modelica.Utilities.Time.DateTime; + output DateTime now; + algorithm + now:=DateTime.'constructor'.fromSystemTime(); + end now; + end DateTime; operator record Duration From 538860291d9f5c41fd741c9e919cbec5694edfc2 Mon Sep 17 00:00:00 2001 From: mkr7 Date: Wed, 8 Apr 2020 14:22:22 +0200 Subject: [PATCH 15/66] Add tests for Duration --- ModelicaTest/Utilities.mo | 150 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index 544296080b..eb1a45de98 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -435,6 +435,155 @@ extends Modelica.Icons.ExamplesPackage; ok := true; end Time; + function Duration "Test constructors and functions in Duration operator record" + + import Modelica.Utilities.Streams; + import Modelica.Utilities.Time.Duration; + import Modelica.Utilities.Time.DateTime; + + extends Modelica.Icons.Function; + + input String logFile="ModelicaTestLog.txt" "Filename where the log is stored"; + output Boolean ok; + + protected + Duration d1, d2, d3; + Duration act, ref; + Real ref_r, act_r; + Integer ref_v[:], act_v[:]; + String act_s, ref_s; + + algorithm + + Streams.print("... Test of Modelica.Utilities.Time.Duration"); + Streams.print("... Test of Modelica.Utilities.Time.Duration", logFile); + + // preparation of duration variables, used through out the test + d1 :=Duration(days=1, hours=1, minutes=1, seconds=1, milliseconds=1); + d2 :=Duration(days=0, hours=23, minutes=59, seconds=59, milliseconds=999); + d3 :=Duration(days=-1, hours=-1, minutes=-1, seconds=-1, milliseconds=-1); + + // constructors + ref := Duration(days=-45, hours=33, minutes=61, seconds=-9871, milliseconds=-1501); + act := Duration(totalSeconds=-3775412.501); + assert(ref==act, "constructor test 1 failed (fromSeconds vs fromInput)"); + + ref := Duration(t1=DateTime(2020, 1, 1, 1, 1, 1, 1), t2=DateTime(2020, 1, 1, 1, 1, 2, 500)); + act := Duration(totalSeconds=1.499); + assert(ref==act, "constructor test 2 failed (fromSeconds vs fromDateTimes, positive result)"); + + ref := Duration(t1=DateTime(2020, 1, 1, 1, 1, 2, 500), t2=DateTime(2020, 1, 1, 1, 1, 1, 1)); + act := Duration(totalSeconds=-1.499); + assert(ref==act, "constructor test 3 failed (fromSeconds vs fromDateTimes, negative result)"); + + // 'String'.formated + ref_s :="1d 1h 1min 1s 1ms"; + act_s :=String(d1); + assert(ref_s==act_s, "default string formating failed. Got "+ref_s+"<>"+act_s); + + ref_s :="25h 01' 01.001''"; + act_s :=String(d1, format="%Hh %MIN' %S.%MS''"); + assert(ref_s==act_s, "custom string formating 1 failed. \n"+ref_s+" <> "+act_s); + + // == + assert(d1==d1, "d1==d1 failed"); + + // <> + assert(d1<>d2, "d1<>d2 failed"); + + // > + assert(d1>d2, "d1>d2 failed"); + + // >= + assert(d1>=d2, "d1>=d2 failed"); + assert(d1>=d1, "d1>=d1 failed"); + + // < + assert(d3 1 + act := Duration.normalize(d1 / 2); + assert(ref==act, "d1/2 failed"); + + // '0' + // todo + + // asVector + ref_v :={0, 23, 59, 59, 999}; + act_v :=Duration.asVector(d2); + + for i in 1:size(ref_v, 1) loop + assert(ref_v[i]==act_v[i], "conversion to vector failed (element "+String(i)+")"); + end for; + + // avg + ref := Duration(days=1, hours=0, minutes=30, seconds=30, milliseconds=500); + act := Duration.avg({d1, d2}); + assert(ref==act, "average of {d1, d2} failed"); + + // inSeconds + ref_r := 2291959.03; + act_r := Duration.inSeconds(Duration(days=27, hours=-12, minutes=30, seconds=559, milliseconds=30)); + assert(ref_r==act_r, "conversion to total seconds failed"); + + // normalize + ref_v :={2, 5, 38, 0, 1}; + act_v :=Duration.asVector( + Duration.normalize( + Duration(days=1, hours=28, minutes=97, seconds=59, milliseconds=1001))); + + for i in 1:size(ref_v, 1) loop + assert(ref_v[i]==act_v[i], "normalization failed (element " +String(i)+")"); + end for; + + // sum + assert(sum(Duration(days=i) for i in 1:2) == Duration(days=3), "sum failed"); + + // return result + ok := true; + + end Duration; + function Internal "Test functions of Modelica.Utilities.Internal" extends Modelica.Icons.Function; import Modelica.Utilities.Internal.FileSystem; @@ -540,6 +689,7 @@ extends Modelica.Icons.ExamplesPackage; result := ModelicaTest.Utilities.Internal(logFile); result := ModelicaTest.Utilities.System(logFile); result := ModelicaTest.Utilities.Time(logFile); + result := ModelicaTest.Utilities.Duration(logFile); ok := true; end testAll; From a314ccb58cf125ed3d3c4d878b4357784553033c Mon Sep 17 00:00:00 2001 From: mkr7 Date: Wed, 8 Apr 2020 15:59:20 +0200 Subject: [PATCH 16/66] Add tests for DateTime --- ModelicaTest/Utilities.mo | 253 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index eb1a45de98..d721a7c903 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -435,6 +435,258 @@ extends Modelica.Icons.ExamplesPackage; ok := true; end Time; + function DateTime "Test constructors of Duration operator record and conversion to epoch" + + import Modelica.Utilities.Streams; + import Modelica.Utilities.Time.DateTime; + import Modelica.Utilities.Time.Duration; + + extends Modelica.Icons.Function; + + input String logFile="ModelicaTestLog.txt" "Filename where the log is stored"; + output Boolean ok; + + protected + Real act_r, ref_r; + String act_s, ref_s; + DateTime act_dt, ref_dt; + DateTime t1, t2, t3; + Integer _ "Dummy to swallow return value"; + + algorithm + + Streams.print("... Test of Modelica.Utilities.Time.DateTime"); + Streams.print("... Test of Modelica.Utilities.Time.DateTime", logFile); + + // ---------------------------------------- // + // constructor and epoch conversion tests // + // ---------------------------------------- // + + // 1 day passed since custom epoch year 2017 + ref_dt := DateTime(2017, 1, 1, 1, 1, 1, 0); + ref_r := 3661; + + act_dt := DateTime(ref_r, epoch_year=2017); + act_r := DateTime.epoch(ref_dt, epoch_year=2017); + + assert(ref_dt==act_dt, "constructor test failed (1 day since 2017)"); + assert(ref_r==act_r, "conversion to epoch failed (1 day since 2017)"); + + // 1 day passed since default epoch year 2017 + ref_dt:= DateTime(1970, 1, 1, 1, 1, 1, 0); + ref_r := 3661; + + act_dt:= DateTime(ref_r); + act_r := DateTime.epoch(ref_dt); + + assert(ref_dt==act_dt, "constructor test failed (1 day since 1970)"); + assert(ref_r==act_r, "conversion to epoch failed (1 day since 1970)"); + + // start of new year + ref_dt:= DateTime(1999, 12, 31, 23, 59, 59, 0); + ref_r := 946684799; + + act_dt:= DateTime(ref_r); + act_r := DateTime.epoch(ref_dt); + + assert(ref_dt==act_dt, "constructor test failed (last second in 1999)"); + assert(ref_r==act_r, "conversion to epoch failed (last second in 1999)"); + + ref_dt:= DateTime(2000, 1, 1, 0, 0, 0, 0); + ref_r := 946684800; + + act_dt:= DateTime(ref_r); + act_r := DateTime.epoch(ref_dt); + + assert(ref_dt==act_dt, "constructor test failed (first second in 2000)"); + assert(ref_r==act_r, "conversion to epoch failed (first second in 2000)"); + + // special leap year (new century and multiple of 400) + ref_dt:= DateTime(2000, 2, 28, 23, 59, 59, 0); + ref_r := 951782399; + + act_dt:= DateTime(ref_r); + act_r := DateTime.epoch(ref_dt); + + assert(ref_dt==act_dt, "constructor test failed (detection of special leap year 1)"); + assert(ref_r==act_r, "conversion to epoch failed (detection of special leap year 1)"); + + ref_dt:= DateTime(2000, 3, 1, 0, 0, 0, 0); + ref_r := 951868800; + + act_dt:= DateTime(ref_r); + act_r := DateTime.epoch(ref_dt); + + assert(ref_dt==act_dt, "constructor test failed (detection of special leap year 2)"); + assert(ref_r==act_r, "conversion to epoch failed (detection of special leap year 2)"); + + // no leap year (new century) + ref_dt:= DateTime(2100, 2, 28, 23, 59, 59, 0); + ref_r := 951782399; + + act_dt:= DateTime(ref_r, epoch_year=2070); + act_r := DateTime.epoch(ref_dt, epoch_year=2070); + + assert(ref_dt==act_dt, "constructor test failed (detection of special non-leap year 1)"); + assert(ref_r==act_r, "conversion to epoch failed (detection of special non-leap year 1)"); + + ref_dt:= DateTime(2100, 3, 1, 0, 0, 0, 0); + ref_r := 951782400; + + act_dt:= DateTime(ref_r, epoch_year=2070); + act_r := DateTime.epoch(ref_dt, epoch_year=2070); + + assert(ref_dt==act_dt, "constructor test failed (detection of special non-leap year 2)"); + assert(ref_r==act_r, "conversion to epoch failed (detection of special non-leap year 2)"); + + // regular year + ref_dt:= DateTime(2017, 2, 28, 23, 59, 59, 0); + ref_r := 1488326399; + + act_dt:= DateTime(ref_r); + act_r := DateTime.epoch(ref_dt); + + assert(ref_dt==act_dt, "constructor test failed (regular year 1)"); + assert(ref_r==act_r, "conversion to epoch failed (regular year 1)"); + + ref_dt:= DateTime(2017, 3, 1, 0, 0, 0, 0); + ref_r := 1488326400; + + act_dt:= DateTime(ref_r); + act_r := DateTime.epoch(ref_dt); + + assert(ref_dt==act_dt, "constructor test failed (regular year 2)"); + assert(ref_r==act_r, "conversion to epoch failed (regular year 2)"); + + // leap year + ref_dt:= DateTime(2020, 2, 28, 23, 59, 59, 0); + ref_r := 1582934399; + + act_dt:= DateTime(ref_r); + act_r := DateTime.epoch(ref_dt); + + assert(ref_dt==act_dt, "constructor test failed (regular leap year 1)"); + assert(ref_r==act_r, "conversion to epoch failed (regular leap year 1)"); + + ref_dt:= DateTime(2020, 3, 1, 0, 0, 0, 0); + ref_r := 1583020800; + + act_dt:= DateTime(ref_r); + act_r := DateTime.epoch(ref_dt); + + assert(ref_dt==act_dt, "constructor test failed (regular leap year 2)"); + assert(ref_r==act_r, "conversion to epoch failed (regular leap year 2)"); + + // end of year + ref_dt:= DateTime(2019, 12, 31, 23, 59, 59, 0); + ref_r := 1577836799; + + act_dt:= DateTime(ref_r); + act_r := DateTime.epoch(ref_dt); + + assert(ref_dt==act_dt, "constructor test failed (end of regular year)"); + assert(ref_r==act_r, "conversion to epoch failed (end of regular year)"); + + ref_dt:= DateTime(2020, 12, 31, 23, 59, 59, 0); + ref_r := 1609459199; + + act_dt:= DateTime(ref_r); + act_r := DateTime.epoch(ref_dt); + + assert(ref_dt==act_dt, "constructor test failed (end of leap year)"); + assert(ref_r==act_r, "conversion to epoch failed (end of leap year)"); + + // lowest value for 32bit integer + // this test is not relevant, as datatype Real is used to count + // seconds from epoch. Would be needed, if Integer is used instead + // (for whatever reason). But it does not hurt to have it, so we keep it. + ref_dt:= DateTime(1901, 12, 13, 20, 45, 53, 0); + ref_r := -2147483647; + + act_dt:= DateTime(ref_r); + act_r := DateTime.epoch(ref_dt); + + assert(ref_dt==act_dt, "constructor test failed (lowest possible value for 32bit integer)"); + assert(ref_r==act_r, "conversion to epoch failed (lowest possible value for 32bit integer)"); + + // regular year in past + ref_dt:= DateTime(1910, 12, 31, 1, 59, 59, 0); + ref_r := -1861999201; + + act_dt:= DateTime(ref_r); + act_r := DateTime.epoch(ref_dt); + + assert(ref_dt==act_dt, "constructor test failed (regular year before epoch)"); + assert(ref_r==act_r, "conversion to epoch failed (regular year before epoch)"); + + // leap year in past + ref_dt:= DateTime(1912, 12, 31, 1, 59, 59, 0); + ref_r := -1798840801; + + act_dt:= DateTime(ref_r); + act_r := DateTime.epoch(ref_dt); + + assert(ref_dt==act_dt, "constructor test failed (leap regular year before epoch)"); + assert(ref_r==act_r, "conversion to epoch failed (leap regular year before epoch)"); + + // compare two DateTime records created from system time. t2 should be a few seconds later than t1 + t1 :=DateTime(); + _ :=Modelica.Utilities.System.command("sleep 1") "Sleep 1s on linux"; + _ :=Modelica.Utilities.System.command("ping -n 2 127.0.0.1 > NUL") "Sleep 1s on windows"; + t2 :=DateTime(); + + assert( (t2 > t1) and (DateTime.epoch(t2)-DateTime.epoch(t1) < 5), + "constructor from system time failed (t1 is younger than t2)"); + + + // ---------------- // + // operator tests // + // ---------------- // + + t1 := DateTime(2019, 12, 31, 23, 59, 59, 999); + t2 := DateTime(2020, 01, 01, 00, 00, 00, 0); + t3 := DateTime(2020, 01, 01, 00, 00, 00, 1); + + // 'String'.formated + ref_s :="2020-01-01 00:00:00"; + act_s :=String(t3); + assert(ref_s==act_s, "default string formating failed. \n"+ref_s+" <> "+act_s); + + ref_s :="0h 0min 0.001s"; + act_s :=String(t3, format="%hourh %minutemin %second.%MSs"); + assert(ref_s==act_s, "custom string formating 1 failed. \n"+ref_s+" <> "+act_s); + + // == + assert(t1==t1, "t1==t1 failed"); + + // <> + assert(t1<>t2, "t1<>t2 failed"); + + // > + assert(t2>t1, "t2>t1 failed"); + + // >= + assert(t2>=t1, "t2>=t1 failed"); + assert(t1>=t1, "t1>=t1 failed"); + + // < + assert(t2 Date: Thu, 9 Apr 2020 13:40:46 +0200 Subject: [PATCH 17/66] Format DateTime like strftime in C++ and Python ... but currently using only a subset of the available formatting options. --- Modelica/Utilities/Time.mo | 163 +++++++++++++++++++++++++++++++++---- ModelicaTest/Utilities.mo | 12 ++- 2 files changed, 158 insertions(+), 17 deletions(-) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index df19baf986..3cf50c2c15 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -334,8 +334,14 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] function formated extends Icons.Function; + import Modelica.Utilities.Internal.Time.dayOfWeek; + import Modelica.Utilities.Time.weekDays; + import Modelica.Utilities.Time.shortWeekDays; + import Modelica.Utilities.Time.months; + import Modelica.Utilities.Time.shortMonths; + input DateTime t; - input String format = "%Y-%MON-%D %H:%MIN:%S"; + input String format = "%Y-%m-%d %H:%M:%S"; output String str; protected @@ -349,21 +355,148 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] end string0; algorithm - str :=replace(format, "%year", String( t.year)); - str :=replace(str, "%Y", string0(t.year, l=4)); - str :=replace(str, "%month", String( t.month)); - str :=replace(str, "%MON", string0(t.month, l=2)); - str :=replace(str, "%day", String( t.day)); - str :=replace(str, "%D", string0(t.day, l=2)); - str :=replace(str, "%hour", String( t.hour)); - str :=replace(str, "%H", string0(t.hour, l=2)); - str :=replace(str, "%minute", String( t.minute)); - str :=replace(str, "%MIN", string0(t.minute, l=2)); - str :=replace(str, "%second", String( t.second)); - str :=replace(str, "%S", string0(t.second, l=2)); - str :=replace(str, "%millisecond", String( t.millisecond)); - str :=replace(str, "%MS", string0(t.millisecond, l=3)); + str :=replace(format, "%%", "%"); + str :=replace(str, "%y", string0(mod(t.year, 100), l=2)); + str :=replace(str, "%Y", string0(t.year, l=4)); + str :=replace(str, "%m", string0(t.month, l=2)); + str :=replace(str, "%d", string0(t.day, l=2)); + str :=replace(str, "%H", string0(t.hour, l=2)); + str :=replace(str, "%M", string0(t.minute, l=2)); + str :=replace(str, "%S", string0(t.second, l=2)); + str :=replace(str, "%L", string0(t.millisecond, l=3)); + str :=replace(str, "%a", shortWeekDays[dayOfWeek(t.year, t.month, t.day)]); + str :=replace(str, "%A", weekDays[dayOfWeek(t.year, t.month, t.day)]); + str :=replace(str, "%b", shortMonths[t.month]); + str :=replace(str, "%B", months[t.month]); + annotation (Documentation(info=" + +

Syntax

+
+
+String(t)
+String(t, format)
+
+
+ +

Description

+

+ The input value \"t\" of type DateTime is converted to a string. +

+

+ The content of the output string can be controlled + via the \"format\" string by setting one or more of the conversion specifiers listed below. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SpecifierMeaningExamples
%yyear without century01, 02, ... 99
%Yyear with century2001, 2002, 2099
%babbreviated month nameJan, Feb
%Bfull month nameJanuary, February
%mmonth as number, zero padded to length 201, 02, ... 12
%aabbreviated weekdayMon, Tue
%Afull name of weekdayMonday, Tuesday
%dday of month, zero padded to length 201, 02, ... 31
%Hhour, zero padded to length 200, 01, ... 24
%Mminute, zero padded to length 200, 01, ... 60
%Ssecond, zero padded to length 200, 01, ... 60
%%single percent character. Not fully supported. See the Limitations section below.%
+ +

Additionally the following conversion specifiers are supported, which are not part of the C standard.

+ + + + + + + + + + + +
SpecifierMeaningExamples
%Lmillisecond, zero padded to length 3000, 001, ... 999
+ +

Example

+
+
+import Modelica.Utilities.Time.DateTime;
+dt = DateTime(2020, 12, 24, 00, 01, 02, 003);
+
+String(dt)                                       // = \"2020-12-24 00:01:02\"
+String(dt, \"\"%a, %b. %d %Y, %H:%M:%S\"\")          // = \"Thu, Dec. 24 2020, 00:01:02\"
+String(dt, format=\"%A, %d. %B %y, %H:%M:%S.%L\")  // = \"Thursday, 24. December 20, 00:01:02.003\"
+
+
+ +

Limitations

+

+ This function uses simple string replace methods to exchange the conversion specifiers with the appropriate values. +

+

+ When additional % characters are included in the format string (via %%) problems can occur, like shown below. +

+
+
+// ANTI-EXAMPLE - do not use
+String(dt, format=\"%%b\")  // Should give \"%b\", but gives \"Dec.\" instead
+
+
+ +")); end formated; end 'String'; diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index d721a7c903..16e055a067 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -652,8 +652,16 @@ extends Modelica.Icons.ExamplesPackage; act_s :=String(t3); assert(ref_s==act_s, "default string formating failed. \n"+ref_s+" <> "+act_s); - ref_s :="0h 0min 0.001s"; - act_s :=String(t3, format="%hourh %minutemin %second.%MSs"); + ref_s :="Monday, 06. April 20, 13:01:07.999"; + act_s :=String(DateTime(2020, 4, 6, 13, 01, 7, 999), format="%A, %d. %B %y, %H:%M:%S.%L"); + assert(ref_s==act_s, "custom string formating 1 failed. \n"+ref_s+" <> "+act_s); + + ref_s :="Tue, Apr. 07 2020, 01:11:17"; + act_s :=String(DateTime(2020, 4, 7, 01, 11, 17, 999), format="%a, %b. %d %Y, %H:%M:%S"); + assert(ref_s==act_s, "custom string formating 1 failed. \n"+ref_s+" <> "+act_s); + + ref_s :="% Tue, %Apr"; + act_s :=String(DateTime(2020, 4, 7, 01, 11, 17, 999), format="%% %a, %%%b"); assert(ref_s==act_s, "custom string formating 1 failed. \n"+ref_s+" <> "+act_s); // == From d02886d1d99d272df2c557ac0eebe60f01a53fd7 Mon Sep 17 00:00:00 2001 From: mkr7 Date: Thu, 9 Apr 2020 14:20:16 +0200 Subject: [PATCH 18/66] Align string formatting for Duration with DateTime --- Modelica/Utilities/Time.mo | 133 +++++++++++++++++++++++++++++++++++-- ModelicaTest/Utilities.mo | 10 ++- 2 files changed, 136 insertions(+), 7 deletions(-) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 3cf50c2c15..34f4fd3230 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -828,7 +828,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead t2 :=t; - if not contains(format, "%days") and not contains(format, "%D") then + if not contains(format, "%days") and not contains(format, "%d") then t2.hours :=t2.hours + t2.days*24; end if; @@ -836,7 +836,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead t2.minutes :=t2.minutes + t2.hours*60; end if; - if not contains(format, "%minutes") and not contains(format, "%MIN") then + if not contains(format, "%minutes") and not contains(format, "%M") then t2.seconds :=t2.seconds + t2.minutes*60; end if; @@ -844,17 +844,138 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead t2.milliseconds :=t2.milliseconds + t2.seconds*1000; end if; - str :=replace(format, "%days", String( t2.days)); - str :=replace(str, "%D", string0(t2.days, l=2)); + str :=replace(format, "%%", "%"); + str :=replace(str, "%days", String( t2.days)); + str :=replace(str, "%d", string0(t2.days, l=2)); str :=replace(str, "%hours", String( t2.hours)); str :=replace(str, "%H", string0(t2.hours, l=2)); str :=replace(str, "%minutes", String( t2.minutes)); - str :=replace(str, "%MIN", string0(t2.minutes, l=2)); + str :=replace(str, "%M", string0(t2.minutes, l=2)); str :=replace(str, "%seconds", String( t2.seconds)); str :=replace(str, "%S", string0(t2.seconds, l=2)); str :=replace(str, "%milliseconds", String( t2.milliseconds)); - str :=replace(str, "%MS", string0(t2.milliseconds, l=3)); + str :=replace(str, "%L", string0(t2.milliseconds, l=3)); + annotation (Documentation(info=" + +

Syntax

+
+
+String(t)
+String(t, format)
+
+
+ +

Description

+

+ The input value \"t\" of type Duration is converted to a string. +

+

+ The content of the output string can be controlled + via the \"format\" string by setting one or more of the conversion specifiers listed below. +

+

+ If higher time value are not included, they are added to the next lower time value. + If e.g. days are not part of the format string, but hours, the number of days will be converted + to hours and added to the hours value. +

+

+ If lower time values are not included, they are neglected. + If e.g. only days and hours are part of the format string, minutes, seconds and milliseconds will be ignored. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
SpecifierMeaningExamples
%dnumber of days, zero padded to length 201, 02, ...
%daysnumber of days as simple decimal1, 2, ...
%Hnumber of hours, zero padded to length 200, 01, ...
%hoursnumber of hours as simple decimal0, 1, ...
%Mnumber of minutes, zero padded to length 200, 01, ...
%minutesnumber of minutes as simple decimal0, 1, ...
%Snumber of seconds, zero padded to length 200, 01, ...
%secondsnumber of seconds as simple decimal0, 1, ...
%Lnumber of milliseconds, zero padded to length 300, 01, ...
%millisecondsnumber of milliseconds as simple decimal0, 1, ...
%%single percent character. Not fully supported. See the Limitations section below.%
+ +

Example

+
+
+import Modelica.Utilities.Time.Duration;
+d = Duration(days=1, hour=2, minutes=3, seconds=4, milliseconds=5);
+
+String(d)                        // = \"1d 2h 3min 4s 5ms\"
+String(d, \"%hoursh %secondss\")   // = \"26h 184s\" (days are included in hours, minutes in seconds, ms stripped off)
+String(d, format=\"%Hh %S.%L''\")  // = \"26h 184.005''\" (days are included in hours, minutes in seconds)
+String(d, format=\"%days'\")          // = \"1\" (only full days are shown, rest is stripped off)
+
+
+ +

Limitations

+

+ This function uses simple string replace methods to exchange the conversion specifiers with the appropriate values. +

+

+ When additional % characters are included in the format string (via %%) problems can occur, like shown below. +

+ +
+
+// ANTI-EXAMPLE - do not use
+String(d, format=\"%%days\")  // Should give \"%days\", but gives \"1\" instead
+
+
+ +")); end formated; end 'String'; diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index 16e055a067..d80dbb6744 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -742,9 +742,17 @@ extends Modelica.Icons.ExamplesPackage; assert(ref_s==act_s, "default string formating failed. Got "+ref_s+"<>"+act_s); ref_s :="25h 01' 01.001''"; - act_s :=String(d1, format="%Hh %MIN' %S.%MS''"); + act_s :=String(d1, format="%Hh %M' %S.%L''"); assert(ref_s==act_s, "custom string formating 1 failed. \n"+ref_s+" <> "+act_s); + ref_s :="25h %01'% 1''"; + act_s :=String(d1, format="%Hh %%%M'%% %seconds''"); + assert(ref_s==act_s, "custom string formating 2 failed. \n"+ref_s+" <> "+act_s); + + ref_s :="25h 61s"; + act_s :=String(d1, format="%Hh %Ss"); + assert(ref_s==act_s, "custom string formating 3 failed. \n"+ref_s+" <> "+act_s); + // == assert(d1==d1, "d1==d1 failed"); From d64accb4d72bc2f0acbc84a301cd5c8026654000 Mon Sep 17 00:00:00 2001 From: mkr7 Date: Thu, 9 Apr 2020 14:43:44 +0200 Subject: [PATCH 19/66] Add class comments to DateTime and all its operators --- Modelica/Utilities/Time.mo | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 34f4fd3230..6be50a1f17 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -185,7 +185,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] final constant String months[12] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} "Array of month names"; final constant String shortMonths[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"} "Array of abbreviated month names"; - operator record DateTime + operator record DateTime "DateTime record with several constructors and overloaded operators" extends Modelica.Icons.Record; Integer millisecond(min=0, max=999) "Millisecond" annotation(absoluteValue=true); @@ -196,12 +196,12 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] Integer month(min=1, max=12) "Month" annotation(absoluteValue=true); Integer year "Year" annotation(absoluteValue=true); - encapsulated operator 'constructor' + encapsulated operator 'constructor' "Available constructors" import Modelica.Utilities.Time.DateTime; import Modelica.Icons; extends Icons.FunctionsPackage; - function fromReadable + function fromReadable "Create DateTime from human readable format" extends Icons.Function; input Integer year "Year"; @@ -218,7 +218,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] end fromReadable; - function fromSystemTime "Get system time as elapsed seconds since custom epoch year" + function fromSystemTime "Create DateTime from current system time" import Modelica.Utilities.Internal.Time.getTime; extends Icons.Function; @@ -239,7 +239,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] end fromSystemTime; - function fromEpoch + function fromEpoch "Create DateTime from elapsed seconds since reference year" import Modelica.Utilities.Internal.Time.getTime; import Modelica.Math.nearestInteger; import Modelica.Utilities.Time.isLeapYear; @@ -331,7 +331,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] import Modelica.Icons; extends Icons.FunctionsPackage; - function formated + function formated "Use a subset of C strftime() conversion specifiers to format a DateTime record as string" extends Icons.Function; import Modelica.Utilities.Internal.Time.dayOfWeek; @@ -501,7 +501,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead end 'String'; - encapsulated operator function '==' + encapsulated operator function '==' "Check equality of two DateTime objects" import Modelica.Utilities.Time.DateTime; import Modelica.Icons; extends Icons.Function; @@ -521,7 +521,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead end '=='; - encapsulated operator function '<>' + encapsulated operator function '<>' "Check inequality of two DateTime objects" import Modelica.Utilities.Time.DateTime; import Modelica.Icons; extends Icons.Function; @@ -534,14 +534,14 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead result := not t1 == t2; end '<>'; - encapsulated operator function '>' + encapsulated operator function '>' "Check if DateTime t1 is later as t2" import Modelica.Utilities.Time.DateTime; import Modelica.Icons; extends Icons.Function; input DateTime t1; input DateTime t2; - output Boolean result "= t1 <> t2"; + output Boolean result "= t1 > t2"; algorithm if t1.year > t2.year then @@ -575,7 +575,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead end if; end '>'; - encapsulated operator function '>=' + encapsulated operator function '>=' "Check if DateTime t1 is equal to t2 or later" import Modelica.Utilities.Time.DateTime; import Modelica.Icons; extends Icons.Function; @@ -589,7 +589,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead end '>='; - encapsulated operator function '<' + encapsulated operator function '<' "Check if DateTime t1 is earlier as t2" import Modelica.Utilities.Time.DateTime; import Modelica.Icons; extends Icons.Function; @@ -603,7 +603,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead end '<'; - encapsulated operator function '<=' + encapsulated operator function '<=' "Check if DateTime t1 is equal to t2 or earlier" import Modelica.Utilities.Time.DateTime; import Modelica.Icons; extends Icons.Function; @@ -623,7 +623,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead extends Icons.FunctionsPackage; - function subtract "Subtract two durations element wise" + function subtract "Return time delta between t2 and t1 as Duration" extends Icons.Function; import Modelica.Utilities.Time.Duration; @@ -639,7 +639,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead end '-'; - encapsulated function epoch "Convert time to elapsed seconds since custom epoch year" + encapsulated function epoch "Convert DateTime to elapsed seconds since custom epoch year" import Modelica.Utilities.Time.DateTime; import Modelica.Utilities.Time.isLeapYear; import Modelica.Icons; From 3cd6124b032bdef307a1bddf670822c990dc7b0a Mon Sep 17 00:00:00 2001 From: mkr7 Date: Thu, 9 Apr 2020 15:01:01 +0200 Subject: [PATCH 20/66] Add class comments to Duration and all its operators --- Modelica/Utilities/Time.mo | 48 ++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 6be50a1f17..749d406aa6 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -689,7 +689,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead end DateTime; - operator record Duration + operator record Duration "Duration record with several constructors and overloaded operators" extends Modelica.Icons.Record; Integer days "Days"; @@ -698,12 +698,12 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead Integer seconds "Seconds"; Integer milliseconds "Milliseconds"; - encapsulated operator 'constructor' + encapsulated operator 'constructor' "Available constructors" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.FunctionsPackage; - function fromInput + function fromInput "Create Duration field by field from user input" extends Icons.Function; input Integer days=0 "Days"; @@ -715,14 +715,14 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead algorithm end fromInput; - function fromDateTimes + function fromDateTimes "Create Duration from two DateTime records" import Modelica.Utilities.Time.DateTime; import Modelica.Math.nearestInteger; extends Icons.Function; input DateTime t1 "Start time"; input DateTime t2 "End time"; - output Duration t; + output Duration t "= t2 - t1"; protected Real diff; @@ -759,7 +759,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead end fromDateTimes; - function fromSeconds "Create duration record from seconds, rounding to the third decimal" + function fromSeconds "Create duration record from total amount of seconds, rounding to the third decimal" extends Icons.Function; import Modelica.Math.nearestInteger; @@ -804,7 +804,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead import Modelica.Icons; extends Icons.FunctionsPackage; - function formated "Convert duration to string, using string replace" + function formated "Convert duration to string, using C inspired conversion specifier characters" import Modelica.Utilities.Strings.contains; extends Icons.Function; @@ -980,7 +980,7 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead end 'String'; - encapsulated operator function '==' + encapsulated operator function '==' "Check equality of two Duration objects by normalizing them" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.Function; @@ -1002,7 +1002,7 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead end '=='; - encapsulated operator function '<>' + encapsulated operator function '<>' "Check inequality of two Duration objects by normalizing them" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.Function; @@ -1015,20 +1015,20 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead result := not t1 == t2; end '<>'; - encapsulated operator function '>' + encapsulated operator function '>' "Check if Duration t1 is larger than t2" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.Function; input Duration t1; input Duration t2; - output Boolean result "= t1 <> t2"; + output Boolean result "= t1 > t2"; algorithm result :=Duration.inSeconds(t1) > Duration.inSeconds(t2); end '>'; - encapsulated operator function '>=' + encapsulated operator function '>=' "Check if Duration t1 is equal to t2 or larger" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.Function; @@ -1042,7 +1042,7 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead end '>='; - encapsulated operator function '<' + encapsulated operator function '<' "Check if Duration t1 is smaller than t2" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.Function; @@ -1056,7 +1056,7 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead end '<'; - encapsulated operator function '<=' + encapsulated operator function '<=' "Check if Duration t1 is equal to t2 or smaller" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.Function; @@ -1070,7 +1070,7 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead end '<='; - encapsulated operator function '+' + encapsulated operator function '+' "Add Durations t1 and t2 and normalize the sum" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.Function; @@ -1095,7 +1095,7 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead extends Icons.FunctionsPackage; - function subtract "Subtract two durations element wise" + function subtract "Subtract two durations element wise and normalize the difference" extends Icons.Function; input Duration t1; @@ -1112,7 +1112,7 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead end subtract; - function negate "Unary minus (multiply duration values by -1)" + function negate "Unary minus (multiply all duration values by -1)" extends Icons.Function; input Duration t; @@ -1160,13 +1160,14 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead end multiply2; end '*'; - encapsulated operator '/' "Divide a duration by a real" + encapsulated operator '/' "Division" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.FunctionsPackage; - function divide "Divide a duration by a real. The first milliseconds value can vary by 1 (due to rounding in the fromSeconds constructor)" + function divide + "Divide a duration by a real. The first milliseconds value can vary by 1 (due to rounding in the fromSeconds constructor)" extends Icons.Function; input Duration t; @@ -1198,7 +1199,7 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead extends Icons.Function; input Duration t "Value to convert"; - output Integer[5] t_vec "Elapsed time as vector {days, hours, minutes, seconds, milliseconds}"; + output Integer[5] t_vec "Duration as vector {days, hours, minutes, seconds, milliseconds}"; algorithm @@ -1206,12 +1207,12 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead end asVector; - encapsulated function avg "Return average duration for a vector of durations" + encapsulated function avg "Return Duration with averaged values for a vector of durations" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.Function; - input Duration t_vec[:]; + input Duration t_vec[:] "Vector of duration"; output Duration t_avg "Average duration"; protected @@ -1240,7 +1241,8 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead end inSeconds; - encapsulated function normalize "Reformat duration with usual maximum values for milliseconds, seconds, minutes and hours and carryover to next bigger unit" + encapsulated function normalize + "Recompute duration with usual maximum values for milliseconds, seconds, minutes and hours" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.Function; From 346bd710f2bd428a8da80adb900c84825105212d Mon Sep 17 00:00:00 2001 From: mkr7 Date: Thu, 9 Apr 2020 16:47:50 +0200 Subject: [PATCH 21/66] Use dt as variable name for DateTimes --- Modelica/Utilities/Time.mo | 148 ++++++++++++++++++------------------- ModelicaTest/Utilities.mo | 40 +++++----- 2 files changed, 94 insertions(+), 94 deletions(-) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 749d406aa6..d9b32190e7 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -212,7 +212,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] input Integer second "Second"; input Integer millisecond "Millisecond"; - output DateTime t(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year); + output DateTime dt(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year); algorithm @@ -222,7 +222,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] import Modelica.Utilities.Internal.Time.getTime; extends Icons.Function; - output DateTime t; + output DateTime dt; protected Integer millisecond "Millisecond"; @@ -235,7 +235,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] algorithm (millisecond, second, minute, hour, day, month, year) := getTime(); - t :=DateTime(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year); + dt :=DateTime(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year); end fromSystemTime; @@ -248,7 +248,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] input Real seconds "Elapsed seconds since epoch_year"; input Integer epoch_year = 1970 "Reference year"; - output DateTime t; + output DateTime dt; protected Integer[2,12] days_passed = {{31,59,90,120,151,181,212,243,273,304,334,365}, @@ -320,7 +320,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] // get day day :=if month > 1 then day_of_year - days_passed[j,month-1] else day_of_year; - t :=DateTime(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year); + dt :=DateTime(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year); end fromEpoch; end 'constructor'; @@ -340,7 +340,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] import Modelica.Utilities.Time.months; import Modelica.Utilities.Time.shortMonths; - input DateTime t; + input DateTime dt; input String format = "%Y-%m-%d %H:%M:%S"; output String str; @@ -356,31 +356,31 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] algorithm str :=replace(format, "%%", "%"); - str :=replace(str, "%y", string0(mod(t.year, 100), l=2)); - str :=replace(str, "%Y", string0(t.year, l=4)); - str :=replace(str, "%m", string0(t.month, l=2)); - str :=replace(str, "%d", string0(t.day, l=2)); - str :=replace(str, "%H", string0(t.hour, l=2)); - str :=replace(str, "%M", string0(t.minute, l=2)); - str :=replace(str, "%S", string0(t.second, l=2)); - str :=replace(str, "%L", string0(t.millisecond, l=3)); - str :=replace(str, "%a", shortWeekDays[dayOfWeek(t.year, t.month, t.day)]); - str :=replace(str, "%A", weekDays[dayOfWeek(t.year, t.month, t.day)]); - str :=replace(str, "%b", shortMonths[t.month]); - str :=replace(str, "%B", months[t.month]); + str :=replace(str, "%y", string0(mod(dt.year, 100), l=2)); + str :=replace(str, "%Y", string0(dt.year, l=4)); + str :=replace(str, "%m", string0(dt.month, l=2)); + str :=replace(str, "%d", string0(dt.day, l=2)); + str :=replace(str, "%H", string0(dt.hour, l=2)); + str :=replace(str, "%M", string0(dt.minute, l=2)); + str :=replace(str, "%S", string0(dt.second, l=2)); + str :=replace(str, "%L", string0(dt.millisecond, l=3)); + str :=replace(str, "%a", shortWeekDays[dayOfWeek(dt.year, dt.month, dt.day)]); + str :=replace(str, "%A", weekDays[dayOfWeek(dt.year, dt.month, dt.day)]); + str :=replace(str, "%b", shortMonths[dt.month]); + str :=replace(str, "%B", months[dt.month]); annotation (Documentation(info="

Syntax

-String(t)
-String(t, format)
+String(dt)
+String(dt, format)
 

Description

- The input value \"t\" of type DateTime is converted to a string. + The input value \"dt\" of type DateTime is converted to a string.

The content of the output string can be controlled @@ -506,18 +506,18 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead import Modelica.Icons; extends Icons.Function; - input DateTime t1; - input DateTime t2; - output Boolean result "= t1 == t2"; + input DateTime dt1; + input DateTime dt2; + output Boolean result "= dt1 == dt2"; algorithm - result := t1.year == t2.year and - t1.month == t2.month and - t1.day == t2.day and - t1.hour == t2.hour and - t1.minute == t2.minute and - t1.second == t2.second and - t1.millisecond == t2.millisecond; + result := dt1.year == dt2.year and + dt1.month == dt2.month and + dt1.day == dt2.day and + dt1.hour == dt2.hour and + dt1.minute == dt2.minute and + dt1.second == dt2.second and + dt1.millisecond == dt2.millisecond; end '=='; @@ -526,43 +526,43 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead import Modelica.Icons; extends Icons.Function; - input DateTime t1; - input DateTime t2; - output Boolean result "= t1 <> t2"; + input DateTime dt1; + input DateTime dt2; + output Boolean result "= dt1 <> dt2"; algorithm - result := not t1 == t2; + result := not dt1 == dt2; end '<>'; - encapsulated operator function '>' "Check if DateTime t1 is later as t2" + encapsulated operator function '>' "Check if DateTime dt1 is later as dt2" import Modelica.Utilities.Time.DateTime; import Modelica.Icons; extends Icons.Function; - input DateTime t1; - input DateTime t2; - output Boolean result "= t1 > t2"; + input DateTime dt1; + input DateTime dt2; + output Boolean result "= dt1 > dt2"; algorithm - if t1.year > t2.year then + if dt1.year > dt2.year then result :=true; else - if t1.year==t2.year and t1.month > t2.month then + if dt1.year==dt2.year and dt1.month > dt2.month then result :=true; else - if t1.month==t2.month and t1.day > t2.day then + if dt1.month==dt2.month and dt1.day > dt2.day then result :=true; else - if t1.day==t2.day and t1.hour > t2.hour then + if dt1.day==dt2.day and dt1.hour > dt2.hour then result :=true; else - if t1.hour==t2.hour and t1.minute > t2.minute then + if dt1.hour==dt2.hour and dt1.minute > dt2.minute then result :=true; else - if t1.minute==t2.minute and t1.second > t2.second then + if dt1.minute==dt2.minute and dt1.second > dt2.second then result :=true; else - if t1.second==t2.second and t1.millisecond > t2.millisecond then + if dt1.second==dt2.second and dt1.millisecond > dt2.millisecond then result :=true; else result :=false; @@ -575,45 +575,45 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead end if; end '>'; - encapsulated operator function '>=' "Check if DateTime t1 is equal to t2 or later" + encapsulated operator function '>=' "Check if DateTime dt1 is equal to dt2 or later" import Modelica.Utilities.Time.DateTime; import Modelica.Icons; extends Icons.Function; - input DateTime t1; - input DateTime t2; - output Boolean result "= t1 >= t2"; + input DateTime dt1; + input DateTime dt2; + output Boolean result "= dt1 >= dt2"; algorithm - result :=t1 == t2 or t1 > t2; + result :=dt1 == dt2 or dt1 > dt2; end '>='; - encapsulated operator function '<' "Check if DateTime t1 is earlier as t2" + encapsulated operator function '<' "Check if DateTime dt1 is earlier as dt2" import Modelica.Utilities.Time.DateTime; import Modelica.Icons; extends Icons.Function; - input DateTime t1; - input DateTime t2; - output Boolean result "= t1 < t2"; + input DateTime dt1; + input DateTime dt2; + output Boolean result "= dt1 < dt2"; algorithm - result := not t1 == t2 and t2 > t1; + result := not dt1 == dt2 and dt2 > dt1; end '<'; - encapsulated operator function '<=' "Check if DateTime t1 is equal to t2 or earlier" + encapsulated operator function '<=' "Check if DateTime dt1 is equal to dt2 or earlier" import Modelica.Utilities.Time.DateTime; import Modelica.Icons; extends Icons.Function; - input DateTime t1; - input DateTime t2; - output Boolean result "= t1 <= t2"; + input DateTime dt1; + input DateTime dt2; + output Boolean result "= dt1 <= dt2"; algorithm - result := t1 == t2 or t1 < t2; + result := dt1 == dt2 or dt1 < dt2; end '<='; @@ -623,17 +623,17 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead extends Icons.FunctionsPackage; - function subtract "Return time delta between t2 and t1 as Duration" + function subtract "Return time delta between dt2 and dt1 as Duration" extends Icons.Function; import Modelica.Utilities.Time.Duration; - input DateTime t1; - input DateTime t2; - output Duration result "= t1 - t2"; + input DateTime dt1; + input DateTime dt2; + output Duration result "= dt1 - dt2"; algorithm - result := Duration.'constructor'.fromDateTimes(t2, t1); + result := Duration.'constructor'.fromDateTimes(dt2, dt1); end subtract; @@ -645,7 +645,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead import Modelica.Icons; extends Icons.Function; - input DateTime t; + input DateTime dt; input Integer epoch_year = 1970 "Reference year"; output Real seconds "Elapsed seconds since epoch_year"; @@ -657,13 +657,13 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead Integer j; // Aux variables for shorter access in code - Integer millisecond=t.millisecond "Millisecond"; - Integer second=t.second "Second"; - Integer minute=t.minute "Minute"; - Integer hour=t.hour "Hour"; - Integer day=t.day "Day"; - Integer month=t.month "Month"; - Integer year=t.year "Year"; + Integer millisecond=dt.millisecond "Millisecond"; + Integer second=dt.second "Second"; + Integer minute=dt.minute "Minute"; + Integer hour=dt.hour "Hour"; + Integer day=dt.day "Day"; + Integer month=dt.month "Month"; + Integer year=dt.year "Year"; algorithm // get leap years from year 0 until the chosen epoch year diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index d80dbb6744..bc1a85dbb5 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -450,7 +450,7 @@ extends Modelica.Icons.ExamplesPackage; Real act_r, ref_r; String act_s, ref_s; DateTime act_dt, ref_dt; - DateTime t1, t2, t3; + DateTime dt1, dt2, dt3; Integer _ "Dummy to swallow return value"; algorithm @@ -629,27 +629,27 @@ extends Modelica.Icons.ExamplesPackage; assert(ref_dt==act_dt, "constructor test failed (leap regular year before epoch)"); assert(ref_r==act_r, "conversion to epoch failed (leap regular year before epoch)"); - // compare two DateTime records created from system time. t2 should be a few seconds later than t1 - t1 :=DateTime(); + // compare two DateTime records created from system time. dt2 should be a few seconds later than dt1 + dt1 :=DateTime(); _ :=Modelica.Utilities.System.command("sleep 1") "Sleep 1s on linux"; _ :=Modelica.Utilities.System.command("ping -n 2 127.0.0.1 > NUL") "Sleep 1s on windows"; - t2 :=DateTime(); + dt2 :=DateTime(); - assert( (t2 > t1) and (DateTime.epoch(t2)-DateTime.epoch(t1) < 5), - "constructor from system time failed (t1 is younger than t2)"); + assert( (dt2 > dt1) and (DateTime.epoch(dt2)-DateTime.epoch(dt1) < 5), + "constructor from system time failed (dt1 is younger than dt2)"); // ---------------- // // operator tests // // ---------------- // - t1 := DateTime(2019, 12, 31, 23, 59, 59, 999); - t2 := DateTime(2020, 01, 01, 00, 00, 00, 0); - t3 := DateTime(2020, 01, 01, 00, 00, 00, 1); + dt1 := DateTime(2019, 12, 31, 23, 59, 59, 999); + dt2 := DateTime(2020, 01, 01, 00, 00, 00, 0); + dt3 := DateTime(2020, 01, 01, 00, 00, 00, 1); // 'String'.formated ref_s :="2020-01-01 00:00:00"; - act_s :=String(t3); + act_s :=String(dt3); assert(ref_s==act_s, "default string formating failed. \n"+ref_s+" <> "+act_s); ref_s :="Monday, 06. April 20, 13:01:07.999"; @@ -665,28 +665,28 @@ extends Modelica.Icons.ExamplesPackage; assert(ref_s==act_s, "custom string formating 1 failed. \n"+ref_s+" <> "+act_s); // == - assert(t1==t1, "t1==t1 failed"); + assert(dt1==dt1, "dt1==dt1 failed"); // <> - assert(t1<>t2, "t1<>t2 failed"); + assert(dt1<>dt2, "dt1<>dt2 failed"); // > - assert(t2>t1, "t2>t1 failed"); + assert(dt2>dt1, "dt2>dt1 failed"); // >= - assert(t2>=t1, "t2>=t1 failed"); - assert(t1>=t1, "t1>=t1 failed"); + assert(dt2>=dt1, "dt2>=dt1 failed"); + assert(dt1>=dt1, "dt1>=dt1 failed"); // < - assert(t2 Date: Thu, 9 Apr 2020 17:04:20 +0200 Subject: [PATCH 22/66] Use d as variable name for Durations .... and make an update regarding DateTimes as dt in Duration as well --- Modelica/Utilities/Time.mo | 222 ++++++++++++++++++------------------- ModelicaTest/Utilities.mo | 4 +- 2 files changed, 113 insertions(+), 113 deletions(-) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index d9b32190e7..883817ab94 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -711,7 +711,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead input Integer minutes=0 "Minutes"; input Integer seconds=0 "Seconds"; input Integer milliseconds=0 "Milliseconds"; - output Duration t(days=days, hours=hours, minutes=minutes, seconds=seconds, milliseconds=milliseconds); + output Duration d(days=days, hours=hours, minutes=minutes, seconds=seconds, milliseconds=milliseconds); algorithm end fromInput; @@ -720,15 +720,15 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead import Modelica.Math.nearestInteger; extends Icons.Function; - input DateTime t1 "Start time"; - input DateTime t2 "End time"; - output Duration t "= t2 - t1"; + input DateTime dt1 "Start time"; + input DateTime dt2 "End time"; + output Duration d "= dt2 - dt1"; protected Real diff; Integer sign_; Real e1, e2; - DateTime t_tmp; + DateTime dt_tmp; Integer days "Elapsed days"; Integer hours "Elapsed hours"; Integer minutes "Elapsed minutes"; @@ -736,10 +736,10 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead Integer milliseconds "Elapsed milliseconds"; algorithm - t_tmp :=t2; + dt_tmp :=dt2; - e1 :=DateTime.epoch(t1, epoch_year=t1.year); - e2 :=DateTime.epoch(t_tmp, epoch_year=t1.year); + e1 :=DateTime.epoch(dt1, epoch_year=dt1.year); + e2 :=DateTime.epoch(dt_tmp, epoch_year=dt1.year); diff :=abs(e2 - e1); sign_ :=sign(e2 - e1); @@ -755,7 +755,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead seconds := sign_*seconds; milliseconds := sign_*milliseconds; - t :=Duration(days=days, hours=hours, minutes=minutes, seconds=seconds, milliseconds=milliseconds); + d :=Duration(days=days, hours=hours, minutes=minutes, seconds=seconds, milliseconds=milliseconds); end fromDateTimes; @@ -764,7 +764,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead import Modelica.Math.nearestInteger; input Real totalSeconds "Duration in seconds. Decimal place is converted to milliseconds"; - output Duration t "Duration with input converted to seconds and milliseconds"; + output Duration d "Duration with input converted to seconds and milliseconds"; protected Integer days, hours, minutes, seconds, milliseconds; @@ -788,7 +788,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead days := carryover; - t :=Duration( + d :=Duration( days=days, hours=hours, minutes=minutes, @@ -808,12 +808,12 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead import Modelica.Utilities.Strings.contains; extends Icons.Function; - input Duration t; + input Duration d; input String format = "%daysd %hoursh %minutesmin %secondss %millisecondsms"; output String str; protected - Duration t2; + Duration d2; encapsulated function string0 "Create string with minimum length, filled with 0" import Modelica.Utilities.Strings.replace; @@ -826,49 +826,49 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead algorithm - t2 :=t; + d2 :=d; if not contains(format, "%days") and not contains(format, "%d") then - t2.hours :=t2.hours + t2.days*24; + d2.hours :=d2.hours + d2.days*24; end if; if not contains(format, "%hours") and not contains(format, "%H") then - t2.minutes :=t2.minutes + t2.hours*60; + d2.minutes :=d2.minutes + d2.hours*60; end if; if not contains(format, "%minutes") and not contains(format, "%M") then - t2.seconds :=t2.seconds + t2.minutes*60; + d2.seconds :=d2.seconds + d2.minutes*60; end if; if not contains(format, "%seconds") and not contains(format, "%S") then - t2.milliseconds :=t2.milliseconds + t2.seconds*1000; + d2.milliseconds :=d2.milliseconds + d2.seconds*1000; end if; str :=replace(format, "%%", "%"); - str :=replace(str, "%days", String( t2.days)); - str :=replace(str, "%d", string0(t2.days, l=2)); - str :=replace(str, "%hours", String( t2.hours)); - str :=replace(str, "%H", string0(t2.hours, l=2)); - str :=replace(str, "%minutes", String( t2.minutes)); - str :=replace(str, "%M", string0(t2.minutes, l=2)); - str :=replace(str, "%seconds", String( t2.seconds)); - str :=replace(str, "%S", string0(t2.seconds, l=2)); - str :=replace(str, "%milliseconds", String( t2.milliseconds)); - str :=replace(str, "%L", string0(t2.milliseconds, l=3)); + str :=replace(str, "%days", String( d2.days)); + str :=replace(str, "%d", string0(d2.days, l=2)); + str :=replace(str, "%hours", String( d2.hours)); + str :=replace(str, "%H", string0(d2.hours, l=2)); + str :=replace(str, "%minutes", String( d2.minutes)); + str :=replace(str, "%M", string0(d2.minutes, l=2)); + str :=replace(str, "%seconds", String( d2.seconds)); + str :=replace(str, "%S", string0(d2.seconds, l=2)); + str :=replace(str, "%milliseconds", String( d2.milliseconds)); + str :=replace(str, "%L", string0(d2.milliseconds, l=3)); annotation (Documentation(info="

Syntax

-String(t)
-String(t, format)
+String(d)
+String(d, format)
 

Description

- The input value \"t\" of type Duration is converted to a string. + The input value \"d\" of type Duration is converted to a string.

The content of the output string can be controlled @@ -985,20 +985,20 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead import Modelica.Icons; extends Icons.Function; - input Duration t1; - input Duration t2; - output Boolean result "= t1 == t2"; + input Duration d1; + input Duration d2; + output Boolean result "= d1 == d2"; protected - Duration t1_norm = Duration.normalize(t1); - Duration t2_norm = Duration.normalize(t2); + Duration d1_norm = Duration.normalize(d1); + Duration d2_norm = Duration.normalize(d2); algorithm - result := t1_norm.days == t2_norm.days and - t1_norm.hours == t2_norm.hours and - t1_norm.minutes == t2_norm.minutes and - t1_norm.seconds == t2_norm.seconds and - t1_norm.milliseconds == t2_norm.milliseconds; + result := d1_norm.days == d2_norm.days and + d1_norm.hours == d2_norm.hours and + d1_norm.minutes == d2_norm.minutes and + d1_norm.seconds == d2_norm.seconds and + d1_norm.milliseconds == d2_norm.milliseconds; end '=='; @@ -1007,85 +1007,85 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead import Modelica.Icons; extends Icons.Function; - input Duration t1; - input Duration t2; - output Boolean result "= t1 <> t2"; + input Duration d1; + input Duration d2; + output Boolean result "= d1 <> d2"; algorithm - result := not t1 == t2; + result := not d1 == d2; end '<>'; - encapsulated operator function '>' "Check if Duration t1 is larger than t2" + encapsulated operator function '>' "Check if Duration d1 is larger than d2" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.Function; - input Duration t1; - input Duration t2; - output Boolean result "= t1 > t2"; + input Duration d1; + input Duration d2; + output Boolean result "= d1 > d2"; algorithm - result :=Duration.inSeconds(t1) > Duration.inSeconds(t2); + result :=Duration.inSeconds(d1) > Duration.inSeconds(d2); end '>'; - encapsulated operator function '>=' "Check if Duration t1 is equal to t2 or larger" + encapsulated operator function '>=' "Check if Duration d1 is equal to d2 or larger" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.Function; - input Duration t1; - input Duration t2; - output Boolean result "= t1 >= t2"; + input Duration d1; + input Duration d2; + output Boolean result "= d1 >= d2"; algorithm - result :=t1 == t2 or t1 > t2; + result :=d1 == d2 or d1 > d2; end '>='; - encapsulated operator function '<' "Check if Duration t1 is smaller than t2" + encapsulated operator function '<' "Check if Duration d1 is smaller than d2" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.Function; - input Duration t1; - input Duration t2; - output Boolean result "= t1 < t2"; + input Duration d1; + input Duration d2; + output Boolean result "= d1 < d2"; algorithm - result := not t1 == t2 and t2 > t1; + result := not d1 == d2 and d2 > d1; end '<'; - encapsulated operator function '<=' "Check if Duration t1 is equal to t2 or smaller" + encapsulated operator function '<=' "Check if Duration d1 is equal to d2 or smaller" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.Function; - input Duration t1; - input Duration t2; - output Boolean result "= t1 <= t2"; + input Duration d1; + input Duration d2; + output Boolean result "= d1 <= d2"; algorithm - result := t1 == t2 or t1 < t2; + result := d1 == d2 or d1 < d2; end '<='; - encapsulated operator function '+' "Add Durations t1 and t2 and normalize the sum" + encapsulated operator function '+' "Add Durations d1 and d2 and normalize the sum" import Modelica.Utilities.Time.Duration; import Modelica.Icons; extends Icons.Function; - input Duration t1; - input Duration t2; - output Duration result "= t1 + t2"; + input Duration d1; + input Duration d2; + output Duration result "= d1 + d2"; algorithm result := Duration.normalize(Duration( - days=t1.days + t2.days, - hours=t1.hours + t2.hours, - minutes=t1.minutes + t2.minutes, - seconds=t1.seconds + t2.seconds, - milliseconds=t1.milliseconds + t2.milliseconds)); + days=d1.days + d2.days, + hours=d1.hours + d2.hours, + minutes=d1.minutes + d2.minutes, + seconds=d1.seconds + d2.seconds, + milliseconds=d1.milliseconds + d2.milliseconds)); end '+'; @@ -1098,33 +1098,33 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead function subtract "Subtract two durations element wise and normalize the difference" extends Icons.Function; - input Duration t1; - input Duration t2; - output Duration result "= t1 - t2"; + input Duration d1; + input Duration d2; + output Duration result "= d1 - d2"; algorithm result := Duration.normalize(Duration( - days=t1.days - t2.days, - hours=t1.hours - t2.hours, - minutes=t1.minutes - t2.minutes, - seconds=t1.seconds - t2.seconds, - milliseconds=t1.milliseconds - t2.milliseconds)); + days=d1.days - d2.days, + hours=d1.hours - d2.hours, + minutes=d1.minutes - d2.minutes, + seconds=d1.seconds - d2.seconds, + milliseconds=d1.milliseconds - d2.milliseconds)); end subtract; function negate "Unary minus (multiply all duration values by -1)" extends Icons.Function; - input Duration t; - output Duration result "= -t1"; + input Duration d; + output Duration result "= -d1"; algorithm result := Duration( - days=-t.days, - hours=-t.hours, - minutes=-t.minutes, - seconds=-t.seconds, - milliseconds=-t.milliseconds); + days=-d.days, + hours=-d.hours, + minutes=-d.minutes, + seconds=-d.seconds, + milliseconds=-d.milliseconds); end negate; end '-'; @@ -1138,12 +1138,12 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead function multiply1 "Multiply a duration with a real (by converting the duration to seconds)" extends Icons.Function; - input Duration t; + input Duration d; input Real r; - output Duration result "= inSeconds(t)*r"; + output Duration result "= inSeconds(d)*r"; algorithm - result := Duration(totalSeconds=Duration.inSeconds(t)*r); + result := Duration(totalSeconds=Duration.inSeconds(d)*r); end multiply1; @@ -1151,11 +1151,11 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead extends Icons.Function; input Real r; - input Duration t; - output Duration result "= inSeconds(t)*r"; + input Duration d; + output Duration result "= inSeconds(d)*r"; algorithm - result := Duration(totalSeconds=r*Duration.inSeconds(t)); + result := Duration(totalSeconds=r*Duration.inSeconds(d)); end multiply2; end '*'; @@ -1170,12 +1170,12 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead "Divide a duration by a real. The first milliseconds value can vary by 1 (due to rounding in the fromSeconds constructor)" extends Icons.Function; - input Duration t; + input Duration d; input Real r; - output Duration result "= inSeconds(t)/r"; + output Duration result "= inSeconds(d)/r"; algorithm - result := Duration(totalSeconds=Duration.inSeconds(t)/r); + result := Duration(totalSeconds=Duration.inSeconds(d)/r); end divide; @@ -1198,12 +1198,12 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead import Modelica.Icons; extends Icons.Function; - input Duration t "Value to convert"; - output Integer[5] t_vec "Duration as vector {days, hours, minutes, seconds, milliseconds}"; + input Duration d "Value to convert"; + output Integer[5] d_vec "Duration as vector {days, hours, minutes, seconds, milliseconds}"; algorithm - t_vec :={t.days, t.hours, t.minutes, t.seconds, t.milliseconds}; + d_vec :={d.days, d.hours, d.minutes, d.seconds, d.milliseconds}; end asVector; @@ -1212,19 +1212,19 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead import Modelica.Icons; extends Icons.Function; - input Duration t_vec[:] "Vector of duration"; - output Duration t_avg "Average duration"; + input Duration d_vec[:] "Vector of duration"; + output Duration d_avg "Average duration"; protected - Integer n = size(t_vec, 1); + Integer n = size(d_vec, 1); Real totalSeconds = 0; algorithm for i in 1:n loop - totalSeconds := totalSeconds + Duration.inSeconds(t_vec[i]); + totalSeconds := totalSeconds + Duration.inSeconds(d_vec[i]); end for; - t_avg := Duration(totalSeconds=totalSeconds/n); + d_avg := Duration(totalSeconds=totalSeconds/n); end avg; @@ -1233,11 +1233,11 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead import Modelica.Icons; extends Icons.Function; - input Duration t; + input Duration d; output Real totalSeconds "Elapsed seconds"; algorithm - totalSeconds :=t.milliseconds/1000 + t.seconds + 60*(t.minutes + 60*(t.hours + 24*t.days)); + totalSeconds :=d.milliseconds/1000 + d.seconds + 60*(d.minutes + 60*(d.hours + 24*d.days)); end inSeconds; @@ -1247,12 +1247,12 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead import Modelica.Icons; extends Icons.Function; - input Duration t "Duration"; - output Duration t_norm "Normalized duration"; + input Duration d "Duration"; + output Duration d_norm "Normalized duration"; algorithm - t_norm :=Duration(totalSeconds=Duration.inSeconds(t)); + d_norm :=Duration(totalSeconds=Duration.inSeconds(d)); end normalize; diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index bc1a85dbb5..826f6af6c4 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -728,11 +728,11 @@ extends Modelica.Icons.ExamplesPackage; act := Duration(totalSeconds=-3775412.501); assert(ref==act, "constructor test 1 failed (fromSeconds vs fromInput)"); - ref := Duration(t1=DateTime(2020, 1, 1, 1, 1, 1, 1), t2=DateTime(2020, 1, 1, 1, 1, 2, 500)); + ref := Duration(dt1=DateTime(2020, 1, 1, 1, 1, 1, 1), dt2=DateTime(2020, 1, 1, 1, 1, 2, 500)); act := Duration(totalSeconds=1.499); assert(ref==act, "constructor test 2 failed (fromSeconds vs fromDateTimes, positive result)"); - ref := Duration(t1=DateTime(2020, 1, 1, 1, 1, 2, 500), t2=DateTime(2020, 1, 1, 1, 1, 1, 1)); + ref := Duration(dt1=DateTime(2020, 1, 1, 1, 1, 2, 500), dt2=DateTime(2020, 1, 1, 1, 1, 1, 1)); act := Duration(totalSeconds=-1.499); assert(ref==act, "constructor test 3 failed (fromSeconds vs fromDateTimes, negative result)"); From 3cc5052209d7c1fdb6ddcb7b7981e550746fc5fd Mon Sep 17 00:00:00 2001 From: mkr7 Date: Thu, 9 Apr 2020 17:36:25 +0200 Subject: [PATCH 23/66] Add basic doc for DateTime --- Modelica/Utilities/Time.mo | 50 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 883817ab94..394c4ec500 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -687,6 +687,56 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead now:=DateTime.'constructor'.fromSystemTime(); end now; + annotation (Documentation(info=" + +

Syntax

+
+
+Time.DateTime();
+Time.DateTime(year, month, day, hour, minute, second, millisecond);
+Time.DateTime(seconds);
+Time.DateTime(seconds, epoch_year);
+
+
+ +

Description

+

+ The operator record DateTime stores the required information to address a specific point in time via date and time values. +

+

+ There are multiple constructors provided to create a DateTime element. See the examples below for details. +

+

+ DateTimes can be compared (==, <>, >, <, ≥, ≤), giving a boolean result and subtracted (-), giving a Duration. +

+

+ DateTimes can be converted to an epoch representation with the function DateTime.epoch(dt, epoch_year). +

+ +

Example

+ +The examples below demonstrate the different methods to create a DateTime record. + +
+
+import Modelica.Utilities.Time.DateTime;
+
+// Create DateTime records from the current system time
+DateTime();                              // create DateTime record using default constructor, which is fromSystemTime
+DateTime.'constructor'.fromSystemTime(); // explicit call of constructor
+DateTime.now();                          // convenience function for explicit call of constructor fromSystemTime
+
+// Create DateTime records manually by setting each field
+DateTime.'constructor'.fromReadable(2020, 01, 31, 14, 40, 50, 500); // explicit call of constructor
+DateTime(2020, 01, 31, 14, 40, 50, 500);                            // automatic selection of constructor fromReadable
+
+// Create DateTime records manually using the elapsed seconds since the given epoch year
+DateTime(seconds=1);                          // automatic selection of constructor fromEpoch
+DateTime.'constructor'.fromEpoch(1);          // explicit call of constructor. 1s passed since default epoch year 1970
+DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1000s passed since custom epoch year 2020
+
+
+")); end DateTime; operator record Duration "Duration record with several constructors and overloaded operators" From 6cabef726965ea456ea4033895fcb45cfe6ef80f Mon Sep 17 00:00:00 2001 From: mkr7 Date: Thu, 9 Apr 2020 18:01:34 +0200 Subject: [PATCH 24/66] Add basic doc for Duration --- Modelica/Utilities/Time.mo | 76 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 394c4ec500..764ff8f08c 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -1306,6 +1306,82 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead end normalize; + annotation (Documentation(info=" + +

Syntax

+
+
+Time.Duration();
+Time.Duration(days=0, hours=0, minutes=0, seconds=0, milliseconds=0);
+Time.Duration(dt1, dt2);
+Time.Duration(totalSeconds);
+
+
+ +

Description

+

+ The operator record Duration is used for elapsed time. + This can be the time between given by DateTime records + (i.e. by subtracting them) or a manually specified duration. +

+

+ There are multiple constructors provided to create a Duration element. + See the examples below for details. +

+ +

Here is a brief summary, what the Duration operator record is capable of:

+
    +
  • + Durations can be compared (==, <>, >, <, ≥, ≤), giving a boolean result. +
  • +
  • + Durations can be added, subtracted and negated (+, -), giving a Duration. +
  • +
  • + Durations can be multiplied and divided by scalars (*, /), giving a Duration. +
  • +
  • + The elements of a Duration can be returned as vector with Duration.asVector(). +
  • +
  • + For a vector of Durations, the average can be computed with Duration.avg(). +
  • +
  • + Durations can be expressed in seconds only, using Duration.inSeconds(). +
  • +
  • + Durations can be normalized with Duration.normalize(), + which ensures a representation with hours < 24, min < 60, etc.). +
  • +
+ +

Example

+ +The examples below demonstrate the different methods to create a Duration record. + +
+
+import Modelica.Utilities.Time.Duration;
+
+// Create Duration records from direct input
+Duration();                                      // create Duration record using default constructor, which is fromInput
+Duration.'constructor'.fromInput(hours=10);      // explicit call of constructor
+Duration(days=1, seconds=100);                   // automatic selection of constructor fromInput
+
+// Create Duration records from two DateTimes
+import Modelica.Utilities.Time.DateTime;
+dt1 = DateTime.now();
+dt2 = DateTime.now();
+Duration.'constructor'.fromDateTimes(dt1, dt2);  // explicit call of constructor
+Duration(dt1, dt2);                              // automatic selection of constructor via data type
+Duration(dt1=dt1, dt2=dt2);                      // automatic selection of constructor via names
+
+// Create Duration records manually using the elapsed seconds since the given epoch year
+Duration.'constructor'.fromSeconds(1);           // explicit call of constructor
+Duration(totalSeconds=1.5);                      // automatic selection of constructor via name
+
+
+")); end Duration; annotation ( Documentation(info=" From 42c523746542a3d0e71b353d43275ec335b84313 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Thu, 9 Apr 2020 22:59:11 +0200 Subject: [PATCH 25/66] Fix lookup of package constant --- ModelicaTest/Utilities.mo | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index 826f6af6c4..c0ff524a99 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -395,6 +395,7 @@ extends Modelica.Icons.ExamplesPackage; extends Modelica.Icons.Function; input String logFile="ModelicaTestLog.txt" "Filename where the log is stored"; + input String weekDays[7] = Modelica.Utilities.Time.weekDays "Array of week days"; output Boolean ok; protected Modelica.Utilities.Types.TimeType now; @@ -412,7 +413,7 @@ extends Modelica.Icons.ExamplesPackage; Streams.print(" mon = " + String(now.month)); Streams.print(" year = " + String(now.year)); dow := Modelica.Utilities.Time.dayOfWeek(now); - Streams.print(" dow = " + Modelica.Utilities.Time.weekDays[dow]); + Streams.print(" dow = " + weekDays[dow]); dow := Modelica.Utilities.Time.dayOfWeek( Modelica.Utilities.Types.TimeType(year=2019, month=12, day=8, hour=12, minute=0, second=0, millisecond=0)); From e379e43e813ca0e45df85034272257ab57998ddb Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Thu, 9 Apr 2020 23:08:01 +0200 Subject: [PATCH 26/66] Fix HTML tags and double quotes --- Modelica/Math/nearestInteger.mo | 6 +++--- Modelica/Utilities/Internal.mo | 9 ++++----- Modelica/Utilities/Time.mo | 4 ++-- Modelica/Utilities/Types.mo | 2 +- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Modelica/Math/nearestInteger.mo b/Modelica/Math/nearestInteger.mo index 6c5050c9e6..21f2af987c 100644 --- a/Modelica/Math/nearestInteger.mo +++ b/Modelica/Math/nearestInteger.mo @@ -17,8 +17,8 @@ Math.nearestInteger(r);

Description

- The input value \"r\" of type Real is converted to the closest Integer value \"i\", - using the round half away from zero rule with the equation: + The input value \"r\" of type Real is converted to the closest Integer value \"i\", + using the round half away from zero rule with the equation:

 i = integer( floor( r + 0.5 ) )  for  r > 0;
@@ -33,7 +33,7 @@ Math.nearestInteger(0.5);                     // = 1
 Math.nearestInteger(-0.4);                    // = 0
 Math.nearestInteger(-0.5);                    // = -1
 Math.nearestInteger(0.3999999999999999+0.1);  // = 0
-Math.nearestInteger(1.39999999999999999+0.1); // = 1 (errorneous border case, see note below)
+Math.nearestInteger(1.39999999999999999+0.1); // = 1 (erroneous border case, see note below)
 

Note

diff --git a/Modelica/Utilities/Internal.mo b/Modelica/Utilities/Internal.mo index 75ffb0c8db..66bea583b8 100644 --- a/Modelica/Utilities/Internal.mo +++ b/Modelica/Utilities/Internal.mo @@ -266,7 +266,7 @@ All returned values are of type Integer and have the following meaning:

- +
@@ -302,13 +302,13 @@ All returned values are of type Integer and have the following meaning:

Note

This function is impure!

", revisions=" -
Argument Range Description
+
- +
Date Description
June 22, 2015 - +
- + @@ -376,7 +376,7 @@ via the format string by setting one or more of the conversion spec - + @@ -391,26 +391,26 @@ via the format string by setting one or more of the conversion spec - + - + - + - + - +
@@ -348,13 +348,12 @@ dow = Internal.Time.dayOfWeek(year, mon, day);

Description

-

Returns the day of the week for a given date using Tomohiko Sakamoto's algorithm. The returned Integer number of dow has the following meaning:

- +
diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 764ff8f08c..9632d2c401 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -75,7 +75,7 @@ The returned Integer number of dow has the following meaning:

-
Day of week Number
+
@@ -926,7 +926,7 @@ String(d, format)

If higher time value are not included, they are added to the next lower time value. - If e.g. days are not part of the format string, but hours, the number of days will be converted + If e.g. days are not part of the format string, but hours, the number of days will be converted to hours and added to the hours value.

diff --git a/Modelica/Utilities/Types.mo b/Modelica/Utilities/Types.mo index 6616447632..3916e7dfb3 100644 --- a/Modelica/Utilities/Types.mo +++ b/Modelica/Utilities/Types.mo @@ -47,7 +47,7 @@ package Types "Type definitions used in package Modelica.Utilities" Integer year "Year" annotation(absoluteValue=true); annotation (Documentation(info="

-
Day of week Number
+
From 0e18b43b1c27baca00a771cbbf9fd37322827780 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Fri, 10 Apr 2020 14:08:01 +0200 Subject: [PATCH 27/66] Can use Modelica.Math.nearestInteger --- Modelica/Blocks/Math.mo | 8 ++++---- Modelica/Math/nearestInteger.mo | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Modelica/Blocks/Math.mo b/Modelica/Blocks/Math.mo index 0ffb0e28fd..ae39670080 100644 --- a/Modelica/Blocks/Math.mo +++ b/Modelica/Blocks/Math.mo @@ -1939,7 +1939,7 @@ Otherwise the input angle u is wrapped to the 0) then integer(floor(u + 0.5)) else integer(ceil(u - 0.5)); + y = Modelica.Math.nearestInteger(u); annotation (Icon(coordinateSystem( preserveAspectRatio=true, extent={{-100.0,-100.0},{100.0,100.0}}), graphics={ @@ -1959,11 +1959,11 @@ Otherwise the input angle u is wrapped to the nearestInteger:

-y = integer( floor( u + 0.5 ) )  for  u > 0;
-y = integer( ceil ( u - 0.5 ) )  for  u < 0;
+y = Modelica.Math.nearestInteger(u);
 
")); end RealToInteger; diff --git a/Modelica/Math/nearestInteger.mo b/Modelica/Math/nearestInteger.mo index 21f2af987c..8d9e9475b6 100644 --- a/Modelica/Math/nearestInteger.mo +++ b/Modelica/Math/nearestInteger.mo @@ -17,8 +17,8 @@ Math.nearestInteger(r);

Description

- The input value \"r\" of type Real is converted to the closest Integer value \"i\", - using the round half away from zero rule with the equation: +The input value \"r\" of type Real is converted to the closest Integer value \"i\", +using the round half away from zero rule with the equation:

 i = integer( floor( r + 0.5 ) )  for  r > 0;
@@ -39,12 +39,12 @@ Math.nearestInteger(1.39999999999999999+0.1); // = 1 (erroneous border case, see
 

Note

- This function does the same conversion as the block - RealToInteger. +This function does the same conversion as the block +RealToInteger.

- The underlying equation is simple, but not always correct. Due to floating point arithmetic some border cases - are not converted correct, like shown in the example above. +The underlying equation is simple, but not always correct. Due to floating point arithmetic some border cases +are not converted correct, like shown in the example above.

")); end nearestInteger; From de1f033000dc20bbb777bf042074e4ac86596b4d Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Fri, 10 Apr 2020 21:45:10 +0200 Subject: [PATCH 28/66] Remove TimeType in favor of DateTime --- Modelica/Utilities/Time.mo | 356 ++++++++++++++++++------------------ Modelica/Utilities/Types.mo | 42 ----- 2 files changed, 182 insertions(+), 216 deletions(-) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 9632d2c401..3bb13bb79b 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -1,12 +1,17 @@ within Modelica.Utilities; package Time "Functions to work with date and time" - extends Modelica.Icons.FunctionsPackage; + extends Modelica.Icons.UtilitiesPackage; + + final constant String weekDays[7] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"} "Array of week days"; + final constant String shortWeekDays[7] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"} "Array of abbreviated week days"; + final constant String months[12] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} "Array of month names"; + final constant String shortMonths[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"} "Array of abbreviated month names"; impure function getTime "Retrieve the current time (in the local time zone)" extends Modelica.Icons.Function; - output Types.TimeType now "Current time"; + output DateTime now "Current date and time"; algorithm - (now.millisecond, now.second, now.minute, now.hour, now.day, now.month, now.year) := Internal.Time.getTime(); + now := DateTime.now(); annotation (Documentation(info="

Syntax

@@ -15,7 +20,7 @@ package Time "Functions to work with date and time"
 

Description

Returns the local time at the time instant this function was called. -All returned values are of type Integer and have the following meaning: +The returned value is of type DateTime.

@@ -49,8 +54,8 @@ All returned values are of type Integer and have the following meaning:

Example

-(ms, sec, min, hour, day, mon, year) = getTime()   // = (281, 30, 13, 10, 15, 2, 2015)
-                                              // Feb. 15, 2015 at 10:13 after 30.281 s
+now = getTime()   // = Modelica.Utilities.Time.DateTime(2015, 2, 15, 10, 13, 30, 281)
+                  // Feb. 15, 2015 at 10:13 after 30.281 s
 

Note

This function is impure!

@@ -59,14 +64,14 @@ All returned values are of type Integer and have the following meaning: function dayOfWeek "Return day of week for given date" extends Modelica.Icons.Function; - input Types.TimeType timeIn "Date"; + input DateTime dt "Date and time"; output Integer dow(min=1, max=7) "Day of week: 1 = Monday, ..., 6 = Saturday, 7 = Sunday"; algorithm - dow := Internal.Time.dayOfWeek(timeIn.year, timeIn.month, timeIn.day); + dow := Internal.Time.dayOfWeek(dt.year, dt.month, dt.day); annotation (Documentation(info="

Syntax

-dow = Time.dayOfWeek(timeIn);
+dow = Time.dayOfWeek(dt);
 

Description

@@ -98,20 +103,31 @@ The returned Integer number of dow has the following meaning:

Example

-now = getTime()      // = Modelica.Utilities.Types.TimeType(281, 30, 13, 10, 6, 12, 2019)
-                     // Dec. 06, 2019 at 10:13 after 30.281 s
-dow = dayOfWeek(now) // = 5
-                     // Dec. 06, 2019 (Saint Nicholas Day) is a Friday
+dt = getTime()      // = Modelica.Utilities.Time.DateTime(2019, 6, 12, 10, 13, 30, 281)
+                    // Dec. 06, 2019 at 10:13 after 30.281 s
+dow = dayOfWeek(dt) // = 5
+str = weekDays[dow] // = Friday
+                    // Dec. 06, 2019 (Saint Nicholas Day) is a Friday
 
")); end dayOfWeek; - function daysInYear "Get number of days in year" + function daysInYear "Return the number of days in year" extends Modelica.Icons.Function; - input Integer a "Year"; - output Integer days "Number of days in year a"; + input Integer year "Year"; + output Integer days "Number of days in year"; algorithm - days := if isLeapYear(a) then 366 else 365; + days := if isLeapYear(year) then 366 else 365; + annotation (Documentation(info=" +

Syntax

+
+days = Time.daysInYear(year);
+
+

Description

+

+Returns the number of days in year. +

+")); end daysInYear; function isLeapYear "Check if a year is a leap year" @@ -175,16 +191,10 @@ In case of year1 > year2, the result is the negativ
 days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019]
                             // for the years 2000, 2004, 2008, 2012 and 2016
-                            // excluding the second year 2020
 
")); end leapDays; - final constant String weekDays[7] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"} "Array of week days"; - final constant String shortWeekDays[7] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"} "Array of abbreviated week days"; - final constant String months[12] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} "Array of month names"; - final constant String shortMonths[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"} "Array of abbreviated month names"; - operator record DateTime "DateTime record with several constructors and overloaded operators" extends Modelica.Icons.Record; @@ -205,24 +215,24 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] extends Icons.Function; input Integer year "Year"; - input Integer month "Month"; - input Integer day "Day"; - input Integer hour "Hour"; - input Integer minute "Minute"; - input Integer second "Second"; - input Integer millisecond "Millisecond"; + input Integer month(min=1, max=12) "Month"; + input Integer day(min=1, max=31) "Day"; + input Integer hour(min=0, max=23) "Hour"; + input Integer minute(min=0, max=59) "Minute"; + input Integer second(min=0, max=61) "Second"; + input Integer millisecond(min=0, max=999)=0 "Millisecond"; - output DateTime dt(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year); + output DateTime dt(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year) "Date and time"; algorithm - + annotation(Inline = true); end fromReadable; function fromSystemTime "Create DateTime from current system time" import Modelica.Utilities.Internal.Time.getTime; extends Icons.Function; - output DateTime dt; + output DateTime dt "Current date and time"; protected Integer millisecond "Millisecond"; @@ -240,7 +250,6 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] end fromSystemTime; function fromEpoch "Create DateTime from elapsed seconds since reference year" - import Modelica.Utilities.Internal.Time.getTime; import Modelica.Math.nearestInteger; import Modelica.Utilities.Time.isLeapYear; import Modelica.Utilities.Time.daysInYear; @@ -248,7 +257,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] input Real seconds "Elapsed seconds since epoch_year"; input Integer epoch_year = 1970 "Reference year"; - output DateTime dt; + output DateTime dt "Date and time"; protected Integer[2,12] days_passed = {{31,59,90,120,151,181,212,243,273,304,334,365}, @@ -282,45 +291,45 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] hour :=nearestInteger(mod(rem/3600, 24)); // get number of days since epoch year - rem :=rem - hour*3600; - days :=nearestInteger(rem/(24*3600)); + rem := rem - hour*3600; + days := nearestInteger(rem/(24*3600)); // get year if days >= 0 then - // time is after reference year: count how many years must pass from reference year until 'days' is reached - year :=epoch_year; - counted_days := 0; - while counted_days+daysInYear(year) <= days loop - counted_days := counted_days + daysInYear(year); - year :=year + 1; - end while; + // time is after reference year: count how many years must pass from reference year until 'days' is reached + year := epoch_year; + counted_days := 0; + while counted_days + daysInYear(year) <= days loop + counted_days := counted_days + daysInYear(year); + year :=year + 1; + end while; else - // time is before reference year: count years downwards - year :=epoch_year - 1; - counted_days := if isLeapYear(year) then -366 else -365; - while counted_days > days loop - year :=year - 1; - counted_days := counted_days - daysInYear(year); - end while; + // time is before reference year: count years downwards + year := epoch_year - 1; + counted_days := -daysInYear(year); + while counted_days > days loop + year := year - 1; + counted_days := counted_days - daysInYear(year); + end while; end if; // compute day in current year - day_of_year :=days - counted_days + 1; + day_of_year := days - counted_days + 1; // get month // use correct column depending on leap and regular year - j :=if isLeapYear(year) then 2 else 1; + j := if isLeapYear(year) then 2 else 1; for i in 1:12 loop if days_passed[j,i] >= day_of_year then - month :=i; + month := i; break; end if; end for; // get day - day :=if month > 1 then day_of_year - days_passed[j,month-1] else day_of_year; + day := if month > 1 then day_of_year - days_passed[j,month-1] else day_of_year; - dt :=DateTime(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year); + dt := DateTime(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year); end fromEpoch; end 'constructor'; @@ -331,7 +340,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] import Modelica.Icons; extends Icons.FunctionsPackage; - function formated "Use a subset of C strftime() conversion specifiers to format a DateTime record as string" + function formatted "Use a subset of C strftime() conversion specifiers to format a DateTime record as string" extends Icons.Function; import Modelica.Utilities.Internal.Time.dayOfWeek; @@ -340,36 +349,35 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] import Modelica.Utilities.Time.months; import Modelica.Utilities.Time.shortMonths; - input DateTime dt; + input DateTime dt "Date and time"; input String format = "%Y-%m-%d %H:%M:%S"; - output String str; + output String str "Formatted data and time string"; protected encapsulated function string0 "Create string with minimum length, filled with 0" import Modelica.Utilities.Strings.replace; - input Integer i; - input Integer l; - output String s0; + input Integer i; + input Integer l; + output String s0; algorithm - s0 :=replace(String(i, minimumLength=l, leftJustified=false), " ", "0"); + s0 := replace(String(i, minimumLength=l, leftJustified=false), " ", "0"); end string0; algorithm - str :=replace(format, "%%", "%"); - str :=replace(str, "%y", string0(mod(dt.year, 100), l=2)); - str :=replace(str, "%Y", string0(dt.year, l=4)); - str :=replace(str, "%m", string0(dt.month, l=2)); - str :=replace(str, "%d", string0(dt.day, l=2)); - str :=replace(str, "%H", string0(dt.hour, l=2)); - str :=replace(str, "%M", string0(dt.minute, l=2)); - str :=replace(str, "%S", string0(dt.second, l=2)); - str :=replace(str, "%L", string0(dt.millisecond, l=3)); - str :=replace(str, "%a", shortWeekDays[dayOfWeek(dt.year, dt.month, dt.day)]); - str :=replace(str, "%A", weekDays[dayOfWeek(dt.year, dt.month, dt.day)]); - str :=replace(str, "%b", shortMonths[dt.month]); - str :=replace(str, "%B", months[dt.month]); + str := replace(format, "%%", "%"); + str := replace(str, "%y", string0(mod(dt.year, 100), l=2)); + str := replace(str, "%Y", string0(dt.year, l=4)); + str := replace(str, "%m", string0(dt.month, l=2)); + str := replace(str, "%d", string0(dt.day, l=2)); + str := replace(str, "%H", string0(dt.hour, l=2)); + str := replace(str, "%M", string0(dt.minute, l=2)); + str := replace(str, "%S", string0(dt.second, l=2)); + str := replace(str, "%L", string0(dt.millisecond, l=3)); + str := replace(str, "%a", shortWeekDays[dayOfWeek(dt.year, dt.month, dt.day)]); + str := replace(str, "%A", weekDays[dayOfWeek(dt.year, dt.month, dt.day)]); + str := replace(str, "%b", shortMonths[dt.month]); + str := replace(str, "%B", months[dt.month]); annotation (Documentation(info=" -

Syntax

@@ -380,11 +388,11 @@ String(dt, format)
 
 

Description

- The input value \"dt\" of type DateTime is converted to a string. +The input value dt of type DateTime is converted to a string.

- The content of the output string can be controlled - via the \"format\" string by setting one or more of the conversion specifiers listed below. +The content of the output string can be controlled +via the format string by setting one or more of the conversion specifiers listed below.

Argument Range Description
@@ -483,10 +491,10 @@ String(dt, format=\"%A, %d. %B %y, %H:%M:%S.%L\") // = \"Thursday, 24. December

Limitations

- This function uses simple string replace methods to exchange the conversion specifiers with the appropriate values. +This function uses simple string replace methods to substitute the conversion specifiers with the appropriate values.

- When additional % characters are included in the format string (via %%) problems can occur, like shown below. +When additional % characters are included in the format string (via %%) problems can occur, like shown below.

@@ -497,7 +505,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead
")); - end formated; + end formatted; end 'String'; @@ -545,27 +553,27 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead algorithm if dt1.year > dt2.year then - result :=true; + result := true; else - if dt1.year==dt2.year and dt1.month > dt2.month then - result :=true; + if dt1.year == dt2.year and dt1.month > dt2.month then + result := true; else - if dt1.month==dt2.month and dt1.day > dt2.day then - result :=true; + if dt1.month == dt2.month and dt1.day > dt2.day then + result := true; else - if dt1.day==dt2.day and dt1.hour > dt2.hour then - result :=true; + if dt1.day == dt2.day and dt1.hour > dt2.hour then + result := true; else - if dt1.hour==dt2.hour and dt1.minute > dt2.minute then - result :=true; + if dt1.hour == dt2.hour and dt1.minute > dt2.minute then + result := true; else - if dt1.minute==dt2.minute and dt1.second > dt2.second then - result :=true; + if dt1.minute == dt2.minute and dt1.second > dt2.second then + result := true; else - if dt1.second==dt2.second and dt1.millisecond > dt2.millisecond then - result :=true; + if dt1.second == dt2.second and dt1.millisecond > dt2.millisecond then + result := true; else - result :=false; + result := false; end if; end if; end if; @@ -585,7 +593,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead output Boolean result "= dt1 >= dt2"; algorithm - result :=dt1 == dt2 or dt1 > dt2; + result := dt1 == dt2 or dt1 > dt2; end '>='; @@ -667,28 +675,27 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead algorithm // get leap years from year 0 until the chosen epoch year - leap_years_til_epoch :=integer(epoch_year/4) - integer((epoch_year)/100) + integer((epoch_year)/400); + leap_years_til_epoch := integer(epoch_year/4) - integer((epoch_year)/100) + integer((epoch_year)/400); // get leap days of passed years since epoch year - leap_days :=integer((year-1)/4) - integer((year-1)/100) + integer((year-1)/400) - leap_years_til_epoch; + leap_days := integer((year-1)/4) - integer((year-1)/100) + integer((year-1)/400) - leap_years_til_epoch; - // get current day of year and consider leap day if current year is leap year and february has passed + // get current day of year and consider leap day if current year is leap year and February has passed j := if isLeapYear(year) then 2 else 1; - day_of_year :=day + days_passed[j,month]; + day_of_year := day + days_passed[j,month]; - seconds :=millisecond/1000 + second + 60*(minute + 60*(hour + 24*(day_of_year-1 + leap_days + 365*(year-epoch_year)))); + seconds := millisecond/1000 + second + 60*(minute + 60*(hour + 24*(day_of_year-1 + leap_days + 365*(year-epoch_year)))); end epoch; encapsulated function now "Get current system date and time as DateTime" import Modelica.Utilities.Time.DateTime; - output DateTime now; + output DateTime now "Current date and time"; algorithm - now:=DateTime.'constructor'.fromSystemTime(); + now := DateTime.'constructor'.fromSystemTime(); end now; annotation (Documentation(info=" -

Syntax

@@ -701,16 +708,16 @@ Time.DateTime(seconds, epoch_year);
 
 

Description

- The operator record DateTime stores the required information to address a specific point in time via date and time values. +The operator record DateTime stores the required information to address a specific point in time via date and time values.

- There are multiple constructors provided to create a DateTime element. See the examples below for details. +There are multiple constructors provided to create a DateTime element. See the examples below for details.

- DateTimes can be compared (==, <>, >, <, ≥, ≤), giving a boolean result and subtracted (-), giving a Duration. +DateTimes can be compared (==, <>, >, <, ≥, ≤), giving a boolean result and subtracted (-), giving a Duration.

- DateTimes can be converted to an epoch representation with the function DateTime.epoch(dt, epoch_year). +DateTimes can be converted to an epoch representation with the function DateTime.epoch(dt, epoch_year).

Example

@@ -761,8 +768,10 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1 input Integer minutes=0 "Minutes"; input Integer seconds=0 "Seconds"; input Integer milliseconds=0 "Milliseconds"; - output Duration d(days=days, hours=hours, minutes=minutes, seconds=seconds, milliseconds=milliseconds); + output Duration d(days=days, hours=hours, minutes=minutes, seconds=seconds, milliseconds=milliseconds) "Duration"; + algorithm + annotation(Inline = true); end fromInput; function fromDateTimes "Create Duration from two DateTime records" @@ -786,18 +795,18 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1 Integer milliseconds "Elapsed milliseconds"; algorithm - dt_tmp :=dt2; + dt_tmp := dt2; - e1 :=DateTime.epoch(dt1, epoch_year=dt1.year); - e2 :=DateTime.epoch(dt_tmp, epoch_year=dt1.year); + e1 := DateTime.epoch(dt1, epoch_year=dt1.year); + e2 := DateTime.epoch(dt_tmp, epoch_year=dt1.year); - diff :=abs(e2 - e1); - sign_ :=sign(e2 - e1); + diff := abs(e2 - e1); + sign_ := sign(e2 - e1); days := integer(diff/(24*3600)); - hours := integer(diff/3600-days*24); - minutes := integer((diff-(days*24+hours)*3600)/60); - seconds := integer(diff - (days*24+hours)*3600 - minutes*60); + hours := integer(diff/3600 - days*24); + minutes := integer((diff - (days*24 + hours)*3600)/60); + seconds := integer(diff - (days*24 + hours)*3600 - minutes*60); milliseconds := nearestInteger(rem(diff,1)*1000); hours := sign_*hours; @@ -805,7 +814,7 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1 seconds := sign_*seconds; milliseconds := sign_*milliseconds; - d :=Duration(days=days, hours=hours, minutes=minutes, seconds=seconds, milliseconds=milliseconds); + d := Duration(days=days, hours=hours, minutes=minutes, seconds=seconds, milliseconds=milliseconds); end fromDateTimes; @@ -817,33 +826,34 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1 output Duration d "Duration with input converted to seconds and milliseconds"; protected - Integer days, hours, minutes, seconds, milliseconds; + Integer days "Days"; + Integer hours "Hours"; + Integer minutes "Minutes"; + Integer seconds "Seconds"; + Integer milliseconds "Milliseconds"; + Integer carryover; algorithm milliseconds := nearestInteger(rem(totalSeconds, 1) * 1000); - seconds :=integer(div(totalSeconds, 1)); - carryover :=div(seconds, 60); + seconds := integer(div(totalSeconds, 1)); + carryover := div(seconds, 60); seconds := rem(seconds, 60); - minutes :=carryover; - carryover :=div(minutes, 60); - minutes :=rem(minutes, 60); + minutes := carryover; + carryover := div(minutes, 60); + minutes := rem(minutes, 60); - hours :=carryover; - carryover :=div(hours, 24); + hours := carryover; + carryover := div(hours, 24); hours := rem(hours, 24); days := carryover; - d :=Duration( - days=days, - hours=hours, - minutes=minutes, - seconds=seconds, - milliseconds=milliseconds); + d := Duration(days=days, hours=hours, minutes=minutes, seconds=seconds, milliseconds=milliseconds); + end fromSeconds; end 'constructor'; @@ -854,13 +864,13 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1 import Modelica.Icons; extends Icons.FunctionsPackage; - function formated "Convert duration to string, using C inspired conversion specifier characters" + function formatted "Convert duration to string, using C inspired conversion specifier characters" import Modelica.Utilities.Strings.contains; extends Icons.Function; - input Duration d; + input Duration d "Duration"; input String format = "%daysd %hoursh %minutesmin %secondss %millisecondsms"; - output String str; + output String str "Formatted duration string"; protected Duration d2; @@ -871,43 +881,42 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1 input Integer l; output String s0; algorithm - s0 :=replace(String(i, minimumLength=l, leftJustified=false), " ", "0"); + s0 := replace(String(i, minimumLength=l, leftJustified=false), " ", "0"); end string0; algorithm - d2 :=d; + d2 := d; if not contains(format, "%days") and not contains(format, "%d") then - d2.hours :=d2.hours + d2.days*24; + d2.hours := d2.hours + d2.days*24; end if; if not contains(format, "%hours") and not contains(format, "%H") then - d2.minutes :=d2.minutes + d2.hours*60; + d2.minutes := d2.minutes + d2.hours*60; end if; if not contains(format, "%minutes") and not contains(format, "%M") then - d2.seconds :=d2.seconds + d2.minutes*60; + d2.seconds := d2.seconds + d2.minutes*60; end if; if not contains(format, "%seconds") and not contains(format, "%S") then - d2.milliseconds :=d2.milliseconds + d2.seconds*1000; + d2.milliseconds := d2.milliseconds + d2.seconds*1000; end if; - str :=replace(format, "%%", "%"); - str :=replace(str, "%days", String( d2.days)); - str :=replace(str, "%d", string0(d2.days, l=2)); - str :=replace(str, "%hours", String( d2.hours)); - str :=replace(str, "%H", string0(d2.hours, l=2)); - str :=replace(str, "%minutes", String( d2.minutes)); - str :=replace(str, "%M", string0(d2.minutes, l=2)); - str :=replace(str, "%seconds", String( d2.seconds)); - str :=replace(str, "%S", string0(d2.seconds, l=2)); - str :=replace(str, "%milliseconds", String( d2.milliseconds)); - str :=replace(str, "%L", string0(d2.milliseconds, l=3)); + str := replace(format, "%%", "%"); + str := replace(str, "%days", String( d2.days)); + str := replace(str, "%d", string0(d2.days, l=2)); + str := replace(str, "%hours", String( d2.hours)); + str := replace(str, "%H", string0(d2.hours, l=2)); + str := replace(str, "%minutes", String( d2.minutes)); + str := replace(str, "%M", string0(d2.minutes, l=2)); + str := replace(str, "%seconds", String( d2.seconds)); + str := replace(str, "%S", string0(d2.seconds, l=2)); + str := replace(str, "%milliseconds", String( d2.milliseconds)); + str := replace(str, "%L", string0(d2.milliseconds, l=3)); annotation (Documentation(info=" -

Syntax

@@ -918,20 +927,20 @@ String(d, format)
 
 

Description

- The input value \"d\" of type Duration is converted to a string. +The input value d of type Duration is converted to a string.

- The content of the output string can be controlled - via the \"format\" string by setting one or more of the conversion specifiers listed below. +The content of the output string can be controlled +via the format string by setting one or more of the conversion specifiers listed below.

- If higher time value are not included, they are added to the next lower time value. - If e.g. days are not part of the format string, but hours, the number of days will be converted - to hours and added to the hours value. +If higher time values are not included, they are added to the next lower time value. +If e.g., days are not part of the format string, but hours, the number of days will be converted +to hours and added to the hours value.

- If lower time values are not included, they are neglected. - If e.g. only days and hours are part of the format string, minutes, seconds and milliseconds will be ignored. +If lower time values are not included, they are neglected. +If e.g., only days and hours are part of the format string, minutes, seconds and milliseconds will be ignored.

@@ -1012,10 +1021,10 @@ String(d, format=\"%days'\") // = \"1\" (only full days are shown, rest

Limitations

- This function uses simple string replace methods to exchange the conversion specifiers with the appropriate values. +This function uses simple string replace methods to exchange the conversion specifiers with the appropriate values.

- When additional % characters are included in the format string (via %%) problems can occur, like shown below. +When additional % characters are included in the format string (via %%) problems can occur, like shown below.

@@ -1026,7 +1035,7 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead
")); - end formated; + end formatted; end 'String'; @@ -1075,7 +1084,7 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead output Boolean result "= d1 > d2"; algorithm - result :=Duration.inSeconds(d1) > Duration.inSeconds(d2); + result := Duration.inSeconds(d1) > Duration.inSeconds(d2); end '>'; encapsulated operator function '>=' "Check if Duration d1 is equal to d2 or larger" @@ -1088,7 +1097,7 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead output Boolean result "= d1 >= d2"; algorithm - result :=d1 == d2 or d1 > d2; + result := d1 == d2 or d1 > d2; end '>='; @@ -1307,7 +1316,6 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead end normalize; annotation (Documentation(info=" -

Syntax

@@ -1320,13 +1328,13 @@ Time.Duration(totalSeconds);
 
 

Description

- The operator record Duration is used for elapsed time. - This can be the time between given by DateTime records - (i.e. by subtracting them) or a manually specified duration. +The operator record Duration is used for elapsed time. +This can be the time between given by DateTime records +(i.e., by subtracting them) or a manually specified duration.

- There are multiple constructors provided to create a Duration element. - See the examples below for details. +There are multiple constructors provided to create a Duration element. +See the examples below for details.

Here is a brief summary, what the Duration operator record is capable of:

@@ -1386,7 +1394,7 @@ Duration(totalSeconds=1.5); // automatic selection of const annotation ( Documentation(info="

-This package contains functions to work with date and time. +This package contains functions and records to work with date and time.

")); end Time; diff --git a/Modelica/Utilities/Types.mo b/Modelica/Utilities/Types.mo index 3916e7dfb3..286f10a9ca 100644 --- a/Modelica/Utilities/Types.mo +++ b/Modelica/Utilities/Types.mo @@ -35,48 +35,6 @@ package Types "Type definitions used in package Modelica.Utilities" ")); end TokenValue; - - record TimeType "Record containing date and time components" - extends Modelica.Icons.Record; - Integer millisecond(min=0, max=999) "Millisecond" annotation(absoluteValue=true); - Integer second(min=0, max=61) "Second" annotation(absoluteValue=true); - Integer minute(min=0, max=59) "Minute" annotation(absoluteValue=true); - Integer hour(min=0, max=23) "Hour" annotation(absoluteValue=true); - Integer day(min=1, max=31) "Day" annotation(absoluteValue=true); - Integer month(min=1, max=12) "Month" annotation(absoluteValue=true); - Integer year "Year" annotation(absoluteValue=true); - annotation (Documentation(info=" -
-
- - - - - - - - - - - - - - - - - - - - - - - - -
ArgumentRangeDescription
ms 0 .. 999Milliseconds after second
sec 0 .. 61Seconds after minute
min 0 .. 59Minutes after hour
hour 0 .. 23Hours after midnight
day 1 .. 31Day of month
mon 1 .. 12Month
year Year
-
-")); - end TimeType; - annotation (Documentation(info="

This package contains type definitions used in Modelica.Utilities. From 2616e2d14a8637393f41b2c57b7a0e42fafa58e1 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Fri, 10 Apr 2020 21:51:21 +0200 Subject: [PATCH 29/66] Fix icon inheritance --- Complex.mo | 13 +- Modelica/Icons.mo | 47 +++++- Modelica/Utilities/Time.mo | 323 ++++++++++++++++++++++++++----------- 3 files changed, 276 insertions(+), 107 deletions(-) diff --git a/Complex.mo b/Complex.mo index cf37fe950c..3a632c949b 100644 --- a/Complex.mo +++ b/Complex.mo @@ -18,15 +18,22 @@ operator record Complex "Complex number with overloaded operators" end fromReal; annotation (Documentation(info="

Here the constructor operator(s) is/are defined.

-"), Icon(graphics={Rectangle( +"), Icon( + graphics={ + Rectangle( lineColor={200,200,200}, fillColor={248,248,248}, fillPattern=FillPattern.HorizontalCylinder, extent={{-100,-100},{100,100}}, - radius=25.0), Rectangle( + radius=25.0), + Rectangle( lineColor={128,128,128}, extent={{-100,-100},{100,100}}, - radius=25.0)})); + radius=25.0), + Text( + textColor={128,128,128}, + extent={{-90,-90},{90,90}}, + textString="f")})); end 'constructor'; encapsulated operator function '0' "Zero-element of addition (= Complex(0))" diff --git a/Modelica/Icons.mo b/Modelica/Icons.mo index d63282f47a..6a7dbb7433 100644 --- a/Modelica/Icons.mo +++ b/Modelica/Icons.mo @@ -527,22 +527,22 @@ This icon is designed for a translational sensor model. extent={{-150,60},{150,100}}, textString="%name"), Rectangle( - origin={0.0,-25.0}, + origin={0,-25}, lineColor={64,64,64}, fillColor={255,215,136}, fillPattern=FillPattern.Solid, - extent={{-100.0,-75.0},{100.0,75.0}}, - radius=25.0), + extent={{-100,-75},{100,75}}, + radius=25), Line( - points={{-100.0,0.0},{100.0,0.0}}, + points={{-100,0},{100,0}}, color={64,64,64}), Line( - origin={0.0,-50.0}, - points={{-100.0,0.0},{100.0,0.0}}, + origin={0,-50}, + points={{-100,0},{100,0}}, color={64,64,64}), Line( - origin={0.0,-25.0}, - points={{0.0,75.0},{0.0,-75.0}}, + origin={0,-25}, + points={{0,75},{0,-75}}, color={64,64,64})}), Documentation(info="

This icon is indicates a record. @@ -550,6 +550,37 @@ This icon is indicates a record. ")); end Record; + partial operator record OperatorRecord "Icon for operator records" + + annotation (Icon(coordinateSystem(preserveAspectRatio=true, extent={{-100,-100},{100,100}}), graphics={ + Text( + textColor={0,0,255}, + extent={{-150,60},{150,100}}, + textString="%name"), + Rectangle( + origin={0,-25}, + lineColor={64,64,64}, + fillColor={255,215,136}, + fillPattern=FillPattern.Solid, + extent={{-100,-75},{100,75}}, + radius=25), + Line( + points={{-100,0},{100,0}}, + color={64,64,64}), + Line( + origin={0,-50}, + points={{-100,0},{100,0}}, + color={64,64,64}), + Line( + origin={0,-25}, + points={{0,75},{0,-75}}, + color={64,64,64})}), Documentation(info=" +

+This icon is indicates an operator record. +

+")); + end OperatorRecord; + type TypeReal "Icon for Real types" extends Real; annotation(Icon(coordinateSystem(preserveAspectRatio=false, extent={{-100,-100},{100,100}}), graphics={ diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 3bb13bb79b..6e0e9f6636 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -196,7 +196,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] end leapDays; operator record DateTime "DateTime record with several constructors and overloaded operators" - extends Modelica.Icons.Record; + extends Modelica.Icons.OperatorRecord; Integer millisecond(min=0, max=999) "Millisecond" annotation(absoluteValue=true); Integer second(min=0, max=61) "Second" annotation(absoluteValue=true); @@ -208,11 +208,10 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] encapsulated operator 'constructor' "Available constructors" import Modelica.Utilities.Time.DateTime; - import Modelica.Icons; - extends Icons.FunctionsPackage; + import Modelica.Icons.Function; function fromReadable "Create DateTime from human readable format" - extends Icons.Function; + extends Function; input Integer year "Year"; input Integer month(min=1, max=12) "Month"; @@ -230,7 +229,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] function fromSystemTime "Create DateTime from current system time" import Modelica.Utilities.Internal.Time.getTime; - extends Icons.Function; + extends Function; output DateTime dt "Current date and time"; @@ -253,7 +252,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] import Modelica.Math.nearestInteger; import Modelica.Utilities.Time.isLeapYear; import Modelica.Utilities.Time.daysInYear; - extends Icons.Function; + extends Function; input Real seconds "Elapsed seconds since epoch_year"; input Integer epoch_year = 1970 "Reference year"; @@ -332,16 +331,33 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] dt := DateTime(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year); end fromEpoch; + annotation (Documentation(info=" +

Here the constructor operator(s) is/are defined.

+"), Icon( + graphics={ + Rectangle( + lineColor={200,200,200}, + fillColor={248,248,248}, + fillPattern=FillPattern.HorizontalCylinder, + extent={{-100,-100},{100,100}}, + radius=25), + Rectangle( + lineColor={128,128,128}, + extent={{-100,-100},{100,100}}, + radius=25), + Text( + textColor={128,128,128}, + extent={{-90,-90},{90,90}}, + textString="f")})); end 'constructor'; encapsulated operator 'String' "Convert DateTime to string" import Modelica.Utilities.Time.DateTime; import Modelica.Utilities.Strings.replace; - import Modelica.Icons; - extends Icons.FunctionsPackage; + import Modelica.Icons.Function; function formatted "Use a subset of C strftime() conversion specifiers to format a DateTime record as string" - extends Icons.Function; + extends Function; import Modelica.Utilities.Internal.Time.dayOfWeek; import Modelica.Utilities.Time.weekDays; @@ -506,13 +522,30 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead ")); end formatted; - + annotation (Documentation(info=" +

Here the String operator(s) is/are defined.

+"), Icon( + graphics={ + Rectangle( + lineColor={200,200,200}, + fillColor={248,248,248}, + fillPattern=FillPattern.HorizontalCylinder, + extent={{-100,-100},{100,100}}, + radius=25), + Rectangle( + lineColor={128,128,128}, + extent={{-100,-100},{100,100}}, + radius=25), + Text( + textColor={128,128,128}, + extent={{-90,-90},{90,90}}, + textString="f")})); end 'String'; encapsulated operator function '==' "Check equality of two DateTime objects" import Modelica.Utilities.Time.DateTime; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; input DateTime dt1; input DateTime dt2; @@ -531,8 +564,8 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead encapsulated operator function '<>' "Check inequality of two DateTime objects" import Modelica.Utilities.Time.DateTime; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; input DateTime dt1; input DateTime dt2; @@ -544,8 +577,8 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead encapsulated operator function '>' "Check if DateTime dt1 is later as dt2" import Modelica.Utilities.Time.DateTime; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; input DateTime dt1; input DateTime dt2; @@ -585,8 +618,8 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead encapsulated operator function '>=' "Check if DateTime dt1 is equal to dt2 or later" import Modelica.Utilities.Time.DateTime; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; input DateTime dt1; input DateTime dt2; @@ -599,8 +632,8 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead encapsulated operator function '<' "Check if DateTime dt1 is earlier as dt2" import Modelica.Utilities.Time.DateTime; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; input DateTime dt1; input DateTime dt2; @@ -613,8 +646,8 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead encapsulated operator function '<=' "Check if DateTime dt1 is equal to dt2 or earlier" import Modelica.Utilities.Time.DateTime; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; input DateTime dt1; input DateTime dt2; @@ -625,14 +658,12 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead end '<='; - encapsulated operator '-' "Unary and binary minus" + encapsulated operator '-' "Binary minus" import Modelica.Utilities.Time.DateTime; - import Modelica.Icons; - - extends Icons.FunctionsPackage; + import Modelica.Icons.Function; function subtract "Return time delta between dt2 and dt1 as Duration" - extends Icons.Function; + extends Function; import Modelica.Utilities.Time.Duration; @@ -644,14 +675,29 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead result := Duration.'constructor'.fromDateTimes(dt2, dt1); end subtract; - + annotation (Documentation(info=" +

Here the binary minus operator is defined.

+"), Icon(coordinateSystem(preserveAspectRatio=false, extent={{-100,-100}, + {100,100}}), graphics={ + Rectangle( + lineColor={200,200,200}, + fillColor={248,248,248}, + fillPattern=FillPattern.HorizontalCylinder, + extent={{-100,-100},{100,100}}, + radius=25), + Rectangle( + lineColor={128,128,128}, + extent={{-100,-100},{100,100}}, + radius=25), + Line( + points={{-50,0},{50,0}})})); end '-'; encapsulated function epoch "Convert DateTime to elapsed seconds since custom epoch year" import Modelica.Utilities.Time.DateTime; import Modelica.Utilities.Time.isLeapYear; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; input DateTime dt; input Integer epoch_year = 1970 "Reference year"; @@ -690,6 +736,9 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead encapsulated function now "Get current system date and time as DateTime" import Modelica.Utilities.Time.DateTime; + import Modelica.Icons.Function; + extends Function; + output DateTime now "Current date and time"; algorithm now := DateTime.'constructor'.fromSystemTime(); @@ -747,7 +796,7 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1 end DateTime; operator record Duration "Duration record with several constructors and overloaded operators" - extends Modelica.Icons.Record; + extends Modelica.Icons.OperatorRecord; Integer days "Days"; Integer hours "Hours"; @@ -757,11 +806,10 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1 encapsulated operator 'constructor' "Available constructors" import Modelica.Utilities.Time.Duration; - import Modelica.Icons; - extends Icons.FunctionsPackage; + import Modelica.Icons.Function; function fromInput "Create Duration field by field from user input" - extends Icons.Function; + extends Function; input Integer days=0 "Days"; input Integer hours=0 "Hours"; @@ -771,13 +819,13 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1 output Duration d(days=days, hours=hours, minutes=minutes, seconds=seconds, milliseconds=milliseconds) "Duration"; algorithm - annotation(Inline = true); + annotation (Inline = true); end fromInput; function fromDateTimes "Create Duration from two DateTime records" import Modelica.Utilities.Time.DateTime; import Modelica.Math.nearestInteger; - extends Icons.Function; + extends Function; input DateTime dt1 "Start time"; input DateTime dt2 "End time"; @@ -819,7 +867,7 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1 end fromDateTimes; function fromSeconds "Create duration record from total amount of seconds, rounding to the third decimal" - extends Icons.Function; + extends Function; import Modelica.Math.nearestInteger; input Real totalSeconds "Duration in seconds. Decimal place is converted to milliseconds"; @@ -854,19 +902,35 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1 d := Duration(days=days, hours=hours, minutes=minutes, seconds=seconds, milliseconds=milliseconds); - end fromSeconds; + annotation (Documentation(info=" +

Here the constructor operator(s) is/are defined.

+"), Icon( + graphics={ + Rectangle( + lineColor={200,200,200}, + fillColor={248,248,248}, + fillPattern=FillPattern.HorizontalCylinder, + extent={{-100,-100},{100,100}}, + radius=25), + Rectangle( + lineColor={128,128,128}, + extent={{-100,-100},{100,100}}, + radius=25), + Text( + textColor={128,128,128}, + extent={{-90,-90},{90,90}}, + textString="f")})); end 'constructor'; encapsulated operator 'String' "Convert Duration to string" import Modelica.Utilities.Time.Duration; import Modelica.Utilities.Strings.replace; - import Modelica.Icons; - extends Icons.FunctionsPackage; + import Modelica.Icons.Function; function formatted "Convert duration to string, using C inspired conversion specifier characters" import Modelica.Utilities.Strings.contains; - extends Icons.Function; + extends Function; input Duration d "Duration"; input String format = "%daysd %hoursh %minutesmin %secondss %millisecondsms"; @@ -1036,13 +1100,30 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead ")); end formatted; - + annotation (Documentation(info=" +

Here the String operator(s) is/are defined.

+"), Icon( + graphics={ + Rectangle( + lineColor={200,200,200}, + fillColor={248,248,248}, + fillPattern=FillPattern.HorizontalCylinder, + extent={{-100,-100},{100,100}}, + radius=25), + Rectangle( + lineColor={128,128,128}, + extent={{-100,-100},{100,100}}, + radius=25), + Text( + textColor={128,128,128}, + extent={{-90,-90},{90,90}}, + textString="f")})); end 'String'; encapsulated operator function '==' "Check equality of two Duration objects by normalizing them" import Modelica.Utilities.Time.Duration; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; input Duration d1; input Duration d2; @@ -1063,8 +1144,8 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead encapsulated operator function '<>' "Check inequality of two Duration objects by normalizing them" import Modelica.Utilities.Time.Duration; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; input Duration d1; input Duration d2; @@ -1076,8 +1157,8 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead encapsulated operator function '>' "Check if Duration d1 is larger than d2" import Modelica.Utilities.Time.Duration; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; input Duration d1; input Duration d2; @@ -1089,8 +1170,8 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead encapsulated operator function '>=' "Check if Duration d1 is equal to d2 or larger" import Modelica.Utilities.Time.Duration; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; input Duration d1; input Duration d2; @@ -1103,8 +1184,8 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead encapsulated operator function '<' "Check if Duration d1 is smaller than d2" import Modelica.Utilities.Time.Duration; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; input Duration d1; input Duration d2; @@ -1117,8 +1198,8 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead encapsulated operator function '<=' "Check if Duration d1 is equal to d2 or smaller" import Modelica.Utilities.Time.Duration; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; input Duration d1; input Duration d2; @@ -1131,8 +1212,8 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead encapsulated operator function '+' "Add Durations d1 and d2 and normalize the sum" import Modelica.Utilities.Time.Duration; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; input Duration d1; input Duration d2; @@ -1150,12 +1231,26 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead encapsulated operator '-' "Unary and binary minus" import Modelica.Utilities.Time.Duration; - import Modelica.Icons; + import Modelica.Icons.Function; + + function negate "Unary minus (multiply all duration values by -1)" + extends Function; + + input Duration d; + output Duration result "= -d"; + + algorithm + result := Duration( + days=-d.days, + hours=-d.hours, + minutes=-d.minutes, + seconds=-d.seconds, + milliseconds=-d.milliseconds); - extends Icons.FunctionsPackage; + end negate; function subtract "Subtract two durations element wise and normalize the difference" - extends Icons.Function; + extends Function; input Duration d1; input Duration d2; @@ -1170,32 +1265,30 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead milliseconds=d1.milliseconds - d2.milliseconds)); end subtract; - - function negate "Unary minus (multiply all duration values by -1)" - extends Icons.Function; - - input Duration d; - output Duration result "= -d1"; - - algorithm - result := Duration( - days=-d.days, - hours=-d.hours, - minutes=-d.minutes, - seconds=-d.seconds, - milliseconds=-d.milliseconds); - - end negate; + annotation (Documentation(info=" +

Here the unary and binary minus operator(s) is/are defined.

+"), Icon(coordinateSystem(preserveAspectRatio=false, extent={{-100,-100}, + {100,100}}), graphics={ + Rectangle( + lineColor={200,200,200}, + fillColor={248,248,248}, + fillPattern=FillPattern.HorizontalCylinder, + extent={{-100,-100},{100,100}}, + radius=25), + Rectangle( + lineColor={128,128,128}, + extent={{-100,-100},{100,100}}, + radius=25), + Line( + points={{-50,0},{50,0}})})); end '-'; encapsulated operator '*' "Multiplication" import Modelica.Utilities.Time.Duration; - import Modelica.Icons; - - extends Icons.FunctionsPackage; + import Modelica.Icons.Function; function multiply1 "Multiply a duration with a real (by converting the duration to seconds)" - extends Icons.Function; + extends Function; input Duration d; input Real r; @@ -1207,7 +1300,7 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead end multiply1; function multiply2 "Multiply a duration with a real (by converting the duration to seconds)" - extends Icons.Function; + extends Function; input Real r; input Duration d; @@ -1217,17 +1310,37 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead result := Duration(totalSeconds=r*Duration.inSeconds(d)); end multiply2; + annotation ( + Documentation(info=" +

Here the multiplication operator(s) is/are defined.

+"), + Icon(coordinateSystem( + preserveAspectRatio=false, + extent={{-100,-100},{100,100}}), + graphics={ + Rectangle( + lineColor={200,200,200}, + fillColor={248,248,248}, + fillPattern=FillPattern.HorizontalCylinder, + extent={{-100,-100},{100,100}}, + radius=25), + Rectangle( + lineColor={128,128,128}, + extent={{-100,-100},{100,100}}, + radius=25), + Line(points={{-40,35},{40,-35}}), + Line(points={{-40,-35},{40,35}}), + Line(points={{-55,0},{55,0}}), + Line(points={{0,55},{0,-55}})})); end '*'; - encapsulated operator '/' "Division" + encapsulated operator '/' "Division" import Modelica.Utilities.Time.Duration; - import Modelica.Icons; - - extends Icons.FunctionsPackage; + import Modelica.Icons.Function; function divide "Divide a duration by a real. The first milliseconds value can vary by 1 (due to rounding in the fromSeconds constructor)" - extends Icons.Function; + extends Function; input Duration d; input Real r; @@ -1237,13 +1350,31 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead result := Duration(totalSeconds=Duration.inSeconds(d)/r); end divide; - + annotation ( + Documentation(info=" +

Here the multiplication operator(s) is/are defined.

+"), + Icon(coordinateSystem( + preserveAspectRatio=false, + extent={{-100,-100},{100,100}}), + graphics={ + Rectangle( + lineColor={200,200,200}, + fillColor={248,248,248}, + fillPattern=FillPattern.HorizontalCylinder, + extent={{-100,-100},{100,100}}, + radius=25), + Rectangle( + lineColor={128,128,128}, + extent={{-100,-100},{100,100}}, + radius=25), + Line(points={{-20,-55},{20,55}})})); end '/'; encapsulated operator function '0' "Zero-element of addition (= Duration())" import Modelica.Utilities.Time.Duration; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; output Duration result "= Duration()"; @@ -1254,8 +1385,8 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead encapsulated function asVector "Return duration as vector {days, hours, minutes, seconds, milliseconds}" import Modelica.Utilities.Time.Duration; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; input Duration d "Value to convert"; output Integer[5] d_vec "Duration as vector {days, hours, minutes, seconds, milliseconds}"; @@ -1268,8 +1399,8 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead encapsulated function avg "Return Duration with averaged values for a vector of durations" import Modelica.Utilities.Time.Duration; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; input Duration d_vec[:] "Vector of duration"; output Duration d_avg "Average duration"; @@ -1289,8 +1420,8 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead encapsulated function inSeconds "Convert Duration to total amount of seconds" import Modelica.Utilities.Time.Duration; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; input Duration d; output Real totalSeconds "Elapsed seconds"; @@ -1303,8 +1434,8 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead encapsulated function normalize "Recompute duration with usual maximum values for milliseconds, seconds, minutes and hours" import Modelica.Utilities.Time.Duration; - import Modelica.Icons; - extends Icons.Function; + import Modelica.Icons.Function; + extends Function; input Duration d "Duration"; output Duration d_norm "Normalized duration"; From c8078e0ebd8022cc69a6fe404f5305c605d71164 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Sat, 11 Apr 2020 23:26:03 +0200 Subject: [PATCH 30/66] Rewrite using external functions from ModelicaTime.c * Rewrite DateTime.'constructor'.fromEpoch to utilize ModelicaTime_localtime * Rewrite DateTime.'String'.formatted to utilize ModelicaTime_strftime * Rewrite DateTime.epoch to utilize ModelicaTime_difftime * Add DateTime.'constructor'.fromString utilizing ModelicaTime_strptime ModelicaTime.[ch] need to be integrated to ModelicaExternalC for later distribution. During development ModelicaTime.c is directly included by the external function declaration. --- .../BuildProjects/autotools/Makefile.am | 2 +- Modelica/Resources/BuildProjects/gcc/Makefile | 6 +- Modelica/Resources/C-Sources/ModelicaTime.c | 210 +++++++ Modelica/Resources/C-Sources/ModelicaTime.h | 96 +++ Modelica/Resources/C-Sources/readme.txt | 3 +- Modelica/Resources/C-Sources/strptime.h | 557 ++++++++++++++++++ .../Licenses/LICENSE_ModelicaTime.txt | 27 + .../Resources/Licenses/LICENSE_strptime.txt | 26 + Modelica/Utilities/Internal.mo | 38 +- Modelica/Utilities/Time.mo | 166 ++---- 10 files changed, 998 insertions(+), 133 deletions(-) create mode 100644 Modelica/Resources/C-Sources/ModelicaTime.c create mode 100644 Modelica/Resources/C-Sources/ModelicaTime.h create mode 100644 Modelica/Resources/C-Sources/strptime.h create mode 100644 Modelica/Resources/Licenses/LICENSE_ModelicaTime.txt create mode 100644 Modelica/Resources/Licenses/LICENSE_strptime.txt diff --git a/Modelica/Resources/BuildProjects/autotools/Makefile.am b/Modelica/Resources/BuildProjects/autotools/Makefile.am index 2fa142a3c9..e2f903934f 100644 --- a/Modelica/Resources/BuildProjects/autotools/Makefile.am +++ b/Modelica/Resources/BuildProjects/autotools/Makefile.am @@ -1,5 +1,5 @@ lib_LTLIBRARIES = libzlib.la libModelicaExternalC.la libModelicaMatIO.la libModelicaIO.la libModelicaStandardTables.la -libModelicaExternalC_la_SOURCES = ../../C-Sources/ModelicaFFT.c ../../C-Sources/ModelicaInternal.c ../../C-Sources/ModelicaRandom.c ../../C-Sources/ModelicaStrings.c +libModelicaExternalC_la_SOURCES = ../../C-Sources/ModelicaFFT.c ../../C-Sources/ModelicaInternal.c ../../C-Sources/ModelicaRandom.c ../../C-Sources/ModelicaStrings.c ../../C-Sources/ModelicaTime.c libModelicaExternalC_la_LIBADD = @LIBMATH@ libModelicaIO_la_SOURCES = ../../C-Sources/ModelicaIO.c libModelicaIO_la_LIBADD = libModelicaMatIO.la diff --git a/Modelica/Resources/BuildProjects/gcc/Makefile b/Modelica/Resources/BuildProjects/gcc/Makefile index 31f6dafbb5..78ad9351fa 100644 --- a/Modelica/Resources/BuildProjects/gcc/Makefile +++ b/Modelica/Resources/BuildProjects/gcc/Makefile @@ -11,7 +11,8 @@ EXTC_OBJS = \ ModelicaFFT.o \ ModelicaInternal.o \ ModelicaRandom.o \ - ModelicaStrings.o + ModelicaStrings.o \ + ModelicaTime.o TABLES_OBJS = \ ModelicaStandardTables.o \ @@ -93,6 +94,9 @@ ModelicaRandom.o: ../../C-Sources/ModelicaRandom.c ModelicaStrings.o: ../../C-Sources/ModelicaStrings.c $(CC) $(CPPFLAGS) $(CFLAGS) $(INC) -c -o $@ $< +ModelicaTime.o: ../../C-Sources/ModelicaTime.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(INC) -c -o $@ $< + %.o: ../../C-Sources/zlib/%.c $(CC) $(CPPFLAGS) $(CFLAGS) $(INC) -c -o $@ $< diff --git a/Modelica/Resources/C-Sources/ModelicaTime.c b/Modelica/Resources/C-Sources/ModelicaTime.c new file mode 100644 index 0000000000..d31208e7e6 --- /dev/null +++ b/Modelica/Resources/C-Sources/ModelicaTime.c @@ -0,0 +1,210 @@ +/* ModelicaTime.c - External functions for Modelica.Utilities.Time + + Copyright (C) 2020, Modelica Association and contributors + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +// TODO: Remove when ModelicaTime.c will become part of ModelicaExternalC +#if defined(DYMOSIM) +#pragma once +#endif + +#include "ModelicaTime.h" + +#include +#include +#include +#include "ModelicaUtilities.h" +#if !defined(__APPLE_CC__) +#include "strptime.h" +#endif + +#if !defined(NO_TIME) +time_t epoch(int sec, int min, int hour, int mday, int mon, int year) { + struct tm tlocal; + time_t calendarTime; + + memset(&tlocal, 0, sizeof(struct tm)); + tlocal.tm_sec = sec; + tlocal.tm_min = min; + tlocal.tm_hour = hour; + tlocal.tm_mday = mday; + tlocal.tm_mon = mon - 1; + tlocal.tm_year = year - 1900; + + calendarTime = mktime(&tlocal); + if (-1 == calendarTime) { + ModelicaFormatError("Not possible to convert \"%4i-%02i-%02i %02i:%02i:%02i\" " + "to time_t type in local time zone.\n", year, mon, mday, hour, min, sec); + } + return calendarTime; +} +#endif + +double ModelicaTime_difftime(int sec, int min, int hour, int mday, int mon, + int year, int refYear) { + /* Get elapsed seconds w.r.t. reference year */ +#if defined(NO_TIME) + return 0.0; +#else + const time_t endTime = epoch(sec, min, hour, mday, mon, year); + if (1970 == refYear) { + /* Avoid calling mktime for New Year 1970 in local time zone */ + const time_t startTime = epoch(0, 0, 0, 2, 1, 1970); + return difftime(endTime, startTime) + 86400.0; + } + else { + const time_t startTime = epoch(0, 0, 0, 1, 1, refYear); + return difftime(endTime, startTime); + } +#endif +} + +void ModelicaTime_localtime(_Out_ int* ms, _Out_ int* sec, _Out_ int* min, + _Out_ int* hour, _Out_ int* mday, _Out_ int* mon, + _Out_ int* year, double seconds, int refYear) { + /* Convert elapsed seconds w.r.t. reference year */ +#if defined(NO_TIME) + *ms = 0; + *sec = 0; + *min = 0; + *hour = 0; + *mday = 0; + *mon = 0; + *year = 0; +#else + struct tm* tlocal; + time_t calendarTime; + double intPartOfSeconds; + double fracPartOfSeconds; +#if defined(_POSIX_) || (defined(_MSC_VER) && _MSC_VER >= 1400) + struct tm tres; +#endif + + fracPartOfSeconds = modf(seconds, &intPartOfSeconds); + calendarTime = (time_t)intPartOfSeconds; + + if (1970 == refYear) { + /* Avoid calling mktime for New Year 1970 in local time zone */ + calendarTime = calendarTime + epoch(0, 0, 0, 2, 1, 1970) - (time_t)86400; + } + else { + calendarTime = calendarTime + epoch(0, 0, 0, 1, 1, refYear); + } + +#if defined(_POSIX_) + tlocal = localtime_r(&calendarTime, &tres); /* Time fields in local time zone */ +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + localtime_s(&tres, &calendarTime); /* Time fields in local time zone */ + tlocal = &tres; +#else + tlocal = localtime(&calendarTime); /* Time fields in local time zone */ +#endif + + /* Do not memcpy as you do not know which sizes are in the struct */ + *ms = (int)(fracPartOfSeconds*1000.0 + 0.5); + *sec = tlocal->tm_sec; + *min = tlocal->tm_min; + *hour = tlocal->tm_hour; + *mday = tlocal->tm_mday; + *mon = 1 + tlocal->tm_mon; /* Correct for month starting at 1 */ + *year = 1900 + tlocal->tm_year; /* Correct for 4-digit year */ +#endif +} + +_Ret_z_ const char* ModelicaTime_strftime(int sec, int min, int hour, int mday, int mon, + int year, _In_z_ const char* format, int _maxSize) { + /* Formatted time to string conversion */ +#if defined(NO_TIME) + return ""; +#else + size_t retLen; + struct tm* tlocal; + const size_t maxSize = (size_t)_maxSize; + char* timePtr = ModelicaAllocateString(maxSize); + time_t calendarTime = epoch(sec, min, hour, mday, mon, year); +#if defined(_POSIX_) || (defined(_MSC_VER) && _MSC_VER >= 1400) + struct tm tres; +#endif + +#if defined(_POSIX_) + tlocal = localtime_r(&calendarTime, &tres); /* Time fields in local time zone */ +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + localtime_s(&tres, &calendarTime); /* Time fields in local time zone */ + tlocal = &tres; +#else + tlocal = localtime(&calendarTime); /* Time fields in local time zone */ +#endif + + retLen = strftime(timePtr, maxSize, format, tlocal); + if (retLen > 0 && retLen <= maxSize) { + return (const char*)timePtr; + } + else { + return ""; + } +#endif +} + +void ModelicaTime_strptime(_Out_ int* ms, _Out_ int* sec, _Out_ int* min, + _Out_ int* hour, _Out_ int* mday, _Out_ int* mon, + _Out_ int* year, _In_z_ const char* buf, + _In_z_ const char* format) { + /* Formatted string to time conversion */ +#if defined(NO_TIME) + *ms = 0; + *sec = 0; + *min = 0; + *hour = 0; + *mday = 0; + *mon = 0; + *year = 0; +#else + struct tm tlocal; + + memset(&tlocal, 0, sizeof(struct tm)); + if (strptime(buf, format, &tlocal)) { + /* Do not memcpy as you do not know which sizes are in the struct */ + *sec = tlocal.tm_sec; + *min = tlocal.tm_min; + *hour = tlocal.tm_hour; + *mday = tlocal.tm_mday; + *mon = 1 + tlocal.tm_mon; /* Correct for month starting at 1 */ + *year = 1900 + tlocal.tm_year; /* Correct for 4-digit year */ + } + else { + *sec = 0; + *min = 0; + *hour = 0; + *mday = 0; + *mon = 0; + *year = 0; + } + *ms = 0; +#endif +} diff --git a/Modelica/Resources/C-Sources/ModelicaTime.h b/Modelica/Resources/C-Sources/ModelicaTime.h new file mode 100644 index 0000000000..d07eaa4653 --- /dev/null +++ b/Modelica/Resources/C-Sources/ModelicaTime.h @@ -0,0 +1,96 @@ +/* ModelicaTime.h - External functions header for Modelica.Utilities.Time + + Copyright (C) 2020, Modelica Association and contributors + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* The following #define's are available. + + NO_TIME : Functions localtime and mktime are not present + MODELICA_EXPORT: Prefix used for function calls. If not defined, blank is used + Useful definition: + - "__declspec(dllexport)" if included in a DLL and the + functions shall be visible outside of the DLL +*/ + +#ifndef MODELICA_TIME_H_ +#define MODELICA_TIME_H_ + +#if !defined(MODELICA_EXPORT) +#if defined(__cplusplus) +#define MODELICA_EXPORT extern "C" +#else +#define MODELICA_EXPORT +#endif +#endif + +/* + * Non-null pointers and esp. null-terminated strings need to be passed to + * external functions. + * + * The following macros handle nonnull attributes for GNU C and Microsoft SAL. + */ +#undef MODELICA_NONNULLATTR +#undef MODELICA_RETURNNONNULLATTR +#if defined(__GNUC__) +#define MODELICA_NONNULLATTR __attribute__((nonnull)) +#if defined(__GNUC_MINOR__) && (__GNUC__ > 3 && __GNUC_MINOR__ > 8) +#define MODELICA_RETURNNONNULLATTR __attribute__((returns_nonnull)) +#else +#define MODELICA_RETURNNONNULLATTR +#endif +#elif defined(__ATTR_SAL) +#define MODELICA_NONNULLATTR +#define MODELICA_RETURNNONNULLATTR _Ret_z_ /* _Ret_notnull_ and null-terminated */ +#else +#define MODELICA_NONNULLATTR +#define MODELICA_RETURNNONNULLATTR +#endif +#if !defined(__ATTR_SAL) +#undef _In_z_ +#undef _Out_ +#undef _Ret_z_ +#define _In_z_ +#define _Out_ +#define _Ret_z_ +#endif + +MODELICA_EXPORT double ModelicaTime_difftime(int sec, int min, int hour, + int mday, int mon, int year, int refYear); +MODELICA_EXPORT void ModelicaTime_localtime(_Out_ int* ms, _Out_ int* sec, + _Out_ int* min, _Out_ int* hour, _Out_ int* mday, _Out_ int* mon, + _Out_ int* year, double seconds, int refYear) MODELICA_NONNULLATTR; +MODELICA_EXPORT MODELICA_RETURNNONNULLATTR const char* ModelicaTime_strftime( + int sec, int min, int hour, int mday, int mon, int year, + _In_z_ const char* format, int _maxSize) MODELICA_NONNULLATTR; +MODELICA_EXPORT void ModelicaTime_strptime(_Out_ int* ms, _Out_ int* sec, + _Out_ int* min, _Out_ int* hour, _Out_ int* mday, _Out_ int* mon, + _Out_ int* year, _In_z_ const char* buf, + _In_z_ const char* format) MODELICA_NONNULLATTR; + +#endif diff --git a/Modelica/Resources/C-Sources/readme.txt b/Modelica/Resources/C-Sources/readme.txt index 8103e46715..2fe1b74e09 100644 --- a/Modelica/Resources/C-Sources/readme.txt +++ b/Modelica/Resources/C-Sources/readme.txt @@ -6,6 +6,7 @@ to the following object libraries ModelicaInternal.c ModelicaRandom.c ModelicaStrings.c + ModelicaTime.c win32_dirent.c (for Visual C++ on Windows) - ModelicaIO (.lib, .dll, .a, .so, depending on tool and OS) containing: @@ -53,4 +54,4 @@ Additionally, a tool vendor has to provide library "lapack" and this library should be used in the linker when a model is compiled that uses this library in its library annotation. -January 05, 2018. +April 11, 2020. diff --git a/Modelica/Resources/C-Sources/strptime.h b/Modelica/Resources/C-Sources/strptime.h new file mode 100644 index 0000000000..0c2d048814 --- /dev/null +++ b/Modelica/Resources/C-Sources/strptime.h @@ -0,0 +1,557 @@ +/*- + * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code was contributed to The NetBSD Foundation by Klaus Klein. + * Heavily optimised by David Laight + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#ifdef _MSC_VER +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +/* + * We do not implement alternate representations. However, we always + * check whether a given modifier is allowed for a certain conversion. + */ +#define ALT_E 0x01 +#define ALT_O 0x02 +#define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; } + +static char gmt[] = { "GMT" }; +#ifdef TM_ZONE +static char utc[] = { "UTC" }; +#endif + +/* RFC-822/RFC-2822 */ +static const char * const nast[5] = { + "EST", "CST", "MST", "PST", "\0\0\0" +}; +static const char * const nadt[5] = { + "EDT", "CDT", "MDT", "PDT", "\0\0\0" +}; + +#define TM_YEAR_BASE 1900 + +static const char *day[7] = { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" +}; +static const char *abday[7] = { + "Sun","Mon","Tue","Wed","Thu","Fri","Sat" +}; +static const char *mon[12] = { + "January", "February", "March", "April", "May", "June", "July", + "August", "September", "October", "November", "December" +}; +static const char *abmon[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; +static const char *am_pm[2] = { + "AM", "PM" +}; + +static const unsigned char *conv_num (const unsigned char *, int *, + unsigned int, unsigned int); +static const unsigned char *find_string (const unsigned char *, int *, const char * const *, + const char * const *, int); + + +static const char * +strptime (const char *buf, const char *fmt, struct tm *tm) +{ + unsigned char c; + const unsigned char *bp, *ep; + int alt_format, i, split_year = 0, neg = 0, offs; + const char *new_fmt; + + bp = (const unsigned char *)buf; + + while (bp != NULL && (c = *fmt++) != '\0') { + /* Clear 'alternate' modifier prior to new conversion. */ + alt_format = 0; + i = 0; + + /* Eat up white-space. */ + if (isspace(c)) { + while (isspace(*bp)) + bp++; + continue; + } + + if (c != '%') + goto literal; + + +again: + switch (c = *fmt++) { + case '%': /* "%%" is converted to "%". */ +literal: + if (c != *bp++) + return NULL; + LEGAL_ALT(0); + continue; + + /* + * "Alternative" modifiers. Just set the appropriate flag + * and start over again. + */ + case 'E': /* "%E?" alternative conversion modifier. */ + LEGAL_ALT(0); + alt_format |= ALT_E; + goto again; + + case 'O': /* "%O?" alternative conversion modifier. */ + LEGAL_ALT(0); + alt_format |= ALT_O; + goto again; + + /* + * "Complex" conversion rules, implemented through recursion. + */ + case 'c': /* Date and time, using the locale's format. */ + new_fmt = "%x %X"; + goto recurse; + + case 'D': /* The date as "%m/%d/%y". */ + new_fmt = "%m/%d/%y"; + LEGAL_ALT(0); + goto recurse; + + case 'F': /* The date as "%Y-%m-%d". */ + new_fmt = "%Y-%m-%d"; + LEGAL_ALT(0); + goto recurse; + + case 'R': /* The time as "%H:%M". */ + new_fmt = "%H:%M"; + LEGAL_ALT(0); + goto recurse; + + case 'r': /* The time in 12-hour clock representation. */ + new_fmt = "%I:%M:%S %p"; + LEGAL_ALT(0); + goto recurse; + + case 'T': /* The time as "%H:%M:%S". */ + new_fmt = "%H:%M:%S"; + LEGAL_ALT(0); + goto recurse; + + case 'X': /* The time, using the locale's format. */ + new_fmt = "%H:%M:%S"; + goto recurse; + + case 'x': /* The date, using the locale's format. */ + new_fmt = "%m/%d/%y"; + recurse: + bp = (const unsigned char *)strptime((const char *)bp, new_fmt, tm); + LEGAL_ALT(ALT_E); + continue; + + /* + * "Elementary" conversion rules. + */ + case 'A': /* The day of week, using the locale's form. */ + case 'a': + bp = find_string(bp, &tm->tm_wday, day, abday, 7); + LEGAL_ALT(0); + continue; + + case 'B': /* The month, using the locale's form. */ + case 'b': + case 'h': + bp = find_string(bp, &tm->tm_mon, mon, abmon, 12); + LEGAL_ALT(0); + continue; + + case 'C': /* The century number. */ + i = 20; + bp = conv_num(bp, &i, 0, 99); + + i = i * 100 - TM_YEAR_BASE; + if (split_year) + i += tm->tm_year % 100; + split_year = 1; + tm->tm_year = i; + LEGAL_ALT(ALT_E); + continue; + + case 'd': /* The day of month. */ + case 'e': + bp = conv_num(bp, &tm->tm_mday, 1, 31); + LEGAL_ALT(ALT_O); + continue; + + case 'k': /* The hour (24-hour clock representation). */ + LEGAL_ALT(0); + /* FALLTHROUGH */ + case 'H': + bp = conv_num(bp, &tm->tm_hour, 0, 23); + LEGAL_ALT(ALT_O); + continue; + + case 'l': /* The hour (12-hour clock representation). */ + LEGAL_ALT(0); + /* FALLTHROUGH */ + case 'I': + bp = conv_num(bp, &tm->tm_hour, 1, 12); + if (tm->tm_hour == 12) + tm->tm_hour = 0; + LEGAL_ALT(ALT_O); + continue; + + case 'j': /* The day of year. */ + i = 1; + bp = conv_num(bp, &i, 1, 366); + tm->tm_yday = i - 1; + LEGAL_ALT(0); + continue; + + case 'M': /* The minute. */ + bp = conv_num(bp, &tm->tm_min, 0, 59); + LEGAL_ALT(ALT_O); + continue; + + case 'm': /* The month. */ + i = 1; + bp = conv_num(bp, &i, 1, 12); + tm->tm_mon = i - 1; + LEGAL_ALT(ALT_O); + continue; + + case 'p': /* The locale's equivalent of AM/PM. */ + bp = find_string(bp, &i, am_pm, NULL, 2); + if (tm->tm_hour > 11) + return NULL; + tm->tm_hour += i * 12; + LEGAL_ALT(0); + continue; + + case 'S': /* The seconds. */ + bp = conv_num(bp, &tm->tm_sec, 0, 61); + LEGAL_ALT(ALT_O); + continue; + + case 'U': /* The week of year, beginning on sunday. */ + case 'W': /* The week of year, beginning on monday. */ + /* + * XXX This is bogus, as we can not assume any valid + * information present in the tm structure at this + * point to calculate a real value, so just check the + * range for now. + */ + bp = conv_num(bp, &i, 0, 53); + LEGAL_ALT(ALT_O); + continue; + + case 'w': /* The day of week, beginning on sunday. */ + bp = conv_num(bp, &tm->tm_wday, 0, 6); + LEGAL_ALT(ALT_O); + continue; + + case 'u': /* The day of week, monday = 1. */ + bp = conv_num(bp, &i, 1, 7); + tm->tm_wday = i % 7; + LEGAL_ALT(ALT_O); + continue; + + case 'g': + /* The year corresponding to the ISO week + * number but without the century. + */ + bp = conv_num(bp, &i, 0, 99); + continue; + + case 'G': + /* The year corresponding to the ISO week + * number with century. + */ + do + bp++; + while (isdigit(*bp)); + continue; + + case 'V': /* The ISO 8601:1988 week number as decimal */ + bp = conv_num(bp, &i, 0, 53); + continue; + + case 'Y': /* The year. */ + i = TM_YEAR_BASE; /* just for data sanity... */ + bp = conv_num(bp, &i, 0, 9999); + tm->tm_year = i - TM_YEAR_BASE; + LEGAL_ALT(ALT_E); + continue; + + case 'y': /* The year within 100 years of the epoch. */ + /* LEGAL_ALT(ALT_E | ALT_O); */ + bp = conv_num(bp, &i, 0, 99); + + if (split_year) + /* preserve century */ + i += (tm->tm_year / 100) * 100; + else { + split_year = 1; + if (i <= 68) + i = i + 2000 - TM_YEAR_BASE; + else + i = i + 1900 - TM_YEAR_BASE; + } + tm->tm_year = i; + continue; + + case 'Z': + if (strncmp((const char *)bp, gmt, 3) == 0) { + tm->tm_isdst = 0; +#ifdef TM_GMTOFF + tm->TM_GMTOFF = 0; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = gmt; +#endif + bp += 3; + } else { +#if defined(_TM_DEFINED) && !defined(_WIN32_WCE) + _tzset(); + ep = find_string(bp, &i, + (const char * const *)tzname, + NULL, 2); + if (ep != NULL) { + tm->tm_isdst = i; +#ifdef TM_GMTOFF + tm->TM_GMTOFF = -(timezone); +#endif +#ifdef TM_ZONE + tm->TM_ZONE = tzname[i]; +#endif + } + bp = ep; +#endif + } + continue; + + case 'z': + /* + * We recognize all ISO 8601 formats: + * Z = Zulu time/UTC + * [+-]hhmm + * [+-]hh:mm + * [+-]hh + * We recognize all RFC-822/RFC-2822 formats: + * UT|GMT + * North American : UTC offsets + * E[DS]T = Eastern : -4 | -5 + * C[DS]T = Central : -5 | -6 + * M[DS]T = Mountain: -6 | -7 + * P[DS]T = Pacific : -7 | -8 + * Military + * [A-IL-M] = -1 ... -9 (J not used) + * [N-Y] = +1 ... +12 + */ + while (isspace(*bp)) + bp++; + + switch (*bp++) { + case 'G': + if (*bp++ != 'M') + return NULL; + /*FALLTHROUGH*/ + case 'U': + if (*bp++ != 'T') + return NULL; + /*FALLTHROUGH*/ + case 'Z': + tm->tm_isdst = 0; +#ifdef TM_GMTOFF + tm->TM_GMTOFF = 0; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = utc; +#endif + continue; + case '+': + neg = 0; + break; + case '-': + neg = 1; + break; + default: + --bp; + ep = find_string(bp, &i, nast, NULL, 4); + if (ep != NULL) { +#ifdef TM_GMTOFF + tm->TM_GMTOFF = -5 - i; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = __UNCONST(nast[i]); +#endif + bp = ep; + continue; + } + ep = find_string(bp, &i, nadt, NULL, 4); + if (ep != NULL) { + tm->tm_isdst = 1; +#ifdef TM_GMTOFF + tm->TM_GMTOFF = -4 - i; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = __UNCONST(nadt[i]); +#endif + bp = ep; + continue; + } + + if ((*bp >= 'A' && *bp <= 'I') || + (*bp >= 'L' && *bp <= 'Y')) { +#ifdef TM_GMTOFF + /* Argh! No 'J'! */ + if (*bp >= 'A' && *bp <= 'I') + tm->TM_GMTOFF = + ('A' - 1) - (int)*bp; + else if (*bp >= 'L' && *bp <= 'M') + tm->TM_GMTOFF = 'A' - (int)*bp; + else if (*bp >= 'N' && *bp <= 'Y') + tm->TM_GMTOFF = (int)*bp - 'M'; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = NULL; /* XXX */ +#endif + bp++; + continue; + } + return NULL; + } + offs = 0; + for (i = 0; i < 4; ) { + if (isdigit(*bp)) { + offs = offs * 10 + (*bp++ - '0'); + i++; + continue; + } + if (i == 2 && *bp == ':') { + bp++; + continue; + } + break; + } + switch (i) { + case 2: + offs *= 100; + break; + case 4: + i = offs % 100; + if (i >= 60) + return NULL; + /* Convert minutes into decimal */ + offs = (offs / 100) * 100 + (i * 50) / 30; + break; + default: + return NULL; + } + if (neg) + offs = -offs; + tm->tm_isdst = 0; /* XXX */ +#ifdef TM_GMTOFF + tm->TM_GMTOFF = offs; +#endif +#ifdef TM_ZONE + tm->TM_ZONE = NULL; /* XXX */ +#endif + continue; + + /* + * Miscellaneous conversions. + */ + case 'n': /* Any kind of white-space. */ + case 't': + while (isspace(*bp)) + bp++; + LEGAL_ALT(0); + continue; + + + default: /* Unknown/unsupported conversion. */ + return NULL; + } + } + + return ((const char *)bp); +} + + +static const unsigned char * +conv_num (const unsigned char *buf, int *dest, + unsigned int llim, unsigned int ulim) +{ + unsigned int result = 0; + unsigned char ch; + + /* The limit also determines the number of valid digits. */ + unsigned int rulim = ulim; + + ch = *buf; + if (ch < '0' || ch > '9') + return NULL; + + do { + result *= 10; + result += ch - '0'; + rulim /= 10; + ch = *++buf; + } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9'); + + if (result < llim || result > ulim) + return NULL; + + *dest = result; + return buf; +} + +static const unsigned char * +find_string (const unsigned char *bp, int *tgt, const char * const *n1, + const char * const *n2, int c) +{ + int i; + unsigned int len; + + /* check full name - then abbreviated ones */ + for (; n1 != NULL; n1 = n2, n2 = NULL) { + for (i = 0; i < c; i++, n1++) { + len = (unsigned int) strlen(*n1); + if (strncasecmp(*n1, (const char *)bp, len) == 0) { + *tgt = i; + return bp + len; + } + } + } + + /* Nothing matched */ + return NULL; +} diff --git a/Modelica/Resources/Licenses/LICENSE_ModelicaTime.txt b/Modelica/Resources/Licenses/LICENSE_ModelicaTime.txt new file mode 100644 index 0000000000..4141670436 --- /dev/null +++ b/Modelica/Resources/Licenses/LICENSE_ModelicaTime.txt @@ -0,0 +1,27 @@ +Copyright (C) 2020, Modelica Association and contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Modelica/Resources/Licenses/LICENSE_strptime.txt b/Modelica/Resources/Licenses/LICENSE_strptime.txt new file mode 100644 index 0000000000..0fdae1e420 --- /dev/null +++ b/Modelica/Resources/Licenses/LICENSE_strptime.txt @@ -0,0 +1,26 @@ +Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. +All rights reserved. + +This code was contributed to The NetBSD Foundation by Klaus Klein. +Heavily optimised by David Laight + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/Modelica/Utilities/Internal.mo b/Modelica/Utilities/Internal.mo index 66bea583b8..0531b03a3f 100644 --- a/Modelica/Utilities/Internal.mo +++ b/Modelica/Utilities/Internal.mo @@ -252,7 +252,7 @@ end FileSystem; output Integer day "Day"; output Integer mon "Month"; output Integer year "Year"; - external "C" ModelicaInternal_getTime(ms,sec,min,hour,day,mon,year) + external "C" ModelicaInternal_getTime(ms, sec, min, hour, day, mon, year) annotation(Library="ModelicaExternalC"); annotation (Documentation(info="

Syntax

@@ -322,6 +322,42 @@ All returned values are of type Integer and have the following meaning: ")); end getTime; + pure function localTime "Retrieve the local time (in the local time zone) from elapsed seconds since reference year" + extends Modelica.Icons.Function; + input Real seconds "Elapsed seconds since epoch_year"; + input Integer epoch_year = 1970 "Reference year"; + output Integer ms "Millisecond"; + output Integer sec "Second"; + output Integer min "Minute"; + output Integer hour "Hour"; + output Integer day "Day"; + output Integer mon "Month"; + output Integer year "Year"; + external "C90" ModelicaTime_localtime(ms, sec, min, hour, day, mon, year, seconds, epoch_year) + annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.c\""); + annotation (Documentation(info=" +TODO +")); + end localTime; + + pure function stringToTime "Retrieve the local time (in the local time zone) from formatted string" + extends Modelica.Icons.Function; + input String str "Formatted data and time string"; + input String format = "%Y-%m-%d %H:%M:%S" "Format string passed to strptime"; + output Integer ms "Millisecond"; + output Integer sec "Second"; + output Integer min "Minute"; + output Integer hour "Hour"; + output Integer day "Day"; + output Integer mon "Month"; + output Integer year "Year"; + external "C99" ModelicaTime_strptime(ms, sec, min, hour, day, mon, year, str, format) + annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.c\""); + annotation (Documentation(info=" +TODO +")); + end stringToTime; + function dayOfWeek "Return day of week for given date" extends Modelica.Icons.Function; input Integer year "Year"; diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 6e0e9f6636..52675ea4f5 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -244,14 +244,12 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] algorithm (millisecond, second, minute, hour, day, month, year) := getTime(); - dt :=DateTime(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year); + dt := DateTime(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year); end fromSystemTime; function fromEpoch "Create DateTime from elapsed seconds since reference year" - import Modelica.Math.nearestInteger; - import Modelica.Utilities.Time.isLeapYear; - import Modelica.Utilities.Time.daysInYear; + import Modelica.Utilities.Internal.Time.localTime; extends Function; input Real seconds "Elapsed seconds since epoch_year"; @@ -259,12 +257,6 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] output DateTime dt "Date and time"; protected - Integer[2,12] days_passed = {{31,59,90,120,151,181,212,243,273,304,334,365}, - {31,60,91,121,152,182,213,244,274,305,335,366}}; - Real rem; - Integer days, day_of_year, counted_days; - Integer i, j; - Integer millisecond "Millisecond"; Integer second "Second"; Integer minute "Minute"; @@ -275,62 +267,34 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] algorithm - // get milliseconds - millisecond := nearestInteger(mod(abs(seconds),1)*1000); - - // get seconds - second :=nearestInteger(mod(seconds,60)); - - // get minutes - rem :=seconds - second; - minute :=nearestInteger(mod(rem/60, 60)); - - // get hours - rem :=rem - minute*60; - hour :=nearestInteger(mod(rem/3600, 24)); - - // get number of days since epoch year - rem := rem - hour*3600; - days := nearestInteger(rem/(24*3600)); - - // get year - if days >= 0 then - // time is after reference year: count how many years must pass from reference year until 'days' is reached - year := epoch_year; - counted_days := 0; - while counted_days + daysInYear(year) <= days loop - counted_days := counted_days + daysInYear(year); - year :=year + 1; - end while; - else - // time is before reference year: count years downwards - year := epoch_year - 1; - counted_days := -daysInYear(year); - while counted_days > days loop - year := year - 1; - counted_days := counted_days - daysInYear(year); - end while; - end if; + (millisecond, second, minute, hour, day, month, year) := localTime(seconds, epoch_year); + dt := DateTime(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year); - // compute day in current year - day_of_year := days - counted_days + 1; + end fromEpoch; - // get month - // use correct column depending on leap and regular year - j := if isLeapYear(year) then 2 else 1; - for i in 1:12 loop - if days_passed[j,i] >= day_of_year then - month := i; - break; - end if; - end for; + function fromString "Create DateTime from formatted string" + import Modelica.Utilities.Internal.Time.stringToTime; + extends Function; + + input String str "Formatted data and time string"; + input String format = "%Y-%m-%d %H:%M:%S" "Format string passed to strptime"; + output DateTime dt "Date and time"; - // get day - day := if month > 1 then day_of_year - days_passed[j,month-1] else day_of_year; + protected + Integer millisecond "Millisecond"; + Integer second "Second"; + Integer minute "Minute"; + Integer hour "Hour"; + Integer day "Day"; + Integer month "Month"; + Integer year "Year"; + algorithm + + (millisecond, second, minute, hour, day, month, year) := stringToTime(str, format); dt := DateTime(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year); - end fromEpoch; + end fromString; annotation (Documentation(info="

Here the constructor operator(s) is/are defined.

"), Icon( @@ -353,52 +317,24 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] encapsulated operator 'String' "Convert DateTime to string" import Modelica.Utilities.Time.DateTime; - import Modelica.Utilities.Strings.replace; import Modelica.Icons.Function; - function formatted "Use a subset of C strftime() conversion specifiers to format a DateTime record as string" + pure function formatted "Use strftime conversion to format a DateTime record as string" extends Function; - import Modelica.Utilities.Internal.Time.dayOfWeek; - import Modelica.Utilities.Time.weekDays; - import Modelica.Utilities.Time.shortWeekDays; - import Modelica.Utilities.Time.months; - import Modelica.Utilities.Time.shortMonths; - input DateTime dt "Date and time"; - input String format = "%Y-%m-%d %H:%M:%S"; + input String format = "%Y-%m-%d %H:%M:%S" "Format string passed to strftime"; + input Integer maxSize = 128 "Maximal length of formatted string"; output String str "Formatted data and time string"; - - protected - encapsulated function string0 "Create string with minimum length, filled with 0" - import Modelica.Utilities.Strings.replace; - input Integer i; - input Integer l; - output String s0; - algorithm - s0 := replace(String(i, minimumLength=l, leftJustified=false), " ", "0"); - end string0; - - algorithm - str := replace(format, "%%", "%"); - str := replace(str, "%y", string0(mod(dt.year, 100), l=2)); - str := replace(str, "%Y", string0(dt.year, l=4)); - str := replace(str, "%m", string0(dt.month, l=2)); - str := replace(str, "%d", string0(dt.day, l=2)); - str := replace(str, "%H", string0(dt.hour, l=2)); - str := replace(str, "%M", string0(dt.minute, l=2)); - str := replace(str, "%S", string0(dt.second, l=2)); - str := replace(str, "%L", string0(dt.millisecond, l=3)); - str := replace(str, "%a", shortWeekDays[dayOfWeek(dt.year, dt.month, dt.day)]); - str := replace(str, "%A", weekDays[dayOfWeek(dt.year, dt.month, dt.day)]); - str := replace(str, "%b", shortMonths[dt.month]); - str := replace(str, "%B", months[dt.month]); + external "C99" str = ModelicaTime_strftime(dt.second, dt.minute, dt.hour, dt.day, dt.month, dt.year, format, maxSize) + annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.c\""); annotation (Documentation(info="

Syntax

 String(dt)
 String(dt, format)
+String(dt, format, maxSize)
 
@@ -693,45 +629,17 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead points={{-50,0},{50,0}})})); end '-'; - encapsulated function epoch "Convert DateTime to elapsed seconds since custom epoch year" + encapsulated pure function epoch "Convert DateTime to elapsed seconds since custom epoch year" import Modelica.Utilities.Time.DateTime; - import Modelica.Utilities.Time.isLeapYear; import Modelica.Icons.Function; extends Function; - input DateTime dt; + input DateTime dt "Date and time"; input Integer epoch_year = 1970 "Reference year"; - output Real seconds "Elapsed seconds since epoch_year"; - - protected - Integer[2,12] days_passed = {{0,31,59,90,120,151,181,212,243,273,304,334}, - {0,31,60,91,121,152,182,213,244,274,305,335}}; - Integer leap_days, day_of_year; - Integer leap_years_til_epoch; - Integer j; - - // Aux variables for shorter access in code - Integer millisecond=dt.millisecond "Millisecond"; - Integer second=dt.second "Second"; - Integer minute=dt.minute "Minute"; - Integer hour=dt.hour "Hour"; - Integer day=dt.day "Day"; - Integer month=dt.month "Month"; - Integer year=dt.year "Year"; - - algorithm - // get leap years from year 0 until the chosen epoch year - leap_years_til_epoch := integer(epoch_year/4) - integer((epoch_year)/100) + integer((epoch_year)/400); - - // get leap days of passed years since epoch year - leap_days := integer((year-1)/4) - integer((year-1)/100) + integer((year-1)/400) - leap_years_til_epoch; - - // get current day of year and consider leap day if current year is leap year and February has passed - j := if isLeapYear(year) then 2 else 1; - day_of_year := day + days_passed[j,month]; - - seconds := millisecond/1000 + second + 60*(minute + 60*(hour + 24*(day_of_year-1 + leap_days + 365*(year-epoch_year)))); + output Real seconds "Elapsed seconds since epoch_year in the current time zone"; + external "C" seconds = ModelicaTime_difftime(dt.second, dt.minute, dt.hour, dt.day, dt.month, dt.year, epoch_year) + annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.c\""); end epoch; encapsulated function now "Get current system date and time as DateTime" @@ -1427,7 +1335,7 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead output Real totalSeconds "Elapsed seconds"; algorithm - totalSeconds :=d.milliseconds/1000 + d.seconds + 60*(d.minutes + 60*(d.hours + 24*d.days)); + totalSeconds := d.milliseconds/1000 + d.seconds + 60*(d.minutes + 60*(d.hours + 24*d.days)); end inSeconds; @@ -1442,7 +1350,7 @@ String(d, format=\"%%days\") // Should give \"%days\", but gives \"1\" instead algorithm - d_norm :=Duration(totalSeconds=Duration.inSeconds(d)); + d_norm := Duration(totalSeconds=Duration.inSeconds(d)); end normalize; From c725aade51046283c76fe36a541e11c8a8293fe1 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Mon, 13 Apr 2020 15:45:11 +0200 Subject: [PATCH 31/66] Reintroduce handling of milliseconds --- Modelica/Resources/C-Sources/ModelicaTime.c | 51 ++++++---- Modelica/Resources/C-Sources/ModelicaTime.h | 8 +- Modelica/Resources/C-Sources/repl_str.h | 96 +++++++++++++++++++ Modelica/Resources/C-Sources/strptime.h | 17 +++- .../Resources/Licenses/LICENSE_repl_str.txt | 4 + Modelica/Utilities/Internal.mo | 12 +-- Modelica/Utilities/Time.mo | 38 +++----- 7 files changed, 169 insertions(+), 57 deletions(-) create mode 100644 Modelica/Resources/C-Sources/repl_str.h create mode 100644 Modelica/Resources/Licenses/LICENSE_repl_str.txt diff --git a/Modelica/Resources/C-Sources/ModelicaTime.c b/Modelica/Resources/C-Sources/ModelicaTime.c index d31208e7e6..e447837699 100644 --- a/Modelica/Resources/C-Sources/ModelicaTime.c +++ b/Modelica/Resources/C-Sources/ModelicaTime.c @@ -40,9 +40,8 @@ #include #include #include "ModelicaUtilities.h" -#if !defined(__APPLE_CC__) #include "strptime.h" -#endif +#include "repl_str.h" #if !defined(NO_TIME) time_t epoch(int sec, int min, int hour, int mday, int mon, int year) { @@ -66,8 +65,8 @@ time_t epoch(int sec, int min, int hour, int mday, int mon, int year) { } #endif -double ModelicaTime_difftime(int sec, int min, int hour, int mday, int mon, - int year, int refYear) { +double ModelicaTime_difftime(int ms, int sec, int min, int hour, int mday, + int mon, int year, int refYear) { /* Get elapsed seconds w.r.t. reference year */ #if defined(NO_TIME) return 0.0; @@ -76,11 +75,11 @@ double ModelicaTime_difftime(int sec, int min, int hour, int mday, int mon, if (1970 == refYear) { /* Avoid calling mktime for New Year 1970 in local time zone */ const time_t startTime = epoch(0, 0, 0, 2, 1, 1970); - return difftime(endTime, startTime) + 86400.0; + return difftime(endTime, startTime) + 86400.0 + ms/1000.0; } else { const time_t startTime = epoch(0, 0, 0, 1, 1, refYear); - return difftime(endTime, startTime); + return difftime(endTime, startTime) + ms/1000.0; } #endif } @@ -137,13 +136,13 @@ void ModelicaTime_localtime(_Out_ int* ms, _Out_ int* sec, _Out_ int* min, #endif } -_Ret_z_ const char* ModelicaTime_strftime(int sec, int min, int hour, int mday, int mon, - int year, _In_z_ const char* format, int _maxSize) { +_Ret_z_ const char* ModelicaTime_strftime(int ms, int sec, int min, int hour, + int mday, int mon, int year, + _In_z_ const char* format, int _maxSize) { /* Formatted time to string conversion */ #if defined(NO_TIME) return ""; #else - size_t retLen; struct tm* tlocal; const size_t maxSize = (size_t)_maxSize; char* timePtr = ModelicaAllocateString(maxSize); @@ -161,12 +160,28 @@ _Ret_z_ const char* ModelicaTime_strftime(int sec, int min, int hour, int mday, tlocal = localtime(&calendarTime); /* Time fields in local time zone */ #endif - retLen = strftime(timePtr, maxSize, format, tlocal); - if (retLen > 0 && retLen <= maxSize) { - return (const char*)timePtr; - } - else { - return ""; + { + char* format2; + char* format3; + char ms_str[32]; + size_t retLen; + + /* Special handling of milliseconds by additional %L format specifier */ + format2 = repl_str(format, "%%", "%|"); + sprintf(ms_str, "%03d", ms); + format3 = repl_str(format2, "%L", ms_str); + free(format2); + format2 = repl_str(format3, "%|", "%%"); + free(format3); + + retLen = strftime(timePtr, maxSize, format2, tlocal); + free(format2); + if (retLen > 0 && retLen <= maxSize) { + return (const char*)timePtr; + } + else { + return ""; + } } #endif } @@ -186,10 +201,12 @@ void ModelicaTime_strptime(_Out_ int* ms, _Out_ int* sec, _Out_ int* min, *year = 0; #else struct tm tlocal; + int tmp = 0; memset(&tlocal, 0, sizeof(struct tm)); - if (strptime(buf, format, &tlocal)) { + if (NULL != strptime_ms(buf, format, &tlocal, &tmp)) { /* Do not memcpy as you do not know which sizes are in the struct */ + *ms = tmp; *sec = tlocal.tm_sec; *min = tlocal.tm_min; *hour = tlocal.tm_hour; @@ -198,6 +215,7 @@ void ModelicaTime_strptime(_Out_ int* ms, _Out_ int* sec, _Out_ int* min, *year = 1900 + tlocal.tm_year; /* Correct for 4-digit year */ } else { + *ms = 0; *sec = 0; *min = 0; *hour = 0; @@ -205,6 +223,5 @@ void ModelicaTime_strptime(_Out_ int* ms, _Out_ int* sec, _Out_ int* min, *mon = 0; *year = 0; } - *ms = 0; #endif } diff --git a/Modelica/Resources/C-Sources/ModelicaTime.h b/Modelica/Resources/C-Sources/ModelicaTime.h index d07eaa4653..6d67c0357c 100644 --- a/Modelica/Resources/C-Sources/ModelicaTime.h +++ b/Modelica/Resources/C-Sources/ModelicaTime.h @@ -31,7 +31,7 @@ /* The following #define's are available. - NO_TIME : Functions localtime and mktime are not present + NO_TIME : Functions localtime or mktime are not present MODELICA_EXPORT: Prefix used for function calls. If not defined, blank is used Useful definition: - "__declspec(dllexport)" if included in a DLL and the @@ -80,13 +80,13 @@ #define _Ret_z_ #endif -MODELICA_EXPORT double ModelicaTime_difftime(int sec, int min, int hour, - int mday, int mon, int year, int refYear); +MODELICA_EXPORT double ModelicaTime_difftime(int ms, int sec, int min, + int hour, int mday, int mon, int year, int refYear); MODELICA_EXPORT void ModelicaTime_localtime(_Out_ int* ms, _Out_ int* sec, _Out_ int* min, _Out_ int* hour, _Out_ int* mday, _Out_ int* mon, _Out_ int* year, double seconds, int refYear) MODELICA_NONNULLATTR; MODELICA_EXPORT MODELICA_RETURNNONNULLATTR const char* ModelicaTime_strftime( - int sec, int min, int hour, int mday, int mon, int year, + int ms, int sec, int min, int hour, int mday, int mon, int year, _In_z_ const char* format, int _maxSize) MODELICA_NONNULLATTR; MODELICA_EXPORT void ModelicaTime_strptime(_Out_ int* ms, _Out_ int* sec, _Out_ int* min, _Out_ int* hour, _Out_ int* mday, _Out_ int* mon, diff --git a/Modelica/Resources/C-Sources/repl_str.h b/Modelica/Resources/C-Sources/repl_str.h new file mode 100644 index 0000000000..23ab457150 --- /dev/null +++ b/Modelica/Resources/C-Sources/repl_str.h @@ -0,0 +1,96 @@ +/* Portable C string replacement function: repl_str + * https://creativeandcritical.net/str-replace-c + * Created by Laird Shaw + * Released to public domain + */ + +#include +#include +#include + +#if (__STDC_VERSION__ >= 199901L) +#include +#endif + +static char *repl_str(const char *str, const char *from, const char *to) { + + /* Adjust each of the below values to suit your needs. */ + + /* Increment positions cache size initially by this number. */ + size_t cache_sz_inc = 16; + /* Thereafter, each time capacity needs to be increased, + * multiply the increment by this factor. */ + const size_t cache_sz_inc_factor = 3; + /* But never increment capacity by more than this number. */ + const size_t cache_sz_inc_max = 1048576; + + char *pret, *ret = NULL; + const char *pstr2, *pstr = str; + size_t i, count = 0; + #if (__STDC_VERSION__ >= 199901L) + uintptr_t *pos_cache_tmp, *pos_cache = NULL; + #else + ptrdiff_t *pos_cache_tmp, *pos_cache = NULL; + #endif + size_t cache_sz = 0; + size_t cpylen, orglen, retlen, tolen, fromlen = strlen(from); + + /* Find all matches and cache their positions. */ + while ((pstr2 = strstr(pstr, from)) != NULL) { + count++; + + /* Increase the cache size when necessary. */ + if (cache_sz < count) { + cache_sz += cache_sz_inc; + pos_cache_tmp = realloc(pos_cache, sizeof(*pos_cache) * cache_sz); + if (pos_cache_tmp == NULL) { + goto end_repl_str; + } else pos_cache = pos_cache_tmp; + cache_sz_inc *= cache_sz_inc_factor; + if (cache_sz_inc > cache_sz_inc_max) { + cache_sz_inc = cache_sz_inc_max; + } + } + + pos_cache[count-1] = pstr2 - str; + pstr = pstr2 + fromlen; + } + + orglen = pstr - str + strlen(pstr); + + /* Allocate memory for the post-replacement string. */ + if (count > 0) { + tolen = strlen(to); + retlen = orglen + (tolen - fromlen) * count; + } else retlen = orglen; + ret = malloc(retlen + 1); + if (ret == NULL) { + goto end_repl_str; + } + + if (count == 0) { + /* If no matches, then just duplicate the string. */ + strcpy(ret, str); + } else { + /* Otherwise, duplicate the string whilst performing + * the replacements using the position cache. */ + pret = ret; + memcpy(pret, str, pos_cache[0]); + pret += pos_cache[0]; + for (i = 0; i < count; i++) { + memcpy(pret, to, tolen); + pret += tolen; + pstr = str + pos_cache[i] + fromlen; + cpylen = (i == count-1 ? orglen : pos_cache[i+1]) - pos_cache[i] - fromlen; + memcpy(pret, pstr, cpylen); + pret += cpylen; + } + ret[retlen] = '\0'; + } + +end_repl_str: + /* Free the cache and return the post-replacement string, + * which will be NULL in the event of an error. */ + free(pos_cache); + return ret; +} diff --git a/Modelica/Resources/C-Sources/strptime.h b/Modelica/Resources/C-Sources/strptime.h index 0c2d048814..e89ce8ef70 100644 --- a/Modelica/Resources/C-Sources/strptime.h +++ b/Modelica/Resources/C-Sources/strptime.h @@ -27,11 +27,19 @@ * POSSIBILITY OF SUCH DAMAGE. */ +/* Adapted to the needs of the Modelica Standard Library: + + Changelog: + Apr. 13, 2020: by Thomas Beutlich + Renamed to strptime_ms to also consider milliseconds by + the %L format specifier +*/ + #include #include #include -#ifdef _MSC_VER +#ifdef _MSC_VER #define strncasecmp _strnicmp #define strcasecmp _stricmp #endif @@ -85,7 +93,7 @@ static const unsigned char *find_string (const unsigned char *, int *, const cha static const char * -strptime (const char *buf, const char *fmt, struct tm *tm) +strptime_ms (const char *buf, const char *fmt, struct tm *tm, int* ms) { unsigned char c; const unsigned char *bp, *ep; @@ -172,7 +180,7 @@ strptime (const char *buf, const char *fmt, struct tm *tm) case 'x': /* The date, using the locale's format. */ new_fmt = "%m/%d/%y"; recurse: - bp = (const unsigned char *)strptime((const char *)bp, new_fmt, tm); + bp = (const unsigned char *)strptime_ms((const char *)bp, new_fmt, tm, ms); LEGAL_ALT(ALT_E); continue; @@ -496,6 +504,9 @@ strptime (const char *buf, const char *fmt, struct tm *tm) LEGAL_ALT(0); continue; + case 'L': /* The milliseconds. */ + bp = conv_num(bp, ms, 0, 999); + continue; default: /* Unknown/unsupported conversion. */ return NULL; diff --git a/Modelica/Resources/Licenses/LICENSE_repl_str.txt b/Modelica/Resources/Licenses/LICENSE_repl_str.txt new file mode 100644 index 0000000000..c2ad6e852f --- /dev/null +++ b/Modelica/Resources/Licenses/LICENSE_repl_str.txt @@ -0,0 +1,4 @@ +Portable C string replacement function: repl_str +https://creativeandcritical.net/str-replace-c +Created by Laird Shaw +Released to public domain diff --git a/Modelica/Utilities/Internal.mo b/Modelica/Utilities/Internal.mo index 0531b03a3f..9dfe82a79d 100644 --- a/Modelica/Utilities/Internal.mo +++ b/Modelica/Utilities/Internal.mo @@ -211,14 +211,14 @@ package FileSystem external "C" ModelicaInternal_readDirectory(directory,nNames,names) annotation(IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaInternal.h\"", Library="ModelicaExternalC"); end readDirectory; -impure function getNumberOfFiles + impure function getNumberOfFiles "Get number of files and directories in a directory (POSIX functions opendir, readdir, closedir)" - extends Modelica.Icons.Function; - input String directory "Directory name"; - output Integer result + extends Modelica.Icons.Function; + input String directory "Directory name"; + output Integer result "Number of files and directories present in 'directory'"; - external "C" result = ModelicaInternal_getNumberOfFiles(directory) annotation(IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaInternal.h\"", Library="ModelicaExternalC"); -end getNumberOfFiles; + external "C" result = ModelicaInternal_getNumberOfFiles(directory) annotation(IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaInternal.h\"", Library="ModelicaExternalC"); + end getNumberOfFiles; annotation ( Documentation(info=" diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 52675ea4f5..76cacbaeb5 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -326,7 +326,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] input String format = "%Y-%m-%d %H:%M:%S" "Format string passed to strftime"; input Integer maxSize = 128 "Maximal length of formatted string"; output String str "Formatted data and time string"; - external "C99" str = ModelicaTime_strftime(dt.second, dt.minute, dt.hour, dt.day, dt.month, dt.year, format, maxSize) + external "C99" str = ModelicaTime_strftime(dt.millisecond, dt.second, dt.minute, dt.hour, dt.day, dt.month, dt.year, format, maxSize) annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.c\""); annotation (Documentation(info="

Syntax

@@ -356,7 +356,7 @@ via the format string by setting one or more of the conversion spec
%y year without century01, 02, ... 9901, 02, … 99
%Y
%m month as number, zero padded to length 201, 02, ... 1201, 02, … 12
%a
%d day of month, zero padded to length 201, 02, ... 3101, 02, … 31
%H hour, zero padded to length 200, 01, ... 2400, 01, … 24
%M minute, zero padded to length 200, 01, ... 6000, 01, … 60
%S second, zero padded to length 200, 01, ... 6000, 01, … 60
%%single percent character. Not fully supported. See the Limitations section below.single percent character %
@@ -425,7 +425,7 @@ via the format string by setting one or more of the conversion spec
%L millisecond, zero padded to length 3000, 001, ... 999000, 001, … 999
@@ -436,26 +436,10 @@ import Modelica.Utilities.Time.DateTime; dt = DateTime(2020, 12, 24, 00, 01, 02, 003); String(dt) // = \"2020-12-24 00:01:02\" -String(dt, \"\"%a, %b. %d %Y, %H:%M:%S\"\") // = \"Thu, Dec. 24 2020, 00:01:02\" +String(dt, \"%a, %b. %d %Y, %H:%M:%S\") // = \"Thu, Dec. 24 2020, 00:01:02\" String(dt, format=\"%A, %d. %B %y, %H:%M:%S.%L\") // = \"Thursday, 24. December 20, 00:01:02.003\"
- -

Limitations

-

-This function uses simple string replace methods to substitute the conversion specifiers with the appropriate values. -

-

-When additional % characters are included in the format string (via %%) problems can occur, like shown below. -

- -
-
-// ANTI-EXAMPLE - do not use
-String(dt, format=\"%%b\")  // Should give \"%b\", but gives \"Dec.\" instead
-
-
- ")); end formatted; annotation (Documentation(info=" @@ -638,7 +622,7 @@ String(dt, format=\"%%b\") // Should give \"%b\", but gives \"Dec.\" instead input Integer epoch_year = 1970 "Reference year"; output Real seconds "Elapsed seconds since epoch_year in the current time zone"; - external "C" seconds = ModelicaTime_difftime(dt.second, dt.minute, dt.hour, dt.day, dt.month, dt.year, epoch_year) + external "C" seconds = ModelicaTime_difftime(dt.millisecond, dt.second, dt.minute, dt.hour, dt.day, dt.month, dt.year, epoch_year) annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.c\""); end epoch; From 128907ebdd08bef1b3b632af15d17971c3d75813 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Mon, 13 Apr 2020 18:28:12 +0200 Subject: [PATCH 32/66] Utilize locale-independent string conversion --- Modelica/Resources/C-Sources/ModelicaTime.c | 26 +++++++++++++++++---- Modelica/Resources/C-Sources/ModelicaTime.h | 1 + 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Modelica/Resources/C-Sources/ModelicaTime.c b/Modelica/Resources/C-Sources/ModelicaTime.c index e447837699..74b389d331 100644 --- a/Modelica/Resources/C-Sources/ModelicaTime.c +++ b/Modelica/Resources/C-Sources/ModelicaTime.c @@ -35,13 +35,17 @@ #endif #include "ModelicaTime.h" +#include "ModelicaUtilities.h" +#include "strptime.h" +#include "repl_str.h" #include #include +#include #include -#include "ModelicaUtilities.h" -#include "strptime.h" -#include "repl_str.h" +#if !defined(NO_LOCALE) +#include +#endif #if !defined(NO_TIME) time_t epoch(int sec, int min, int hour, int mday, int mon, int year) { @@ -166,7 +170,7 @@ _Ret_z_ const char* ModelicaTime_strftime(int ms, int sec, int min, int hour, char ms_str[32]; size_t retLen; - /* Special handling of milliseconds by additional %L format specifier */ + /* Special handling of milliseconds by non-standard %L format specifier */ format2 = repl_str(format, "%%", "%|"); sprintf(ms_str, "%03d", ms); format3 = repl_str(format2, "%L", ms_str); @@ -174,7 +178,21 @@ _Ret_z_ const char* ModelicaTime_strftime(int ms, int sec, int min, int hour, format2 = repl_str(format3, "%|", "%%"); free(format3); +#if !defined(NO_LOCALE) && (defined(_MSC_VER) && _MSC_VER >= 1400) + { + _locale_t loc = _create_locale(LC_TIME, "C"); + retLen = _strftime_l(timePtr, maxSize, format2, tlocal, loc); + _free_locale(loc); + } +#elif !defined(NO_LOCALE) && (defined(__GLIBC__) && defined(__GLIBC_MINOR__) && ((__GLIBC__ << 16) + __GLIBC_MINOR__ >= (2 << 16) + 3)) + { + locale_t loc = newlocale(LC_TIME, "C", NULL); + retLen = strftime_l(timePtr, maxSize, format2, tlocal, loc); + freelocale(loc); + } +#else retLen = strftime(timePtr, maxSize, format2, tlocal); +#endif free(format2); if (retLen > 0 && retLen <= maxSize) { return (const char*)timePtr; diff --git a/Modelica/Resources/C-Sources/ModelicaTime.h b/Modelica/Resources/C-Sources/ModelicaTime.h index 6d67c0357c..912f4bb0f2 100644 --- a/Modelica/Resources/C-Sources/ModelicaTime.h +++ b/Modelica/Resources/C-Sources/ModelicaTime.h @@ -31,6 +31,7 @@ /* The following #define's are available. + NO_LOCALE : locale.h is not present (e.g. on AVR). NO_TIME : Functions localtime or mktime are not present MODELICA_EXPORT: Prefix used for function calls. If not defined, blank is used Useful definition: From ceb93ccd537bcefdf21f32794de2d9888a1bb947 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Mon, 13 Apr 2020 20:02:18 +0200 Subject: [PATCH 33/66] Fix C++ compilation --- Modelica/Resources/C-Sources/repl_str.h | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Modelica/Resources/C-Sources/repl_str.h b/Modelica/Resources/C-Sources/repl_str.h index 23ab457150..1fa189b871 100644 --- a/Modelica/Resources/C-Sources/repl_str.h +++ b/Modelica/Resources/C-Sources/repl_str.h @@ -27,11 +27,11 @@ static char *repl_str(const char *str, const char *from, const char *to) { char *pret, *ret = NULL; const char *pstr2, *pstr = str; size_t i, count = 0; - #if (__STDC_VERSION__ >= 199901L) +#if (__STDC_VERSION__ >= 199901L) uintptr_t *pos_cache_tmp, *pos_cache = NULL; - #else +#else ptrdiff_t *pos_cache_tmp, *pos_cache = NULL; - #endif +#endif size_t cache_sz = 0; size_t cpylen, orglen, retlen, tolen, fromlen = strlen(from); @@ -42,7 +42,13 @@ static char *repl_str(const char *str, const char *from, const char *to) { /* Increase the cache size when necessary. */ if (cache_sz < count) { cache_sz += cache_sz_inc; +#if defined(__cplusplus) && __STDC_VERSION__ >= 199901L + pos_cache_tmp = (uintptr_t*)realloc(pos_cache, sizeof(*pos_cache) * cache_sz); +#elif defined(__cplusplus) + pos_cache_tmp = (ptrdiff_t*)realloc(pos_cache, sizeof(*pos_cache) * cache_sz); +#else pos_cache_tmp = realloc(pos_cache, sizeof(*pos_cache) * cache_sz); +#endif if (pos_cache_tmp == NULL) { goto end_repl_str; } else pos_cache = pos_cache_tmp; @@ -63,7 +69,11 @@ static char *repl_str(const char *str, const char *from, const char *to) { tolen = strlen(to); retlen = orglen + (tolen - fromlen) * count; } else retlen = orglen; +#if defined(__cplusplus) + ret = (char*)malloc(retlen + 1); +#else ret = malloc(retlen + 1); +#endif if (ret == NULL) { goto end_repl_str; } From ddf7c198e62e792c8f8c319a6ef5da705b225f4e Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Mon, 13 Apr 2020 21:45:22 +0200 Subject: [PATCH 34/66] Update UsersGuide and package doc --- Modelica/Utilities/package.mo | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/Modelica/Utilities/package.mo b/Modelica/Utilities/package.mo index 1dc028524e..4652c6d048 100644 --- a/Modelica/Utilities/package.mo +++ b/Modelica/Utilities/package.mo @@ -125,20 +125,23 @@ email: Dag.Bruck@3ds.com

Acknowledgements

    -
  • This library has been designed by:
    -
    - Dag Brück, Dassault Systèmes AB, Sweden
    - Hilding Elmqvist, previously at Dassault Systèmes AB, Sweden
    - Hans Olsson, Dassault Systèmes AB, Sweden
    - Martin Otter, DLR Oberpfaffenhofen, Germany. -
  • -
  • The library including the C reference implementation has - been implemented by Martin Otter and Dag Brück.
  • -
  • The Examples.calculator demonstration to implement a calculator - with this library is from Hilding Elmqvist.
  • -
  • Helpful comments from Kaj Nyström, PELAB, Linköping, Sweden, - are appreciated, as well as discussions at the 34th, 36th, and 40th - Modelica Design Meetings in Vienna, Linköping, and Dresden.
  • +
  • This library has been designed and developed by:
    +
    + Dag Brück, Dassault Systèmes AB, Sweden
    + Hilding Elmqvist, previously at Dassault Systèmes AB, Sweden
    + Hans Olsson, Dassault Systèmes AB, Sweden
    + Martin Otter, DLR Oberpfaffenhofen, Germany
    + Marco Kessler, Dassault Systèmes Austria GmbH, Austria
    + Thomas Beutlich, Germany.
    +
  • +
  • The library including the C reference implementation has + been implemented by Martin Otter and Dag Brück.
  • +
  • The Examples.calculator demonstration to implement a calculator + with this library is from Hilding Elmqvist.
  • +
  • Helpful comments from Kaj Nyström, PELAB, Linköping, Sweden, + are appreciated, as well as discussions at the 34th, 36th, and 40th + Modelica Design Meetings in Vienna, Linköping, and Dresden.
  • +
  • The Utilities.Time sublibrary was implemented by Marco Kessler and Thomas Beutlich.
")); end Contact; @@ -208,6 +211,8 @@ The following main sublibraries are available: provides functions to interact with the environment. E.g., get or set the working directory or environment variables and to send a command to the default shell. +
  • Time + provides records and functions to work with date and time.
  • From 6135677911e7a0e14aafa6685982e195f4a9a56e Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Mon, 13 Apr 2020 21:45:36 +0200 Subject: [PATCH 35/66] Add wrappers for external functions Force consistency and make SimulationX happy --- Modelica/Utilities/Internal.mo | 42 ++++++++++++++++++++++++++++++++-- Modelica/Utilities/Time.mo | 25 ++++++++++++-------- 2 files changed, 55 insertions(+), 12 deletions(-) diff --git a/Modelica/Utilities/Internal.mo b/Modelica/Utilities/Internal.mo index 9dfe82a79d..02051d32cd 100644 --- a/Modelica/Utilities/Internal.mo +++ b/Modelica/Utilities/Internal.mo @@ -243,6 +243,25 @@ end FileSystem; package Time "Internal package with external functions as interface to date and time" extends Modelica.Icons.InternalPackage; + + pure function diffTime "Convert local time to elapsed seconds since custom epoch year" + extends Modelica.Icons.Function; + input Integer ms "Millisecond"; + input Integer sec "Second"; + input Integer min "Minute"; + input Integer hour "Hour"; + input Integer day "Day"; + input Integer mon "Month"; + input Integer year "Year"; + input Integer epoch_year = 1970 "Reference year"; + output Real seconds "Elapsed seconds since epoch_year in the current time zone"; + external "C" seconds = ModelicaTime_difftime(ms, sec, min, hour, day, mon, year, epoch_year) + annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.c\""); + annotation (Documentation(info=" +TODO +")); + end diffTime; + impure function getTime "Retrieve the local time (in the local time zone)" extends Modelica.Icons.Function; output Integer ms "Millisecond"; @@ -342,7 +361,7 @@ TODO pure function stringToTime "Retrieve the local time (in the local time zone) from formatted string" extends Modelica.Icons.Function; - input String str "Formatted data and time string"; + input String str "Formatted date and time string"; input String format = "%Y-%m-%d %H:%M:%S" "Format string passed to strptime"; output Integer ms "Millisecond"; output Integer sec "Second"; @@ -358,12 +377,31 @@ TODO ")); end stringToTime; + pure function timeToString "Convert the local time (in the local time zone) to string" + extends Modelica.Icons.Function; + input Integer ms "Millisecond"; + input Integer sec "Second"; + input Integer min "Minute"; + input Integer hour "Hour"; + input Integer day "Day"; + input Integer mon "Month"; + input Integer year "Year"; + input String format = "%Y-%m-%d %H:%M:%S" "Format string passed to strftime"; + input Integer maxSize = 128 "Maximal length of formatted string"; + output String str "Formatted date and time string"; + external "C99" str = ModelicaTime_strftime(ms, sec, min, hour, day, mon, year, format, maxSize) + annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.c\""); + annotation (Documentation(info=" +TODO +")); + end timeToString; + function dayOfWeek "Return day of week for given date" extends Modelica.Icons.Function; input Integer year "Year"; input Integer mon=1 "Month"; input Integer day=1 "Day of month"; - output Integer dow(min=1, max=7) "Day of week: 1 = Monday, ..., 6 = Saturday, 7 = Sunday"; + output Integer dow(min=1, max=7) "Day of week: 1 = Monday, ..., 6 = Saturday, 7 = Sunday"; protected constant Integer t[:] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; Integer y = year; diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 76cacbaeb5..c55418dc6d 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -276,7 +276,7 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] import Modelica.Utilities.Internal.Time.stringToTime; extends Function; - input String str "Formatted data and time string"; + input String str "Formatted date and time string"; input String format = "%Y-%m-%d %H:%M:%S" "Format string passed to strptime"; output DateTime dt "Date and time"; @@ -319,16 +319,18 @@ days = leapDays(2000, 2020) // = 5 leap days in range [2000, 2019] import Modelica.Utilities.Time.DateTime; import Modelica.Icons.Function; - pure function formatted "Use strftime conversion to format a DateTime record as string" + function formatted "Use strftime conversion to format a DateTime record as string" + import Modelica.Utilities.Internal.Time.timeToString; extends Function; input DateTime dt "Date and time"; input String format = "%Y-%m-%d %H:%M:%S" "Format string passed to strftime"; input Integer maxSize = 128 "Maximal length of formatted string"; - output String str "Formatted data and time string"; - external "C99" str = ModelicaTime_strftime(dt.millisecond, dt.second, dt.minute, dt.hour, dt.day, dt.month, dt.year, format, maxSize) - annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.c\""); - annotation (Documentation(info=" + output String str "Formatted date and time string"; + + algorithm + str := timeToString(dt.millisecond, dt.second, dt.minute, dt.hour, dt.day, dt.month, dt.year, format, maxSize); + annotation (Inline=true, Documentation(info="

    Syntax

    @@ -613,8 +615,9 @@ String(dt, format=\"%A, %d. %B %y, %H:%M:%S.%L\")  // = \"Thursday, 24. December
                 points={{-50,0},{50,0}})}));
         end '-';
     
    -    encapsulated pure function epoch "Convert DateTime to elapsed seconds since custom epoch year"
    +    encapsulated function epoch "Convert DateTime to elapsed seconds since custom epoch year"
           import Modelica.Utilities.Time.DateTime;
    +      import Modelica.Utilities.Internal.Time.diffTime;
           import Modelica.Icons.Function;
           extends Function;
     
    @@ -622,8 +625,9 @@ String(dt, format=\"%A, %d. %B %y, %H:%M:%S.%L\")  // = \"Thursday, 24. December
           input Integer epoch_year = 1970 "Reference year";
           output Real seconds "Elapsed seconds since epoch_year in the current time zone";
     
    -      external "C" seconds = ModelicaTime_difftime(dt.millisecond, dt.second, dt.minute, dt.hour, dt.day, dt.month, dt.year, epoch_year)
    -        annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.c\"");
    +    algorithm
    +      seconds := diffTime(dt.millisecond, dt.second, dt.minute, dt.hour, dt.day, dt.month, dt.year, epoch_year);
    +      annotation (Inline=true);
         end epoch;
     
         encapsulated function now "Get current system date and time as DateTime"
    @@ -633,7 +637,8 @@ String(dt, format=\"%A, %d. %B %y, %H:%M:%S.%L\")  // = \"Thursday, 24. December
     
           output DateTime now "Current date and time";
         algorithm
    -       now := DateTime.'constructor'.fromSystemTime();
    +      now := DateTime.'constructor'.fromSystemTime();
    +      annotation (Inline=true);
         end now;
     
         annotation (Documentation(info="
    
    From f99dc0da82605c4d0bc03205fc06fb70ea5ee5d9 Mon Sep 17 00:00:00 2001
    From: Thomas Beutlich 
    Date: Mon, 13 Apr 2020 21:53:16 +0200
    Subject: [PATCH 36/66] Use horizontal ellipsis
    
    ---
     Modelica/Utilities/Internal.mo | 14 +++++++-------
     1 file changed, 7 insertions(+), 7 deletions(-)
    
    diff --git a/Modelica/Utilities/Internal.mo b/Modelica/Utilities/Internal.mo
    index 02051d32cd..7d2428a3ed 100644
    --- a/Modelica/Utilities/Internal.mo
    +++ b/Modelica/Utilities/Internal.mo
    @@ -290,25 +290,25 @@ All returned values are of type Integer and have the following meaning:
         Range
         Description
     
    -ms 0 .. 999
    +ms 0 … 999
         Milli-seconds after seconds
     
    -sec 0 .. 59
    +sec 0 … 59
         Seconds after minute
     
    -min 0 .. 59
    +min 0 … 59
         Minutes after hour
     
    -hour 0 .. 23
    +hour 0 … 23
         Hours after midnight
     
    -day 1 .. 31
    +day 1 … 31
         Day of month
     
    -mon 1 .. 12
    +mon 1 … 12
         Current month
     
    -year ≥ 2015
    +year ≥ 1970
         Current year
     
     
    From b552cef7a48ee13a01502581e6b99ef30e8e52fb Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Mon, 13 Apr 2020 22:54:03 +0200 Subject: [PATCH 37/66] Avoid limitation of Duration.'String'.formatted --- Modelica/Utilities/Time.mo | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index c55418dc6d..934923b085 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -865,7 +865,7 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1 d2.milliseconds := d2.milliseconds + d2.seconds*1000; end if; - str := replace(format, "%%", "%"); + str := replace(format, "%%", "%|"); str := replace(str, "%days", String( d2.days)); str := replace(str, "%d", string0(d2.days, l=2)); str := replace(str, "%hours", String( d2.hours)); @@ -876,6 +876,7 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1 str := replace(str, "%S", string0(d2.seconds, l=2)); str := replace(str, "%milliseconds", String( d2.milliseconds)); str := replace(str, "%L", string0(d2.milliseconds, l=3)); + str := replace(str , "%|", "%"); annotation (Documentation(info="

    Syntax

    @@ -962,7 +963,7 @@ If e.g., only days and hours are part of the format string, minutes, seconds and %% - single percent character. Not fully supported. See the Limitations section below. + single percent character % @@ -979,22 +980,6 @@ String(d, format=\"%Hh %S.%L''\") // = \"26h 184.005''\" (days are included in String(d, format=\"%days'\") // = \"1\" (only full days are shown, rest is stripped off) - -

    Limitations

    -

    -This function uses simple string replace methods to exchange the conversion specifiers with the appropriate values. -

    -

    -When additional % characters are included in the format string (via %%) problems can occur, like shown below. -

    - -
    -
    -// ANTI-EXAMPLE - do not use
    -String(d, format=\"%%days\")  // Should give \"%days\", but gives \"1\" instead
    -
    -
    - ")); end formatted; annotation (Documentation(info=" From 957cc3903b15c426eae6a7a4f12b3031e9af5425 Mon Sep 17 00:00:00 2001 From: mkr7 Date: Tue, 14 Apr 2020 12:04:45 +0200 Subject: [PATCH 38/66] Fix example for string formatting of Duration --- Modelica/Utilities/Time.mo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 934923b085..d48de58828 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -972,7 +972,7 @@ If e.g., only days and hours are part of the format string, minutes, seconds and
     import Modelica.Utilities.Time.Duration;
    -d = Duration(days=1, hour=2, minutes=3, seconds=4, milliseconds=5);
    +d = Duration(days=1, hours=2, minutes=3, seconds=4, milliseconds=5);
     
     String(d)                        // = \"1d 2h 3min 4s 5ms\"
     String(d, \"%hoursh %secondss\")   // = \"26h 184s\" (days are included in hours, minutes in seconds, ms stripped off)
    
    From ac44252db963ec3baf773e12156bb8b186fe8981 Mon Sep 17 00:00:00 2001
    From: mkr7 
    Date: Tue, 14 Apr 2020 15:33:25 +0200
    Subject: [PATCH 39/66] Make Duration constructors unambiguous
    
    This requires fromSeconds to be the new default.
    And it's not possible anymore to create Durations by specifying e.g. hour only.
    But unless you write tests or doc, this is not really needed I would say.
    ---
     Modelica/Utilities/Time.mo | 19 ++++++++++---------
     ModelicaTest/Utilities.mo  |  2 +-
     2 files changed, 11 insertions(+), 10 deletions(-)
    
    diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo
    index d48de58828..8d9d017cd7 100644
    --- a/Modelica/Utilities/Time.mo
    +++ b/Modelica/Utilities/Time.mo
    @@ -708,11 +708,11 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1
           function fromInput "Create Duration field by field from user input"
             extends Function;
     
    -        input Integer days=0 "Days";
    -        input Integer hours=0 "Hours";
    -        input Integer minutes=0 "Minutes";
    -        input Integer seconds=0 "Seconds";
    -        input Integer milliseconds=0 "Milliseconds";
    +        input Integer days "Days";
    +        input Integer hours "Hours";
    +        input Integer minutes "Minutes";
    +        input Integer seconds "Seconds";
    +        input Integer milliseconds "Milliseconds";
             output Duration d(days=days, hours=hours, minutes=minutes, seconds=seconds, milliseconds=milliseconds) "Duration";
     
           algorithm
    @@ -767,7 +767,7 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1
             extends Function;
             import Modelica.Math.nearestInteger;
     
    -        input Real totalSeconds "Duration in seconds. Decimal place is converted to milliseconds";
    +        input Real totalSeconds=0 "Duration in seconds. Decimal place is converted to milliseconds";
             output Duration d "Duration with input converted to seconds and milliseconds";
     
           protected
    @@ -1385,9 +1385,9 @@ The examples below demonstrate the different methods to create a Duration record
     import Modelica.Utilities.Time.Duration;
     
     // Create Duration records from direct input
    -Duration();                                      // create Duration record using default constructor, which is fromInput
    -Duration.'constructor'.fromInput(hours=10);      // explicit call of constructor
    -Duration(days=1, seconds=100);                   // automatic selection of constructor fromInput
    +Duration.'constructor'.fromInput(10, 0, 0, 0, 0);                  // explicit call of constructor
    +Duration(days=10, hours=0, minutes=0, seconds=00, milliseconds=0); // automatic selection of constructor fromInput
    +Duration(10, 0, 0, 0, 0);                                          // automatic selection of constructor fromInput
     
     // Create Duration records from two DateTimes
     import Modelica.Utilities.Time.DateTime;
    @@ -1398,6 +1398,7 @@ Duration(dt1, dt2);                              // automatic selection of const
     Duration(dt1=dt1, dt2=dt2);                      // automatic selection of constructor via names
     
     // Create Duration records manually using the elapsed seconds since the given epoch year
    +Duration();                                      // create Duration record using default constructor, which is fromSeconds
     Duration.'constructor'.fromSeconds(1);           // explicit call of constructor
     Duration(totalSeconds=1.5);                      // automatic selection of constructor via name
     
    diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index c0ff524a99..53df25bf28 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -846,7 +846,7 @@ extends Modelica.Icons.ExamplesPackage; end for; // sum - assert(sum(Duration(days=i) for i in 1:2) == Duration(days=3), "sum failed"); + assert(sum(Duration(i, 0, 0, 0, 0) for i in 1:2) == Duration(3, 0, 0, 0, 0), "sum failed"); // return result ok := true; From e55abd50108b0ff9633c45ed02128c213d722025 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Tue, 14 Apr 2020 22:15:44 +0200 Subject: [PATCH 40/66] Fix more corner cases of ModelicaTime_difftime --- Modelica/Resources/C-Sources/ModelicaTime.c | 29 +++++++++++++++------ 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/Modelica/Resources/C-Sources/ModelicaTime.c b/Modelica/Resources/C-Sources/ModelicaTime.c index 74b389d331..90de7a4993 100644 --- a/Modelica/Resources/C-Sources/ModelicaTime.c +++ b/Modelica/Resources/C-Sources/ModelicaTime.c @@ -75,15 +75,28 @@ double ModelicaTime_difftime(int ms, int sec, int min, int hour, int mday, #if defined(NO_TIME) return 0.0; #else - const time_t endTime = epoch(sec, min, hour, mday, mon, year); - if (1970 == refYear) { - /* Avoid calling mktime for New Year 1970 in local time zone */ - const time_t startTime = epoch(0, 0, 0, 2, 1, 1970); - return difftime(endTime, startTime) + 86400.0 + ms/1000.0; + if (1 == mday && 1 == mon) { + if (year == refYear) { + return ms/1000.0; + } + else if (1970 == year) { + /* Avoid calling mktime for New Year 1970 in local time zone */ + const time_t endTime = epoch(sec, min, hour, 2, 1, 1970); + const time_t startTime = epoch(0, 0, 0, 1, 1, refYear); + return difftime(endTime, startTime) - 86400.0 + ms/1000.0; + } } - else { - const time_t startTime = epoch(0, 0, 0, 1, 1, refYear); - return difftime(endTime, startTime) + ms/1000.0; + { + const time_t endTime = epoch(sec, min, hour, mday, mon, year); + if (1970 == refYear) { + /* Avoid calling mktime for New Year 1970 in local time zone */ + const time_t startTime = epoch(0, 0, 0, 2, 1, 1970); + return difftime(endTime, startTime) + 86400.0 + ms/1000.0; + } + else { + const time_t startTime = epoch(0, 0, 0, 1, 1, refYear); + return difftime(endTime, startTime) + ms/1000.0; + } } #endif } From f7fa01d2886698031e81065f6737de0d577e1705 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Tue, 14 Apr 2020 23:20:20 +0200 Subject: [PATCH 41/66] Fix consideration of DST --- Modelica/Resources/C-Sources/ModelicaTime.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Modelica/Resources/C-Sources/ModelicaTime.c b/Modelica/Resources/C-Sources/ModelicaTime.c index 90de7a4993..60618338f8 100644 --- a/Modelica/Resources/C-Sources/ModelicaTime.c +++ b/Modelica/Resources/C-Sources/ModelicaTime.c @@ -52,13 +52,15 @@ time_t epoch(int sec, int min, int hour, int mday, int mon, int year) { struct tm tlocal; time_t calendarTime; - memset(&tlocal, 0, sizeof(struct tm)); tlocal.tm_sec = sec; tlocal.tm_min = min; tlocal.tm_hour = hour; tlocal.tm_mday = mday; tlocal.tm_mon = mon - 1; tlocal.tm_year = year - 1900; + tlocal.tm_isdst = -1; + tlocal.tm_wday = 0; + tlocal.tm_yday = 0; calendarTime = mktime(&tlocal); if (-1 == calendarTime) { From 742dde0b8b446ca38512c5a50a8f4ed367dcc072 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Wed, 15 Apr 2020 08:19:25 +0200 Subject: [PATCH 42/66] Fix corner case --- Modelica/Resources/C-Sources/ModelicaTime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modelica/Resources/C-Sources/ModelicaTime.c b/Modelica/Resources/C-Sources/ModelicaTime.c index 60618338f8..ea942ac5b5 100644 --- a/Modelica/Resources/C-Sources/ModelicaTime.c +++ b/Modelica/Resources/C-Sources/ModelicaTime.c @@ -79,7 +79,7 @@ double ModelicaTime_difftime(int ms, int sec, int min, int hour, int mday, #else if (1 == mday && 1 == mon) { if (year == refYear) { - return ms/1000.0; + return 60 * (60 * hour + min) + sec + ms/1000.0; } else if (1970 == year) { /* Avoid calling mktime for New Year 1970 in local time zone */ From c4a8a442e23d655772413758d6a0846eb29cbe80 Mon Sep 17 00:00:00 2001 From: mkr7 Date: Wed, 15 Apr 2020 11:57:25 +0200 Subject: [PATCH 43/66] Fix subtraction test of DateTime Creating a Duration does not work anymore that way, due to "e547700 - Make Duration constructors unambiguous" --- ModelicaTest/Utilities.mo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index 53df25bf28..ba610cd150 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -686,8 +686,8 @@ extends Modelica.Icons.ExamplesPackage; assert(dt3<=dt3, "dt3<=dt3 failed"); // - subtract - assert(dt2-dt1==Duration(milliseconds=1), "dt2-dt1 failed"); - assert(dt1-dt2==Duration(milliseconds=-1), "dt1-dt2 failed"); + assert(dt2-dt1==Duration(days=0, hours=0, minutes=0, seconds=0, milliseconds=1), "dt2-dt1 failed"); + assert(dt1-dt2==Duration(days=0, hours=0, minutes=0, seconds=0, milliseconds=-1), "dt1-dt2 failed"); // return result From a9073f11a4d663d6bf6de989bb8e044a8c2ee66b Mon Sep 17 00:00:00 2001 From: mkr7 Date: Wed, 15 Apr 2020 12:31:11 +0200 Subject: [PATCH 44/66] Add tests for nearestInteger --- ModelicaTest/Math.mo | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ModelicaTest/Math.mo b/ModelicaTest/Math.mo index 26d07b7cc9..38e3dcfd34 100644 --- a/ModelicaTest/Math.mo +++ b/ModelicaTest/Math.mo @@ -32,6 +32,14 @@ extends Modelica.Icons.ExamplesPackage; assert(Math.isPowerOf2(1), "isPowerOf2(1) is wrong"); assert(Math.isPowerOf2(4), "isPowerOf2(4) is wrong"); assert(not Math.isPowerOf2(9), "isPowerOf2(9) is wrong"); + + assert(Math.nearestInteger(1.4999)==1, "nearesetInteger(1.4999) failed"); + assert(Math.nearestInteger(1.5)==2, "nearesetInteger(1.5) failed"); + assert(Math.nearestInteger(-1.4999)==-1, "nearesetInteger(-1.4999) failed"); + assert(Math.nearestInteger(-1.5)==-2, "nearesetInteger(-1.5) failed"); + // Test deactivated - would fail with current implementation + // assert(Math.nearestInteger(1.49999999999999999999)==1, "nearestInteger border case failed"); + ok:=true; end ScalarFunctions; From 1ce4579b2b70d83af9963e59fb57e565807d4d89 Mon Sep 17 00:00:00 2001 From: Marco Kessler Date: Wed, 15 Apr 2020 13:33:06 +0200 Subject: [PATCH 45/66] Fix naming in nearestInteger tests Co-Authored-By: Thomas Beutlich --- ModelicaTest/Math.mo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ModelicaTest/Math.mo b/ModelicaTest/Math.mo index 38e3dcfd34..df9d43d692 100644 --- a/ModelicaTest/Math.mo +++ b/ModelicaTest/Math.mo @@ -33,10 +33,10 @@ extends Modelica.Icons.ExamplesPackage; assert(Math.isPowerOf2(4), "isPowerOf2(4) is wrong"); assert(not Math.isPowerOf2(9), "isPowerOf2(9) is wrong"); - assert(Math.nearestInteger(1.4999)==1, "nearesetInteger(1.4999) failed"); - assert(Math.nearestInteger(1.5)==2, "nearesetInteger(1.5) failed"); - assert(Math.nearestInteger(-1.4999)==-1, "nearesetInteger(-1.4999) failed"); - assert(Math.nearestInteger(-1.5)==-2, "nearesetInteger(-1.5) failed"); + assert(Math.nearestInteger(1.4999)==1, "nearestInteger(1.4999) failed"); + assert(Math.nearestInteger(1.5)==2, "nearestInteger(1.5) failed"); + assert(Math.nearestInteger(-1.4999)==-1, "nearestInteger(-1.4999) failed"); + assert(Math.nearestInteger(-1.5)==-2, "nearestInteger(-1.5) failed"); // Test deactivated - would fail with current implementation // assert(Math.nearestInteger(1.49999999999999999999)==1, "nearestInteger border case failed"); From 1c9814c5d15b5fa3458686a498550aa8219db06e Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Wed, 15 Apr 2020 19:16:48 +0200 Subject: [PATCH 46/66] Implement test for Duration.'0' --- ModelicaTest/Utilities.mo | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index ba610cd150..58b04d6876 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -815,7 +815,9 @@ extends Modelica.Icons.ExamplesPackage; assert(ref==act, "d1/2 failed"); // '0' - // todo + ref := Duration(days=0, hours=0, minutes=0, seconds=0, milliseconds=0); + act := Duration.'0'(); + assert(ref==act, "'0' failed"); // asVector ref_v :={0, 23, 59, 59, 999}; From 78283c920567365cf8acde74c4499a2c22e9889b Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Wed, 15 Apr 2020 19:46:02 +0200 Subject: [PATCH 47/66] Fix ModelicaTest.Utilities.Time --- ModelicaTest/Utilities.mo | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index 58b04d6876..a8b4b057e9 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -392,19 +392,20 @@ extends Modelica.Icons.ExamplesPackage; function Time "Test functions of Modelica.Utilities.Time" import Modelica.Utilities.Streams; + import Modelica.Utilities.Time.{DateTime, dayOfWeek, getTime, isLeapYear, leapDays}; extends Modelica.Icons.Function; input String logFile="ModelicaTestLog.txt" "Filename where the log is stored"; input String weekDays[7] = Modelica.Utilities.Time.weekDays "Array of week days"; output Boolean ok; protected - Modelica.Utilities.Types.TimeType now; + DateTime now; Integer dow(min=1, max=7) "Day of week"; algorithm Streams.print("... Test of Modelica.Utilities.Time"); Streams.print("... Test of Modelica.Utilities.Time", logFile); - now := Modelica.Utilities.Time.getTime(); + now := getTime(); Streams.print(" ms = " + String(now.millisecond)); Streams.print(" sec = " + String(now.second)); Streams.print(" min = " + String(now.minute)); @@ -412,26 +413,25 @@ extends Modelica.Icons.ExamplesPackage; Streams.print(" day = " + String(now.day)); Streams.print(" mon = " + String(now.month)); Streams.print(" year = " + String(now.year)); - dow := Modelica.Utilities.Time.dayOfWeek(now); + dow := dayOfWeek(now); Streams.print(" dow = " + weekDays[dow]); - dow := Modelica.Utilities.Time.dayOfWeek( - Modelica.Utilities.Types.TimeType(year=2019, month=12, day=8, hour=12, minute=0, second=0, millisecond=0)); + dow := dayOfWeek(DateTime(year=2019, month=12, day=8, hour=12, minute=0, second=0, millisecond=0)); assert(7 == dow, "Time.dayOfWeek failed"); - assert(not Modelica.Utilities.Time.isLeapYear(1900), "Time.isLeapYear failed"); - assert(Modelica.Utilities.Time.isLeapYear(2000), "Time.isLeapYear failed"); - assert(not Modelica.Utilities.Time.isLeapYear(2019), "Time.isLeapYear failed"); - assert(Modelica.Utilities.Time.isLeapYear(2020), "Time.isLeapYear failed"); - - assert(0 == Modelica.Utilities.Time.leapDays(2000, 2000), "Time.leapDays failed"); - assert(5 == Modelica.Utilities.Time.leapDays(2000, 2020), "Time.leapDays failed"); - assert(-5 == Modelica.Utilities.Time.leapDays(2020, 2000), "Time.leapDays failed"); - assert(5 == Modelica.Utilities.Time.leapDays(-2020, -2000), "Time.leapDays failed"); - assert(98 == Modelica.Utilities.Time.leapDays(1600, 2001), "Time.leapDays failed"); - assert(97 == Modelica.Utilities.Time.leapDays(1601, 2001), "Time.leapDays failed"); - assert(97 == Modelica.Utilities.Time.leapDays(1600, 2000), "Time.leapDays failed"); - assert(96 == Modelica.Utilities.Time.leapDays(1601, 2000), "Time.leapDays failed"); + assert(not isLeapYear(1900), "Time.isLeapYear failed"); + assert(isLeapYear(2000), "Time.isLeapYear failed"); + assert(not isLeapYear(2019), "Time.isLeapYear failed"); + assert(isLeapYear(2020), "Time.isLeapYear failed"); + + assert(0 == leapDays(2000, 2000), "Time.leapDays failed"); + assert(5 == leapDays(2000, 2020), "Time.leapDays failed"); + assert(-5 == leapDays(2020, 2000), "Time.leapDays failed"); + assert(5 == leapDays(-2020, -2000), "Time.leapDays failed"); + assert(98 == leapDays(1600, 2001), "Time.leapDays failed"); + assert(97 == leapDays(1601, 2001), "Time.leapDays failed"); + assert(97 == leapDays(1600, 2000), "Time.leapDays failed"); + assert(96 == leapDays(1601, 2000), "Time.leapDays failed"); ok := true; end Time; From 872188e07c9570a912c0b61c6519815bc05fe301 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Fri, 17 Apr 2020 20:51:58 +0200 Subject: [PATCH 48/66] Fix Duration.'constructor'.fromDateTimes for negative time span --- Modelica/Utilities/Time.mo | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 8d9d017cd7..29ea8c3e38 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -730,9 +730,7 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1 protected Real diff; - Integer sign_; Real e1, e2; - DateTime dt_tmp; Integer days "Elapsed days"; Integer hours "Elapsed hours"; Integer minutes "Elapsed minutes"; @@ -740,13 +738,10 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1 Integer milliseconds "Elapsed milliseconds"; algorithm - dt_tmp := dt2; - e1 := DateTime.epoch(dt1, epoch_year=dt1.year); - e2 := DateTime.epoch(dt_tmp, epoch_year=dt1.year); + e2 := DateTime.epoch(dt2, epoch_year=dt1.year); diff := abs(e2 - e1); - sign_ := sign(e2 - e1); days := integer(diff/(24*3600)); hours := integer(diff/3600 - days*24); @@ -754,12 +749,10 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1 seconds := integer(diff - (days*24 + hours)*3600 - minutes*60); milliseconds := nearestInteger(rem(diff,1)*1000); - hours := sign_*hours; - minutes := sign_*minutes; - seconds := sign_*seconds; - milliseconds := sign_*milliseconds; - d := Duration(days=days, hours=hours, minutes=minutes, seconds=seconds, milliseconds=milliseconds); + if e2 < e1 then + d := -d; + end if; end fromDateTimes; @@ -876,7 +869,7 @@ DateTime.'constructor'.fromEpoch(1000, 2020); // explicit call of constructor. 1 str := replace(str, "%S", string0(d2.seconds, l=2)); str := replace(str, "%milliseconds", String( d2.milliseconds)); str := replace(str, "%L", string0(d2.milliseconds, l=3)); - str := replace(str , "%|", "%"); + str := replace(str, "%|", "%"); annotation (Documentation(info="

    Syntax

    From 9c8090d04e63cc823e85b709b6b9c85e9ac7df4f Mon Sep 17 00:00:00 2001 From: mkr7 Date: Mon, 20 Apr 2020 13:11:12 +0200 Subject: [PATCH 49/66] Add another test for negative durations ... as the one we had did not catch negative durations with multiple days. --- ModelicaTest/Utilities.mo | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index a8b4b057e9..c529d811ab 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -737,6 +737,14 @@ extends Modelica.Icons.ExamplesPackage; act := Duration(totalSeconds=-1.499); assert(ref==act, "constructor test 3 failed (fromSeconds vs fromDateTimes, negative result)"); + ref := Duration(days=-8, hours=-23, minutes=-30, seconds=-0, milliseconds=0); + act := Duration(DateTime("2020-1-10 0:0:0"), DateTime("2020-1-1 0:30:0")); + assert(ref==act, "constructor test 4a failed (fromInput vs fromDateTimes, negative result)"); + + ref_r := -775800.0; + act_r := Duration.inSeconds(act); + assert(ref_r==act_r, "constructor test 4b failed (real vs fromDateTimes in seconds, negative result)"); + // 'String'.formated ref_s :="1d 1h 1min 1s 1ms"; act_s :=String(d1); From e66052bdc28de70d3d67357ecacbd4637c712855 Mon Sep 17 00:00:00 2001 From: mkr7 Date: Mon, 20 Apr 2020 13:55:59 +0200 Subject: [PATCH 50/66] Support + and - for DateTime with Duration --- Modelica/Utilities/Time.mo | 69 ++++++++++++++++++++++++++++++++++++-- ModelicaTest/Utilities.mo | 13 ++++++- 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index 29ea8c3e38..a8abe93996 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -580,11 +580,61 @@ String(dt, format=\"%A, %d. %B %y, %H:%M:%S.%L\") // = \"Thursday, 24. December end '<='; + encapsulated operator '+' "Add to DateTime" + function add_Duration1 "Add Duration d to DateTime dt" + + import Modelica.Utilities.Time.{DateTime, Duration}; + import Modelica.Icons.Function; + extends Function; + + input DateTime dt; + input Duration d; + output DateTime result "= dt + d"; + + algorithm + result := DateTime.'constructor'.fromEpoch(DateTime.epoch(dt) + Duration.inSeconds(d)); + + end add_Duration1; + + function add_Duration2 "Add Duration d to DateTime dt - swapped input" + + import Modelica.Utilities.Time.{DateTime, Duration}; + import Modelica.Icons.Function; + extends Function; + + input Duration d; + input DateTime dt; + output DateTime result "= d + dt"; + + algorithm + result := DateTime.'+'.add_Duration1(dt, d); + + end add_Duration2; + + annotation (Icon(graphics={ + Rectangle( + lineColor={200,200,200}, + fillColor={248,248,248}, + fillPattern=FillPattern.HorizontalCylinder, + extent={{-100,-102},{100,98}}, + radius=25), + Rectangle( + lineColor={128,128,128}, + extent={{-100,-102},{100,98}}, + radius=25), + Line( + points={{-50,0},{50,0}}), + Line( + points={{-50,0},{50,0}}, + origin={0,0}, + rotation=90)})); + end '+'; + encapsulated operator '-' "Binary minus" import Modelica.Utilities.Time.DateTime; import Modelica.Icons.Function; - function subtract "Return time delta between dt2 and dt1 as Duration" + function subtract_DateTime "Return time delta between dt2 and dt1 as Duration" extends Function; import Modelica.Utilities.Time.Duration; @@ -596,7 +646,22 @@ String(dt, format=\"%A, %d. %B %y, %H:%M:%S.%L\") // = \"Thursday, 24. December algorithm result := Duration.'constructor'.fromDateTimes(dt2, dt1); - end subtract; + end subtract_DateTime; + + function subtract_Duration "Subtract Duration d from DateTime dt" + extends Function; + + import Modelica.Utilities.Time.Duration; + + input DateTime dt; + input Duration d; + output DateTime result "= dt - d"; + + algorithm + result := DateTime.'constructor'.fromEpoch(DateTime.epoch(dt) - Duration.inSeconds(d)); + + end subtract_Duration; + annotation (Documentation(info="

    Here the binary minus operator is defined.

    "), Icon(coordinateSystem(preserveAspectRatio=false, extent={{-100,-100}, diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index c529d811ab..9d15f6a7ae 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -685,10 +685,21 @@ extends Modelica.Icons.ExamplesPackage; assert(dt2<=dt3, "dt2<=dt3 failed"); assert(dt3<=dt3, "dt3<=dt3 failed"); - // - subtract + // + add Duration + dt0 := DateTime("2020-04-20 04:30:00"); + d := Duration(days=11, hours=2, minutes=3, seconds=100, milliseconds=0); + ref_dt := DateTime("2020-05-01 06:34:40"); + assert(dt0+d==ref_dt, "DateTime+Duration failed"); + assert(d+dt0==ref_dt, "Duration+DateTime failed"); + + // - subtract DateTime assert(dt2-dt1==Duration(days=0, hours=0, minutes=0, seconds=0, milliseconds=1), "dt2-dt1 failed"); assert(dt1-dt2==Duration(days=0, hours=0, minutes=0, seconds=0, milliseconds=-1), "dt1-dt2 failed"); + // - subtract Duration + act_dt := DateTime("2020-04-20 04:30:00") - Duration(days=10, hours=2, minutes=3, seconds=4, milliseconds=0); + ref_dt := DateTime("2020-04-10 02:26:56"); + assert(act_dt==ref_dt, "DateTime-Duration failed"); // return result ok := true; From f1673fb34abb69b53483a85d50bf1062f6aa7fd5 Mon Sep 17 00:00:00 2001 From: mkr7 Date: Mon, 20 Apr 2020 13:57:40 +0200 Subject: [PATCH 51/66] Optionally skip DateTime tests for years before 1970 ... as this is not supported at the moment. --- ModelicaTest/Utilities.mo | 54 +++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index 9d15f6a7ae..b0d827718e 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -445,13 +445,15 @@ extends Modelica.Icons.ExamplesPackage; extends Modelica.Icons.Function; input String logFile="ModelicaTestLog.txt" "Filename where the log is stored"; + input Boolean test_past = true "Optionally run / skip tests for DateTimes before epoch year 1970"; output Boolean ok; protected Real act_r, ref_r; String act_s, ref_s; DateTime act_dt, ref_dt; - DateTime dt1, dt2, dt3; + DateTime dt0, dt1, dt2, dt3; + Duration d; Integer _ "Dummy to swallow return value"; algorithm @@ -597,38 +599,40 @@ extends Modelica.Icons.ExamplesPackage; assert(ref_dt==act_dt, "constructor test failed (end of leap year)"); assert(ref_r==act_r, "conversion to epoch failed (end of leap year)"); - // lowest value for 32bit integer - // this test is not relevant, as datatype Real is used to count - // seconds from epoch. Would be needed, if Integer is used instead - // (for whatever reason). But it does not hurt to have it, so we keep it. - ref_dt:= DateTime(1901, 12, 13, 20, 45, 53, 0); - ref_r := -2147483647; + if test_past then + // lowest value for 32bit integer + // this test is not relevant, as datatype Real is used to count + // seconds from epoch. Would be needed, if Integer is used instead + // (for whatever reason). But it does not hurt to have it, so we keep it. + ref_dt:= DateTime(1901, 12, 13, 20, 45, 53, 0); + ref_r := -2147483647; - act_dt:= DateTime(ref_r); - act_r := DateTime.epoch(ref_dt); + act_dt:= DateTime(ref_r); + act_r := DateTime.epoch(ref_dt); - assert(ref_dt==act_dt, "constructor test failed (lowest possible value for 32bit integer)"); - assert(ref_r==act_r, "conversion to epoch failed (lowest possible value for 32bit integer)"); + assert(ref_dt==act_dt, "constructor test failed (lowest possible value for 32bit integer)"); + assert(ref_r==act_r, "conversion to epoch failed (lowest possible value for 32bit integer)"); - // regular year in past - ref_dt:= DateTime(1910, 12, 31, 1, 59, 59, 0); - ref_r := -1861999201; + // regular year in past + ref_dt:= DateTime(1910, 12, 31, 1, 59, 59, 0); + ref_r := -1861999201; - act_dt:= DateTime(ref_r); - act_r := DateTime.epoch(ref_dt); + act_dt:= DateTime(ref_r); + act_r := DateTime.epoch(ref_dt); - assert(ref_dt==act_dt, "constructor test failed (regular year before epoch)"); - assert(ref_r==act_r, "conversion to epoch failed (regular year before epoch)"); + assert(ref_dt==act_dt, "constructor test failed (regular year before epoch)"); + assert(ref_r==act_r, "conversion to epoch failed (regular year before epoch)"); - // leap year in past - ref_dt:= DateTime(1912, 12, 31, 1, 59, 59, 0); - ref_r := -1798840801; + // leap year in past + ref_dt:= DateTime(1912, 12, 31, 1, 59, 59, 0); + ref_r := -1798840801; - act_dt:= DateTime(ref_r); - act_r := DateTime.epoch(ref_dt); + act_dt:= DateTime(ref_r); + act_r := DateTime.epoch(ref_dt); - assert(ref_dt==act_dt, "constructor test failed (leap regular year before epoch)"); - assert(ref_r==act_r, "conversion to epoch failed (leap regular year before epoch)"); + assert(ref_dt==act_dt, "constructor test failed (leap regular year before epoch)"); + assert(ref_r==act_r, "conversion to epoch failed (leap regular year before epoch)"); + end if; // compare two DateTime records created from system time. dt2 should be a few seconds later than dt1 dt1 :=DateTime(); From f88e2952ad45bf4f9a6867d702742c64ed97fd4c Mon Sep 17 00:00:00 2001 From: mkr7 Date: Tue, 19 May 2020 15:50:07 +0200 Subject: [PATCH 52/66] Use class for operator record icon ... to fix inheritance, as its not allowed to extend from an operator record in Modelica 3.4 --- Modelica/Icons.mo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modelica/Icons.mo b/Modelica/Icons.mo index 6a7dbb7433..777e8a762b 100644 --- a/Modelica/Icons.mo +++ b/Modelica/Icons.mo @@ -550,7 +550,7 @@ This icon is indicates a record. ")); end Record; - partial operator record OperatorRecord "Icon for operator records" + partial class OperatorRecord "Icon for operator records" annotation (Icon(coordinateSystem(preserveAspectRatio=true, extent={{-100,-100},{100,100}}), graphics={ Text( From ef1c6e91b89d72678b1c8522daad7f609713b165 Mon Sep 17 00:00:00 2001 From: mkr7 Date: Wed, 20 May 2020 08:15:58 +0200 Subject: [PATCH 53/66] Use proper comments for operators --- Modelica/Utilities/Time.mo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modelica/Utilities/Time.mo b/Modelica/Utilities/Time.mo index a8abe93996..148d8d168c 100644 --- a/Modelica/Utilities/Time.mo +++ b/Modelica/Utilities/Time.mo @@ -1095,7 +1095,7 @@ String(d, format=\"%days'\") // = \"1\" (only full days are shown, rest result := not d1 == d2; end '<>'; - encapsulated operator function '>' "Check if Duration d1 is larger than d2" + encapsulated operator function '>' "Check if Duration d1 is greater than d2" import Modelica.Utilities.Time.Duration; import Modelica.Icons.Function; extends Function; @@ -1108,7 +1108,7 @@ String(d, format=\"%days'\") // = \"1\" (only full days are shown, rest result := Duration.inSeconds(d1) > Duration.inSeconds(d2); end '>'; - encapsulated operator function '>=' "Check if Duration d1 is equal to d2 or larger" + encapsulated operator function '>=' "Check if Duration d1 is greater than d2 or equal" import Modelica.Utilities.Time.Duration; import Modelica.Icons.Function; extends Function; @@ -1122,7 +1122,7 @@ String(d, format=\"%days'\") // = \"1\" (only full days are shown, rest end '>='; - encapsulated operator function '<' "Check if Duration d1 is smaller than d2" + encapsulated operator function '<' "Check if Duration d1 is less than d2" import Modelica.Utilities.Time.Duration; import Modelica.Icons.Function; extends Function; @@ -1136,7 +1136,7 @@ String(d, format=\"%days'\") // = \"1\" (only full days are shown, rest end '<'; - encapsulated operator function '<=' "Check if Duration d1 is equal to d2 or smaller" + encapsulated operator function '<=' "Check if Duration d1 is less than d2 or equal" import Modelica.Utilities.Time.Duration; import Modelica.Icons.Function; extends Function; From 5b05f8b8e2d00265bc917d34771cca8862f039fa Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Tue, 27 Oct 2020 20:46:09 +0100 Subject: [PATCH 54/66] Update description --- Modelica/Utilities/Internal.mo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modelica/Utilities/Internal.mo b/Modelica/Utilities/Internal.mo index 7d2428a3ed..d338bd3a36 100644 --- a/Modelica/Utilities/Internal.mo +++ b/Modelica/Utilities/Internal.mo @@ -254,7 +254,7 @@ end FileSystem; input Integer mon "Month"; input Integer year "Year"; input Integer epoch_year = 1970 "Reference year"; - output Real seconds "Elapsed seconds since epoch_year in the current time zone"; + output Real seconds "Elapsed seconds since start of epoch_year in the current time zone"; external "C" seconds = ModelicaTime_difftime(ms, sec, min, hour, day, mon, year, epoch_year) annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.c\""); annotation (Documentation(info=" From ee54ab32e7340d5a265805049d2b613250841c2b Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Tue, 27 Oct 2020 20:58:41 +0100 Subject: [PATCH 55/66] Remove C99 standard version specifier --- Modelica/Utilities/Internal.mo | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modelica/Utilities/Internal.mo b/Modelica/Utilities/Internal.mo index d338bd3a36..48a6bbced4 100644 --- a/Modelica/Utilities/Internal.mo +++ b/Modelica/Utilities/Internal.mo @@ -370,7 +370,7 @@ TODO output Integer day "Day"; output Integer mon "Month"; output Integer year "Year"; - external "C99" ModelicaTime_strptime(ms, sec, min, hour, day, mon, year, str, format) + external "C" ModelicaTime_strptime(ms, sec, min, hour, day, mon, year, str, format) annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.c\""); annotation (Documentation(info=" TODO @@ -389,7 +389,7 @@ TODO input String format = "%Y-%m-%d %H:%M:%S" "Format string passed to strftime"; input Integer maxSize = 128 "Maximal length of formatted string"; output String str "Formatted date and time string"; - external "C99" str = ModelicaTime_strftime(ms, sec, min, hour, day, mon, year, format, maxSize) + external "C" str = ModelicaTime_strftime(ms, sec, min, hour, day, mon, year, format, maxSize) annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.c\""); annotation (Documentation(info=" TODO From 57ec62fd456ea2bd7428bcfc43d38d93967f4448 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Thu, 29 Oct 2020 20:03:02 +0100 Subject: [PATCH 56/66] Remove documentation of border cases --- Modelica/Math/nearestInteger.mo | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Modelica/Math/nearestInteger.mo b/Modelica/Math/nearestInteger.mo index 8d9e9475b6..bda058567c 100644 --- a/Modelica/Math/nearestInteger.mo +++ b/Modelica/Math/nearestInteger.mo @@ -32,8 +32,6 @@ Math.nearestInteger(0.4); // = 0 Math.nearestInteger(0.5); // = 1 Math.nearestInteger(-0.4); // = 0 Math.nearestInteger(-0.5); // = -1 -Math.nearestInteger(0.3999999999999999+0.1); // = 0 -Math.nearestInteger(1.39999999999999999+0.1); // = 1 (erroneous border case, see note below)

    Note

    @@ -42,9 +40,5 @@ Math.nearestInteger(1.39999999999999999+0.1); // = 1 (erroneous border case, see This function does the same conversion as the block RealToInteger.

    -

    -The underlying equation is simple, but not always correct. Due to floating point arithmetic some border cases -are not converted correct, like shown in the example above. -

    ")); end nearestInteger; From 99a7ca3482ff92b90c9c20e70b675ad4d75d2503 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Thu, 29 Oct 2020 20:36:06 +0100 Subject: [PATCH 57/66] Improve return value handling of localtime --- Modelica/Resources/C-Sources/ModelicaTime.c | 78 ++++++++++----------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/Modelica/Resources/C-Sources/ModelicaTime.c b/Modelica/Resources/C-Sources/ModelicaTime.c index ea942ac5b5..e53822ace4 100644 --- a/Modelica/Resources/C-Sources/ModelicaTime.c +++ b/Modelica/Resources/C-Sources/ModelicaTime.c @@ -49,20 +49,20 @@ #if !defined(NO_TIME) time_t epoch(int sec, int min, int hour, int mday, int mon, int year) { - struct tm tlocal; + struct tm tres; time_t calendarTime; - tlocal.tm_sec = sec; - tlocal.tm_min = min; - tlocal.tm_hour = hour; - tlocal.tm_mday = mday; - tlocal.tm_mon = mon - 1; - tlocal.tm_year = year - 1900; - tlocal.tm_isdst = -1; - tlocal.tm_wday = 0; - tlocal.tm_yday = 0; + tres.tm_sec = sec; + tres.tm_min = min; + tres.tm_hour = hour; + tres.tm_mday = mday; + tres.tm_mon = mon - 1; + tres.tm_year = year - 1900; + tres.tm_isdst = -1; + tres.tm_wday = 0; + tres.tm_yday = 0; - calendarTime = mktime(&tlocal); + calendarTime = mktime(&tres); if (-1 == calendarTime) { ModelicaFormatError("Not possible to convert \"%4i-%02i-%02i %02i:%02i:%02i\" " "to time_t type in local time zone.\n", year, mon, mday, hour, min, sec); @@ -116,12 +116,12 @@ void ModelicaTime_localtime(_Out_ int* ms, _Out_ int* sec, _Out_ int* min, *mon = 0; *year = 0; #else - struct tm* tlocal; + struct tm tres; time_t calendarTime; double intPartOfSeconds; double fracPartOfSeconds; -#if defined(_POSIX_) || (defined(_MSC_VER) && _MSC_VER >= 1400) - struct tm tres; +#if !defined(_POSIX_) && !(defined(_MSC_VER) && _MSC_VER >= 1400) + struct tm* tlocal; #endif fracPartOfSeconds = modf(seconds, &intPartOfSeconds); @@ -136,22 +136,22 @@ void ModelicaTime_localtime(_Out_ int* ms, _Out_ int* sec, _Out_ int* min, } #if defined(_POSIX_) - tlocal = localtime_r(&calendarTime, &tres); /* Time fields in local time zone */ + localtime_r(&calendarTime, &tres); /* Time fields in local time zone */ #elif defined(_MSC_VER) && _MSC_VER >= 1400 localtime_s(&tres, &calendarTime); /* Time fields in local time zone */ - tlocal = &tres; #else tlocal = localtime(&calendarTime); /* Time fields in local time zone */ + tres = *tlocal; #endif /* Do not memcpy as you do not know which sizes are in the struct */ *ms = (int)(fracPartOfSeconds*1000.0 + 0.5); - *sec = tlocal->tm_sec; - *min = tlocal->tm_min; - *hour = tlocal->tm_hour; - *mday = tlocal->tm_mday; - *mon = 1 + tlocal->tm_mon; /* Correct for month starting at 1 */ - *year = 1900 + tlocal->tm_year; /* Correct for 4-digit year */ + *sec = tres.tm_sec; + *min = tres.tm_min; + *hour = tres.tm_hour; + *mday = tres.tm_mday; + *mon = 1 + tres.tm_mon; /* Correct for month starting at 1 */ + *year = 1900 + tres.tm_year; /* Correct for 4-digit year */ #endif } @@ -162,21 +162,21 @@ _Ret_z_ const char* ModelicaTime_strftime(int ms, int sec, int min, int hour, #if defined(NO_TIME) return ""; #else - struct tm* tlocal; + struct tm tres; const size_t maxSize = (size_t)_maxSize; char* timePtr = ModelicaAllocateString(maxSize); time_t calendarTime = epoch(sec, min, hour, mday, mon, year); -#if defined(_POSIX_) || (defined(_MSC_VER) && _MSC_VER >= 1400) - struct tm tres; +#if !defined(_POSIX_) && !(defined(_MSC_VER) && _MSC_VER >= 1400) + struct tm* tlocal; #endif #if defined(_POSIX_) - tlocal = localtime_r(&calendarTime, &tres); /* Time fields in local time zone */ + localtime_r(&calendarTime, &tres); /* Time fields in local time zone */ #elif defined(_MSC_VER) && _MSC_VER >= 1400 localtime_s(&tres, &calendarTime); /* Time fields in local time zone */ - tlocal = &tres; #else tlocal = localtime(&calendarTime); /* Time fields in local time zone */ + tres = *tlocal; #endif { @@ -196,17 +196,17 @@ _Ret_z_ const char* ModelicaTime_strftime(int ms, int sec, int min, int hour, #if !defined(NO_LOCALE) && (defined(_MSC_VER) && _MSC_VER >= 1400) { _locale_t loc = _create_locale(LC_TIME, "C"); - retLen = _strftime_l(timePtr, maxSize, format2, tlocal, loc); + retLen = _strftime_l(timePtr, maxSize, format2, &tres, loc); _free_locale(loc); } #elif !defined(NO_LOCALE) && (defined(__GLIBC__) && defined(__GLIBC_MINOR__) && ((__GLIBC__ << 16) + __GLIBC_MINOR__ >= (2 << 16) + 3)) { locale_t loc = newlocale(LC_TIME, "C", NULL); - retLen = strftime_l(timePtr, maxSize, format2, tlocal, loc); + retLen = strftime_l(timePtr, maxSize, format2, &tres, loc); freelocale(loc); } #else - retLen = strftime(timePtr, maxSize, format2, tlocal); + retLen = strftime(timePtr, maxSize, format2, &tres); #endif free(format2); if (retLen > 0 && retLen <= maxSize) { @@ -233,19 +233,19 @@ void ModelicaTime_strptime(_Out_ int* ms, _Out_ int* sec, _Out_ int* min, *mon = 0; *year = 0; #else - struct tm tlocal; + struct tm tres; int tmp = 0; - memset(&tlocal, 0, sizeof(struct tm)); - if (NULL != strptime_ms(buf, format, &tlocal, &tmp)) { + memset(&tres, 0, sizeof(struct tm)); + if (NULL != strptime_ms(buf, format, &tres, &tmp)) { /* Do not memcpy as you do not know which sizes are in the struct */ *ms = tmp; - *sec = tlocal.tm_sec; - *min = tlocal.tm_min; - *hour = tlocal.tm_hour; - *mday = tlocal.tm_mday; - *mon = 1 + tlocal.tm_mon; /* Correct for month starting at 1 */ - *year = 1900 + tlocal.tm_year; /* Correct for 4-digit year */ + *sec = tres.tm_sec; + *min = tres.tm_min; + *hour = tres.tm_hour; + *mday = tres.tm_mday; + *mon = 1 + tres.tm_mon; /* Correct for month starting at 1 */ + *year = 1900 + tres.tm_year; /* Correct for 4-digit year */ } else { *ms = 0; From 55cf27b1de243db92cde41c984ca1812d9a112ad Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Thu, 29 Oct 2020 20:36:24 +0100 Subject: [PATCH 58/66] Exclude past tests --- ModelicaTest/Utilities.mo | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index b0d827718e..6d6216e05b 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -445,7 +445,7 @@ extends Modelica.Icons.ExamplesPackage; extends Modelica.Icons.Function; input String logFile="ModelicaTestLog.txt" "Filename where the log is stored"; - input Boolean test_past = true "Optionally run / skip tests for DateTimes before epoch year 1970"; + input Boolean test_past = false "Optionally run / skip tests for DateTimes before epoch year 1970"; output Boolean ok; protected @@ -454,7 +454,7 @@ extends Modelica.Icons.ExamplesPackage; DateTime act_dt, ref_dt; DateTime dt0, dt1, dt2, dt3; Duration d; - Integer _ "Dummy to swallow return value"; + Integer rc "Dummy return value"; algorithm @@ -636,8 +636,8 @@ extends Modelica.Icons.ExamplesPackage; // compare two DateTime records created from system time. dt2 should be a few seconds later than dt1 dt1 :=DateTime(); - _ :=Modelica.Utilities.System.command("sleep 1") "Sleep 1s on linux"; - _ :=Modelica.Utilities.System.command("ping -n 2 127.0.0.1 > NUL") "Sleep 1s on windows"; + rc :=Modelica.Utilities.System.command("sleep 1") "Sleep 1s on linux"; + rc :=Modelica.Utilities.System.command("ping -n 2 127.0.0.1 > NUL") "Sleep 1s on windows"; dt2 :=DateTime(); assert( (dt2 > dt1) and (DateTime.epoch(dt2)-DateTime.epoch(dt1) < 5), @@ -693,8 +693,8 @@ extends Modelica.Icons.ExamplesPackage; dt0 := DateTime("2020-04-20 04:30:00"); d := Duration(days=11, hours=2, minutes=3, seconds=100, milliseconds=0); ref_dt := DateTime("2020-05-01 06:34:40"); - assert(dt0+d==ref_dt, "DateTime+Duration failed"); - assert(d+dt0==ref_dt, "Duration+DateTime failed"); + assert(dt0+d==ref_dt, "DateTime+Duration failed"); + assert(d+dt0==ref_dt, "Duration+DateTime failed"); // - subtract DateTime assert(dt2-dt1==Duration(days=0, hours=0, minutes=0, seconds=0, milliseconds=1), "dt2-dt1 failed"); @@ -703,7 +703,7 @@ extends Modelica.Icons.ExamplesPackage; // - subtract Duration act_dt := DateTime("2020-04-20 04:30:00") - Duration(days=10, hours=2, minutes=3, seconds=4, milliseconds=0); ref_dt := DateTime("2020-04-10 02:26:56"); - assert(act_dt==ref_dt, "DateTime-Duration failed"); + assert(act_dt==ref_dt, "DateTime-Duration failed"); // return result ok := true; From ec34d6ebae28b8617e93e7fd5c2ab389d44cf919 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Thu, 29 Oct 2020 20:53:52 +0100 Subject: [PATCH 59/66] Add some tests for CI --- .CI/Test/ModelicaTime.c | 29 +++++++++++++++++++++++++++++ .CI/Test/test.sh | 4 ++++ 2 files changed, 33 insertions(+) create mode 100644 .CI/Test/ModelicaTime.c diff --git a/.CI/Test/ModelicaTime.c b/.CI/Test/ModelicaTime.c new file mode 100644 index 0000000000..d5dd66ddf4 --- /dev/null +++ b/.CI/Test/ModelicaTime.c @@ -0,0 +1,29 @@ +#include "../../Modelica/Resources/C-Sources/ModelicaTime.h" +#include "Common.c" + +#include +#include + +int main(int argc, char **argv) { + int ms = 501; + int sec = 30; + int min = 44; + int hour = 20; + int mday = 29; + int mon = 10; + int year = 2020; + const char *format = "%Y-%m-%d %H:%M:%S.%L"; + int maxSize = 128; + const char *dateStr = ModelicaTime_strftime(ms, sec, min, hour, mday, mon, year, format, maxSize); + ModelicaFormatMessage("Date: %s\n", dateStr); + assert(0 == strcmp("2020-10-29 20:44:30.501", dateStr)); + ModelicaTime_strptime(&ms, &sec, &min, &hour, &mday, &mon, &year, "2000-09-30 06:45:01.123", format); + assert(ms == 123); + assert(sec == 1); + assert(min == 45); + assert(hour == 6); + assert(mday == 30); + assert(mon == 9); + assert(year == 2000); + return 0; +} diff --git a/.CI/Test/test.sh b/.CI/Test/test.sh index 66dce3ab6e..aee876e360 100755 --- a/.CI/Test/test.sh +++ b/.CI/Test/test.sh @@ -10,6 +10,8 @@ if test ! "$1" = "nostatic"; then ./a.out || exit 1 "$CC" -L $LIBRARIES ModelicaStrings.c -Wl,-Bstatic -lModelicaExternalC -Wl,-Bdynamic || exit 1 ./a.out || exit 1 +"$CC" -L $LIBRARIES ModelicaTime.c -Wl,-Bstatic -lModelicaExternalC -Wl,-Bdynamic || exit 1 +./a.out || exit 1 "$CC" -L $LIBRARIES Streams.c -Wl,-Bstatic -lModelicaExternalC -Wl,-Bdynamic || exit 1 ./a.out || exit 1 "$CC" -L $LIBRARIES Tables.c -Wl,-Bstatic -lModelicaStandardTables -lModelicaIO -lModelicaMatIO -lzlib -Wl,-Bdynamic -lm || exit 1 @@ -31,6 +33,8 @@ if test ! "$1" = "onlystatic"; then ./a.out || exit 1 "$CC" -L $LIBRARIES -Wl,-rpath $LIBRARIES ModelicaStrings.c -lModelicaExternalC || exit 1 ./a.out || exit 1 +"$CC" -L $LIBRARIES -Wl,-rpath $LIBRARIES ModelicaTime.c -lModelicaExternalC || exit 1 +./a.out || exit 1 "$CC" -L $LIBRARIES -Wl,-rpath $LIBRARIES Streams.c -lModelicaExternalC || exit 1 ./a.out || exit 1 "$CC" -L $LIBRARIES -Wl,-rpath $LIBRARIES Tables.c -lModelicaStandardTables -lModelicaIO -lModelicaMatIO || exit 1 From e928d7f6458b0567239eb5763b94c9ca34015489 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Mon, 18 Jan 2021 20:52:50 +0100 Subject: [PATCH 60/66] Migrate conversion to next major version Conversion for MSL v4.0.0 no longer is suitable, hence migrate to next major version. --- .../ConvertModelica_from_3.2.3_to_4.0.0.mos | 2 - .../ConvertModelica_from_4.0.0_to_5.0.0.mos | 5 +++ ModelicaTestConversion4.mo | 21 ----------- ModelicaTestConversion5.mo | 37 +++++++++++++++++++ 4 files changed, 42 insertions(+), 23 deletions(-) create mode 100644 Modelica/Resources/Scripts/Conversion/ConvertModelica_from_4.0.0_to_5.0.0.mos create mode 100644 ModelicaTestConversion5.mo diff --git a/Modelica/Resources/Scripts/Conversion/ConvertModelica_from_3.2.3_to_4.0.0.mos b/Modelica/Resources/Scripts/Conversion/ConvertModelica_from_3.2.3_to_4.0.0.mos index e8dc46aed3..02bde6c6bd 100644 --- a/Modelica/Resources/Scripts/Conversion/ConvertModelica_from_3.2.3_to_4.0.0.mos +++ b/Modelica/Resources/Scripts/Conversion/ConvertModelica_from_3.2.3_to_4.0.0.mos @@ -13,8 +13,6 @@ convertClass("Modelica.Fluid.Icons.BaseClassLibrary", "Modelica.Icons.BasesPackage"); convertClass("Modelica.Icons.TypeComplex", "Complex"); -convertClass("Modelica.Utilities.System.getTime", - "Modelica.Utilities.Internal.Time.getTime"); convertClass("Modelica.Mechanics.MultiBody.Sensors.Internal.ZeroForceAndTorque", "Modelica.Mechanics.MultiBody.Forces.Internal.ZeroForceAndTorque"); convertClass("Modelica.Mechanics.Rotational.Interfaces.PartialElementaryOneFlangeAndSupport", diff --git a/Modelica/Resources/Scripts/Conversion/ConvertModelica_from_4.0.0_to_5.0.0.mos b/Modelica/Resources/Scripts/Conversion/ConvertModelica_from_4.0.0_to_5.0.0.mos new file mode 100644 index 0000000000..7819f592fd --- /dev/null +++ b/Modelica/Resources/Scripts/Conversion/ConvertModelica_from_4.0.0_to_5.0.0.mos @@ -0,0 +1,5 @@ +// Convert from Modelica 4.0.0 to Modelica 5.0.0 + +// Change references to classes that are removed from Modelica Standard Library +convertClass("Modelica.Utilities.System.getTime", + "Modelica.Utilities.Internal.Time.getTime"); diff --git a/ModelicaTestConversion4.mo b/ModelicaTestConversion4.mo index 03441928fe..b9da936dc1 100644 --- a/ModelicaTestConversion4.mo +++ b/ModelicaTestConversion4.mo @@ -3069,27 +3069,6 @@ Conversion test for #3247. -

    -")); - end Issue3247; - end Utilities; - package Icons extends Modelica.Icons.ExamplesPackage; model Issue340 "Conversion test for #340" diff --git a/ModelicaTestConversion5.mo b/ModelicaTestConversion5.mo new file mode 100644 index 0000000000..ffd53ac9bf --- /dev/null +++ b/ModelicaTestConversion5.mo @@ -0,0 +1,37 @@ +package ModelicaTestConversion5 + extends Modelica.Icons.ExamplesPackage; + package Utilities + extends Modelica.Icons.ExamplesPackage; + model Issue3247 "Conversion test for #3247" + extends Modelica.Icons.Example; + Integer ms; + Integer sec; + Integer min; + Integer hour; + Integer day; + Integer mon; + Integer year; + equation + (ms, sec, min, hour, day, mon, year) = Modelica.Utilities.System.getTime(); + annotation(experiment(StopTime=1), Documentation(info=" +

    +Conversion test for #3247. +

    +")); + end Issue3247; + end Utilities; + annotation(uses(Modelica(version="4.0.0"), Complex(version="4.0.0")), Documentation(info=" +

    +This library provides models and functions to test the MSL v5.0.0 conversion script \"ConvertModelica_from_4.0.0_to_5.0.0.mos\" +for conversion of Modelica libraries using MSL v4.0.0 to MSL v5.0.0. These models are not meant to be meaningful otherwise. +

    + +

    +Copyright © 2021, Modelica Association and contributors +

    + +

    +This Modelica package is free software and the use is completely at your own risk; it can be redistributed and/or modified under the terms of the 3-Clause BSD license. For license conditions (including the disclaimer of warranty) visit https://modelica.org/licenses/modelica-3-clause-bsd. +

    +")); +end ModelicaTestConversion5; From 8edddc26a929cae7fc56a60d4167ddbb0fcd48ed Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Sun, 27 Oct 2024 11:31:57 +0100 Subject: [PATCH 61/66] Move third-party license files --- .../Resources/Licenses/{ => Third-party}/LICENSE_repl_str.txt | 0 .../Resources/Licenses/{ => Third-party}/LICENSE_strptime.txt | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename Modelica/Resources/Licenses/{ => Third-party}/LICENSE_repl_str.txt (100%) rename Modelica/Resources/Licenses/{ => Third-party}/LICENSE_strptime.txt (100%) diff --git a/Modelica/Resources/Licenses/LICENSE_repl_str.txt b/Modelica/Resources/Licenses/Third-party/LICENSE_repl_str.txt similarity index 100% rename from Modelica/Resources/Licenses/LICENSE_repl_str.txt rename to Modelica/Resources/Licenses/Third-party/LICENSE_repl_str.txt diff --git a/Modelica/Resources/Licenses/LICENSE_strptime.txt b/Modelica/Resources/Licenses/Third-party/LICENSE_strptime.txt similarity index 100% rename from Modelica/Resources/Licenses/LICENSE_strptime.txt rename to Modelica/Resources/Licenses/Third-party/LICENSE_strptime.txt From 5a961eff2d2ad6c18568ce1dc409a48800af01ab Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Sun, 27 Oct 2024 12:02:33 +0100 Subject: [PATCH 62/66] Update CMake configuration --- Modelica/Resources/BuildProjects/CMake/src.cmake | 4 ++++ Modelica/Resources/BuildProjects/CMake/test.cmake | 1 + 2 files changed, 5 insertions(+) diff --git a/Modelica/Resources/BuildProjects/CMake/src.cmake b/Modelica/Resources/BuildProjects/CMake/src.cmake index 2a2a7e9002..1a3d3e37ae 100644 --- a/Modelica/Resources/BuildProjects/CMake/src.cmake +++ b/Modelica/Resources/BuildProjects/CMake/src.cmake @@ -70,9 +70,13 @@ set(EXTC_SOURCES "${MODELICA_SOURCE_DIR}/ModelicaRandom.h" "${MODELICA_SOURCE_DIR}/ModelicaStrings.c" "${MODELICA_SOURCE_DIR}/ModelicaStrings.h" + "${MODELICA_SOURCE_DIR}/ModelicaTime.c" + "${MODELICA_SOURCE_DIR}/ModelicaTime.h" "${MODELICA_SOURCE_DIR}/gconstructor.h" + "${MODELICA_SOURCE_DIR}/repl_str.h" "${MODELICA_SOURCE_DIR}/stdint_msvc.h" "${MODELICA_SOURCE_DIR}/stdint_wrap.h" + "${MODELICA_SOURCE_DIR}/strptime.h" "${MODELICA_SOURCE_DIR}/uthash.h" "${MODELICA_SOURCE_DIR}/win32_dirent.c" "${MODELICA_SOURCE_DIR}/win32_dirent.h" diff --git a/Modelica/Resources/BuildProjects/CMake/test.cmake b/Modelica/Resources/BuildProjects/CMake/test.cmake index b6c8929724..d8c3369b11 100644 --- a/Modelica/Resources/BuildProjects/CMake/test.cmake +++ b/Modelica/Resources/BuildProjects/CMake/test.cmake @@ -6,6 +6,7 @@ if(MODELICA_BUILD_TESTING) set(MODELICA_TESTS FileSystem ModelicaStrings + ModelicaTime Streams Tables TablesFromCsvFile From 040080fa03dd683bcdbb2386228999ca185051c2 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Sun, 27 Oct 2024 12:15:45 +0100 Subject: [PATCH 63/66] Fix build errors --- Modelica/Resources/C-Sources/ModelicaTime.c | 5 ++--- Modelica/Resources/C-Sources/ModelicaTime.h | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Modelica/Resources/C-Sources/ModelicaTime.c b/Modelica/Resources/C-Sources/ModelicaTime.c index e53822ace4..61f74309fb 100644 --- a/Modelica/Resources/C-Sources/ModelicaTime.c +++ b/Modelica/Resources/C-Sources/ModelicaTime.c @@ -29,9 +29,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// TODO: Remove when ModelicaTime.c will become part of ModelicaExternalC -#if defined(DYMOSIM) -#pragma once +#if defined(__gnu_linux__) +#define _GNU_SOURCE 1 #endif #include "ModelicaTime.h" diff --git a/Modelica/Resources/C-Sources/ModelicaTime.h b/Modelica/Resources/C-Sources/ModelicaTime.h index 912f4bb0f2..18a1d04810 100644 --- a/Modelica/Resources/C-Sources/ModelicaTime.h +++ b/Modelica/Resources/C-Sources/ModelicaTime.h @@ -42,6 +42,8 @@ #ifndef MODELICA_TIME_H_ #define MODELICA_TIME_H_ +#include + #if !defined(MODELICA_EXPORT) #if defined(__cplusplus) #define MODELICA_EXPORT extern "C" From 462fcf7ebb9cb2b425c69d3eda93da0983172ae4 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Sun, 27 Oct 2024 12:28:59 +0100 Subject: [PATCH 64/66] Bump copyright year --- Modelica/Resources/C-Sources/ModelicaTime.c | 2 +- Modelica/Resources/C-Sources/ModelicaTime.h | 2 +- Modelica/Resources/Licenses/LICENSE_ModelicaTime.txt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Modelica/Resources/C-Sources/ModelicaTime.c b/Modelica/Resources/C-Sources/ModelicaTime.c index 61f74309fb..b924bd88f2 100644 --- a/Modelica/Resources/C-Sources/ModelicaTime.c +++ b/Modelica/Resources/C-Sources/ModelicaTime.c @@ -1,6 +1,6 @@ /* ModelicaTime.c - External functions for Modelica.Utilities.Time - Copyright (C) 2020, Modelica Association and contributors + Copyright (C) 2020-2024, Modelica Association and contributors All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/Modelica/Resources/C-Sources/ModelicaTime.h b/Modelica/Resources/C-Sources/ModelicaTime.h index 18a1d04810..59cbbeb3d5 100644 --- a/Modelica/Resources/C-Sources/ModelicaTime.h +++ b/Modelica/Resources/C-Sources/ModelicaTime.h @@ -1,6 +1,6 @@ /* ModelicaTime.h - External functions header for Modelica.Utilities.Time - Copyright (C) 2020, Modelica Association and contributors + Copyright (C) 2020-2024, Modelica Association and contributors All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/Modelica/Resources/Licenses/LICENSE_ModelicaTime.txt b/Modelica/Resources/Licenses/LICENSE_ModelicaTime.txt index 4141670436..151a5ff2cd 100644 --- a/Modelica/Resources/Licenses/LICENSE_ModelicaTime.txt +++ b/Modelica/Resources/Licenses/LICENSE_ModelicaTime.txt @@ -1,4 +1,4 @@ -Copyright (C) 2020, Modelica Association and contributors +Copyright (C) 2020-2024, Modelica Association and contributors All rights reserved. Redistribution and use in source and binary forms, with or without From 479e4a99367315ef5d4c3fc728ca0cd49309cc6c Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Sun, 27 Oct 2024 12:37:40 +0100 Subject: [PATCH 65/66] Add include guards --- Modelica/Resources/C-Sources/repl_str.h | 5 +++++ Modelica/Resources/C-Sources/strptime.h | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/Modelica/Resources/C-Sources/repl_str.h b/Modelica/Resources/C-Sources/repl_str.h index 1fa189b871..c451971511 100644 --- a/Modelica/Resources/C-Sources/repl_str.h +++ b/Modelica/Resources/C-Sources/repl_str.h @@ -4,6 +4,9 @@ * Released to public domain */ +#ifndef MODELICA_REPL_STR_H_ +#define MODELICA_REPL_STR_H_ + #include #include #include @@ -104,3 +107,5 @@ static char *repl_str(const char *str, const char *from, const char *to) { free(pos_cache); return ret; } + +#endif diff --git a/Modelica/Resources/C-Sources/strptime.h b/Modelica/Resources/C-Sources/strptime.h index e89ce8ef70..e57033be28 100644 --- a/Modelica/Resources/C-Sources/strptime.h +++ b/Modelica/Resources/C-Sources/strptime.h @@ -35,6 +35,9 @@ the %L format specifier */ +#ifndef MODELICA_STRPTIME_H_ +#define MODELICA_STRPTIME_H_ + #include #include #include @@ -566,3 +569,5 @@ find_string (const unsigned char *bp, int *tgt, const char * const *n1, /* Nothing matched */ return NULL; } + +#endif From f7be3cc6e7ad8d7af88f7604930e6fd26aa0f421 Mon Sep 17 00:00:00 2001 From: Thomas Beutlich Date: Sun, 27 Oct 2024 12:44:07 +0100 Subject: [PATCH 66/66] Update include annotations --- Modelica/Utilities/Internal.mo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modelica/Utilities/Internal.mo b/Modelica/Utilities/Internal.mo index 48a6bbced4..9e36bd1f99 100644 --- a/Modelica/Utilities/Internal.mo +++ b/Modelica/Utilities/Internal.mo @@ -256,7 +256,7 @@ end FileSystem; input Integer epoch_year = 1970 "Reference year"; output Real seconds "Elapsed seconds since start of epoch_year in the current time zone"; external "C" seconds = ModelicaTime_difftime(ms, sec, min, hour, day, mon, year, epoch_year) - annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.c\""); + annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.h\"", Library="ModelicaExternalC"); annotation (Documentation(info=" TODO ")); @@ -353,7 +353,7 @@ All returned values are of type Integer and have the following meaning: output Integer mon "Month"; output Integer year "Year"; external "C90" ModelicaTime_localtime(ms, sec, min, hour, day, mon, year, seconds, epoch_year) - annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.c\""); + annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.h\"", Library="ModelicaExternalC"); annotation (Documentation(info=" TODO ")); @@ -371,7 +371,7 @@ TODO output Integer mon "Month"; output Integer year "Year"; external "C" ModelicaTime_strptime(ms, sec, min, hour, day, mon, year, str, format) - annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.c\""); + annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.h\"", Library="ModelicaExternalC"); annotation (Documentation(info=" TODO ")); @@ -390,7 +390,7 @@ TODO input Integer maxSize = 128 "Maximal length of formatted string"; output String str "Formatted date and time string"; external "C" str = ModelicaTime_strftime(ms, sec, min, hour, day, mon, year, format, maxSize) - annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.c\""); + annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.h\"", Library="ModelicaExternalC"); annotation (Documentation(info=" TODO "));