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 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/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/Icons.mo b/Modelica/Icons.mo index d63282f47a..777e8a762b 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 class 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/Math/nearestInteger.mo b/Modelica/Math/nearestInteger.mo new file mode 100644 index 0000000000..bda058567c --- /dev/null +++ b/Modelica/Math/nearestInteger.mo @@ -0,0 +1,44 @@ +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
+
+ +

Note

+ +

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

+")); +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 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 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..b924bd88f2 --- /dev/null +++ b/Modelica/Resources/C-Sources/ModelicaTime.c @@ -0,0 +1,259 @@ +/* ModelicaTime.c - External functions for Modelica.Utilities.Time + + Copyright (C) 2020-2024, 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. +*/ + +#if defined(__gnu_linux__) +#define _GNU_SOURCE 1 +#endif + +#include "ModelicaTime.h" +#include "ModelicaUtilities.h" +#include "strptime.h" +#include "repl_str.h" + +#include +#include +#include +#include +#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) { + struct tm tres; + time_t calendarTime; + + 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(&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); + } + return calendarTime; +} +#endif + +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; +#else + if (1 == mday && 1 == mon) { + if (year == refYear) { + return 60 * (60 * hour + min) + sec + 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; + } + } + { + 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 +} + +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 tres; + time_t calendarTime; + double intPartOfSeconds; + double fracPartOfSeconds; +#if !defined(_POSIX_) && !(defined(_MSC_VER) && _MSC_VER >= 1400) + struct tm* tlocal; +#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_) + 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 */ +#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 = 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 +} + +_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 + 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* tlocal; +#endif + +#if defined(_POSIX_) + 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 */ +#else + tlocal = localtime(&calendarTime); /* Time fields in local time zone */ + tres = *tlocal; +#endif + + { + char* format2; + char* format3; + char ms_str[32]; + size_t retLen; + + /* 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); + free(format2); + 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, &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, &tres, loc); + freelocale(loc); + } +#else + retLen = strftime(timePtr, maxSize, format2, &tres); +#endif + free(format2); + 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 tres; + int tmp = 0; + + 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 = 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; + *sec = 0; + *min = 0; + *hour = 0; + *mday = 0; + *mon = 0; + *year = 0; + } +#endif +} diff --git a/Modelica/Resources/C-Sources/ModelicaTime.h b/Modelica/Resources/C-Sources/ModelicaTime.h new file mode 100644 index 0000000000..59cbbeb3d5 --- /dev/null +++ b/Modelica/Resources/C-Sources/ModelicaTime.h @@ -0,0 +1,99 @@ +/* ModelicaTime.h - External functions header for Modelica.Utilities.Time + + Copyright (C) 2020-2024, 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_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: + - "__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_ + +#include + +#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 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 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, + _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/repl_str.h b/Modelica/Resources/C-Sources/repl_str.h new file mode 100644 index 0000000000..c451971511 --- /dev/null +++ b/Modelica/Resources/C-Sources/repl_str.h @@ -0,0 +1,111 @@ +/* Portable C string replacement function: repl_str + * https://creativeandcritical.net/str-replace-c + * Created by Laird Shaw + * Released to public domain + */ + +#ifndef MODELICA_REPL_STR_H_ +#define MODELICA_REPL_STR_H_ + +#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; +#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; + 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; +#if defined(__cplusplus) + ret = (char*)malloc(retlen + 1); +#else + ret = malloc(retlen + 1); +#endif + 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; +} + +#endif diff --git a/Modelica/Resources/C-Sources/strptime.h b/Modelica/Resources/C-Sources/strptime.h new file mode 100644 index 0000000000..e57033be28 --- /dev/null +++ b/Modelica/Resources/C-Sources/strptime.h @@ -0,0 +1,573 @@ +/*- + * 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. + */ + +/* 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 +*/ + +#ifndef MODELICA_STRPTIME_H_ +#define MODELICA_STRPTIME_H_ + +#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_ms (const char *buf, const char *fmt, struct tm *tm, int* ms) +{ + 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_ms((const char *)bp, new_fmt, tm, ms); + 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; + + case 'L': /* The milliseconds. */ + bp = conv_num(bp, ms, 0, 999); + 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; +} + +#endif diff --git a/Modelica/Resources/Licenses/LICENSE_ModelicaTime.txt b/Modelica/Resources/Licenses/LICENSE_ModelicaTime.txt new file mode 100644 index 0000000000..151a5ff2cd --- /dev/null +++ b/Modelica/Resources/Licenses/LICENSE_ModelicaTime.txt @@ -0,0 +1,27 @@ +Copyright (C) 2020-2024, 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/Third-party/LICENSE_repl_str.txt b/Modelica/Resources/Licenses/Third-party/LICENSE_repl_str.txt new file mode 100644 index 0000000000..c2ad6e852f --- /dev/null +++ b/Modelica/Resources/Licenses/Third-party/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/Resources/Licenses/Third-party/LICENSE_strptime.txt b/Modelica/Resources/Licenses/Third-party/LICENSE_strptime.txt new file mode 100644 index 0000000000..0fdae1e420 --- /dev/null +++ b/Modelica/Resources/Licenses/Third-party/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/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/Modelica/Utilities/Internal.mo b/Modelica/Utilities/Internal.mo index d3ad75276a..9e36bd1f99 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=" @@ -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,233 @@ 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; + + 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 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.h\"", Library="ModelicaExternalC"); + 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"; + 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 ≥ 1970Current 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; + + 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.h\"", Library="ModelicaExternalC"); + 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 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"; + output Integer min "Minute"; + output Integer hour "Hour"; + output Integer day "Day"; + 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.h\"", Library="ModelicaExternalC"); + annotation (Documentation(info=" +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 "C" str = ModelicaTime_strftime(ms, sec, min, hour, day, mon, year, format, maxSize) + annotation (IncludeDirectory="modelica://Modelica/Resources/C-Sources", Include="#include \"ModelicaTime.h\"", Library="ModelicaExternalC"); + 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"; + 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

+
+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
Monday 1
Tuesday 2
Wednesday 3
Thursday 4
Friday 5
Saturday 6
Sunday 7
+
+ +

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=" +

+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..148d8d168c --- /dev/null +++ b/Modelica/Utilities/Time.mo @@ -0,0 +1,1472 @@ +within Modelica.Utilities; +package Time "Functions to work with date and time" + 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 DateTime now "Current date and time"; + algorithm + now := DateTime.now(); + 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. +The returned value is of type DateTime. +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
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

+
+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!

+")); + end getTime; + + function dayOfWeek "Return day of week for given date" + extends Modelica.Icons.Function; + 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(dt.year, dt.month, dt.day); + annotation (Documentation(info=" +

Syntax

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

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
Monday 1
Tuesday 2
Wednesday 3
Thursday 4
Friday 5
Saturday 6
Sunday 7
+
+ +

Example

+
+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 "Return the number of days in year" + extends Modelica.Icons.Function; + input Integer year "Year"; + output Integer days "Number of days in year"; + algorithm + 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" + 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
+
+")); + end leapDays; + + operator record DateTime "DateTime record with several constructors and overloaded operators" + extends Modelica.Icons.OperatorRecord; + + 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' "Available constructors" + import Modelica.Utilities.Time.DateTime; + import Modelica.Icons.Function; + + function fromReadable "Create DateTime from human readable format" + extends Function; + + input Integer year "Year"; + 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) "Date and time"; + + algorithm + annotation(Inline = true); + end fromReadable; + + function fromSystemTime "Create DateTime from current system time" + import Modelica.Utilities.Internal.Time.getTime; + extends Function; + + output DateTime dt "Current date and time"; + + 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) := getTime(); + 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.Utilities.Internal.Time.localTime; + extends Function; + + input Real seconds "Elapsed seconds since epoch_year"; + input Integer epoch_year = 1970 "Reference year"; + output DateTime dt "Date and time"; + + 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) := localTime(seconds, epoch_year); + dt := DateTime(millisecond=millisecond, second=second, minute=minute, hour=hour, day=day, month=month, year=year); + + end fromEpoch; + + function fromString "Create DateTime from formatted string" + import Modelica.Utilities.Internal.Time.stringToTime; + extends Function; + + 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"; + + 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 fromString; + 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.Icons.Function; + + 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 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

+
+
+String(dt)
+String(dt, format)
+String(dt, format, maxSize)
+
+
+ +

Description

+

+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. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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%
+ +

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\"
+
+
+")); + 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.Function; + extends Function; + + input DateTime dt1; + input DateTime dt2; + output Boolean result "= dt1 == dt2"; + + algorithm + 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 '=='; + + encapsulated operator function '<>' "Check inequality of two DateTime objects" + import Modelica.Utilities.Time.DateTime; + import Modelica.Icons.Function; + extends Function; + + input DateTime dt1; + input DateTime dt2; + output Boolean result "= dt1 <> dt2"; + + algorithm + result := not dt1 == dt2; + end '<>'; + + encapsulated operator function '>' "Check if DateTime dt1 is later as dt2" + import Modelica.Utilities.Time.DateTime; + import Modelica.Icons.Function; + extends Function; + + input DateTime dt1; + input DateTime dt2; + output Boolean result "= dt1 > dt2"; + + algorithm + if dt1.year > dt2.year then + result := true; + else + 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; + else + 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; + else + 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; + else + result := false; + end if; + end if; + end if; + end if; + end if; + end if; + end if; + end '>'; + + encapsulated operator function '>=' "Check if DateTime dt1 is equal to dt2 or later" + import Modelica.Utilities.Time.DateTime; + import Modelica.Icons.Function; + extends Function; + + input DateTime dt1; + input DateTime dt2; + output Boolean result "= dt1 >= dt2"; + + algorithm + result := dt1 == dt2 or dt1 > dt2; + + end '>='; + + encapsulated operator function '<' "Check if DateTime dt1 is earlier as dt2" + import Modelica.Utilities.Time.DateTime; + import Modelica.Icons.Function; + extends Function; + + input DateTime dt1; + input DateTime dt2; + output Boolean result "= dt1 < dt2"; + + algorithm + result := not dt1 == dt2 and dt2 > dt1; + + end '<'; + + encapsulated operator function '<=' "Check if DateTime dt1 is equal to dt2 or earlier" + import Modelica.Utilities.Time.DateTime; + import Modelica.Icons.Function; + extends Function; + + input DateTime dt1; + input DateTime dt2; + output Boolean result "= dt1 <= dt2"; + + algorithm + result := dt1 == dt2 or dt1 < dt2; + + 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_DateTime "Return time delta between dt2 and dt1 as Duration" + extends Function; + + import Modelica.Utilities.Time.Duration; + + input DateTime dt1; + input DateTime dt2; + output Duration result "= dt1 - dt2"; + + algorithm + result := Duration.'constructor'.fromDateTimes(dt2, dt1); + + 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}, + {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.Internal.Time.diffTime; + import Modelica.Icons.Function; + extends Function; + + input DateTime dt "Date and time"; + input Integer epoch_year = 1970 "Reference year"; + output Real seconds "Elapsed seconds since epoch_year in the current time zone"; + + 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" + import Modelica.Utilities.Time.DateTime; + import Modelica.Icons.Function; + extends Function; + + output DateTime now "Current date and time"; + algorithm + now := DateTime.'constructor'.fromSystemTime(); + annotation (Inline=true); + 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" + extends Modelica.Icons.OperatorRecord; + + Integer days "Days"; + Integer hours "Hours"; + Integer minutes "Minutes"; + Integer seconds "Seconds"; + Integer milliseconds "Milliseconds"; + + encapsulated operator 'constructor' "Available constructors" + import Modelica.Utilities.Time.Duration; + import Modelica.Icons.Function; + + function fromInput "Create Duration field by field from user input" + extends Function; + + 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 + annotation (Inline = true); + end fromInput; + + function fromDateTimes "Create Duration from two DateTime records" + import Modelica.Utilities.Time.DateTime; + import Modelica.Math.nearestInteger; + extends Function; + + input DateTime dt1 "Start time"; + input DateTime dt2 "End time"; + output Duration d "= dt2 - dt1"; + + protected + Real diff; + Real e1, e2; + Integer days "Elapsed days"; + Integer hours "Elapsed hours"; + Integer minutes "Elapsed minutes"; + Integer seconds "Elapsed seconds"; + Integer milliseconds "Elapsed milliseconds"; + + algorithm + e1 := DateTime.epoch(dt1, epoch_year=dt1.year); + e2 := DateTime.epoch(dt2, epoch_year=dt1.year); + + diff := abs(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); + milliseconds := nearestInteger(rem(diff,1)*1000); + + d := Duration(days=days, hours=hours, minutes=minutes, seconds=seconds, milliseconds=milliseconds); + if e2 < e1 then + d := -d; + end if; + + end fromDateTimes; + + function fromSeconds "Create duration record from total amount of seconds, rounding to the third decimal" + extends Function; + import Modelica.Math.nearestInteger; + + 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 + 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 := rem(seconds, 60); + + minutes := carryover; + carryover := div(minutes, 60); + minutes := rem(minutes, 60); + + hours := carryover; + carryover := div(hours, 24); + hours := rem(hours, 24); + + days := carryover; + + 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.Function; + + function formatted "Convert duration to string, using C inspired conversion specifier characters" + import Modelica.Utilities.Strings.contains; + extends Function; + + input Duration d "Duration"; + input String format = "%daysd %hoursh %minutesmin %secondss %millisecondsms"; + output String str "Formatted duration string"; + + protected + Duration d2; + + 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 + + d2 := d; + + if not contains(format, "%days") and not contains(format, "%d") then + 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; + end if; + + if not contains(format, "%minutes") and not contains(format, "%M") then + 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; + 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(str, "%|", "%"); + + annotation (Documentation(info=" +

Syntax

+
+
+String(d)
+String(d, format)
+
+
+ +

Description

+

+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. +

+

+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. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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%
+ +

Example

+
+
+import Modelica.Utilities.Time.Duration;
+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)
+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)
+
+
+")); + 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.Function; + extends Function; + + input Duration d1; + input Duration d2; + output Boolean result "= d1 == d2"; + + protected + Duration d1_norm = Duration.normalize(d1); + Duration d2_norm = Duration.normalize(d2); + + algorithm + 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 '=='; + + encapsulated operator function '<>' "Check inequality of two Duration objects by normalizing them" + import Modelica.Utilities.Time.Duration; + import Modelica.Icons.Function; + extends Function; + + input Duration d1; + input Duration d2; + output Boolean result "= d1 <> d2"; + + algorithm + result := not d1 == d2; + end '<>'; + + encapsulated operator function '>' "Check if Duration d1 is greater than d2" + import Modelica.Utilities.Time.Duration; + import Modelica.Icons.Function; + extends Function; + + input Duration d1; + input Duration d2; + output Boolean result "= d1 > d2"; + + algorithm + result := Duration.inSeconds(d1) > Duration.inSeconds(d2); + end '>'; + + encapsulated operator function '>=' "Check if Duration d1 is greater than d2 or equal" + import Modelica.Utilities.Time.Duration; + import Modelica.Icons.Function; + extends Function; + + input Duration d1; + input Duration d2; + output Boolean result "= d1 >= d2"; + + algorithm + result := d1 == d2 or d1 > d2; + + end '>='; + + encapsulated operator function '<' "Check if Duration d1 is less than d2" + import Modelica.Utilities.Time.Duration; + import Modelica.Icons.Function; + extends Function; + + input Duration d1; + input Duration d2; + output Boolean result "= d1 < d2"; + + algorithm + result := not d1 == d2 and d2 > d1; + + end '<'; + + encapsulated operator function '<=' "Check if Duration d1 is less than d2 or equal" + import Modelica.Utilities.Time.Duration; + import Modelica.Icons.Function; + extends Function; + + input Duration d1; + input Duration d2; + output Boolean result "= d1 <= d2"; + + algorithm + result := d1 == d2 or d1 < d2; + + end '<='; + + encapsulated operator function '+' "Add Durations d1 and d2 and normalize the sum" + import Modelica.Utilities.Time.Duration; + import Modelica.Icons.Function; + extends Function; + + input Duration d1; + input Duration d2; + output Duration result "= d1 + d2"; + + algorithm + result := Duration.normalize(Duration( + 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 '+'; + + encapsulated operator '-' "Unary and binary minus" + import Modelica.Utilities.Time.Duration; + 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); + + end negate; + + function subtract "Subtract two durations element wise and normalize the difference" + extends Function; + + input Duration d1; + input Duration d2; + output Duration result "= d1 - d2"; + + algorithm + result := Duration.normalize(Duration( + 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; + 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.Function; + + function multiply1 "Multiply a duration with a real (by converting the duration to seconds)" + extends Function; + + input Duration d; + input Real r; + output Duration result "= inSeconds(d)*r"; + + algorithm + result := Duration(totalSeconds=Duration.inSeconds(d)*r); + + end multiply1; + + function multiply2 "Multiply a duration with a real (by converting the duration to seconds)" + extends Function; + + input Real r; + input Duration d; + output Duration result "= inSeconds(d)*r"; + + algorithm + 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" + import Modelica.Utilities.Time.Duration; + 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 Function; + + input Duration d; + input Real r; + output Duration result "= inSeconds(d)/r"; + + algorithm + 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.Function; + extends Function; + + output Duration result "= Duration()"; + + algorithm + result := Duration(days=0, hours=0, minutes=0, seconds=0, milliseconds=0); + + end '0'; + + encapsulated function asVector "Return duration as vector {days, hours, minutes, seconds, milliseconds}" + import Modelica.Utilities.Time.Duration; + 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}"; + + algorithm + + d_vec :={d.days, d.hours, d.minutes, d.seconds, d.milliseconds}; + + end asVector; + + encapsulated function avg "Return Duration with averaged values for a vector of durations" + import Modelica.Utilities.Time.Duration; + import Modelica.Icons.Function; + extends Function; + + input Duration d_vec[:] "Vector of duration"; + output Duration d_avg "Average duration"; + + protected + Integer n = size(d_vec, 1); + Real totalSeconds = 0; + + algorithm + for i in 1:n loop + totalSeconds := totalSeconds + Duration.inSeconds(d_vec[i]); + end for; + + d_avg := Duration(totalSeconds=totalSeconds/n); + + end avg; + + encapsulated function inSeconds "Convert Duration to total amount of seconds" + import Modelica.Utilities.Time.Duration; + import Modelica.Icons.Function; + extends Function; + + input Duration d; + output Real totalSeconds "Elapsed seconds"; + + algorithm + totalSeconds := d.milliseconds/1000 + d.seconds + 60*(d.minutes + 60*(d.hours + 24*d.days)); + + end inSeconds; + + encapsulated function normalize + "Recompute duration with usual maximum values for milliseconds, seconds, minutes and hours" + import Modelica.Utilities.Time.Duration; + import Modelica.Icons.Function; + extends Function; + + input Duration d "Duration"; + output Duration d_norm "Normalized duration"; + + algorithm + + d_norm := Duration(totalSeconds=Duration.inSeconds(d)); + + 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.'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;
+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();                                      // 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
+
+
+")); + end Duration; + annotation ( +Documentation(info=" +

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

+")); +end Time; 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.
  • 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/Math.mo b/ModelicaTest/Math.mo index 26d07b7cc9..df9d43d692 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, "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"); + ok:=true; end ScalarFunctions; diff --git a/ModelicaTest/Utilities.mo b/ModelicaTest/Utilities.mo index 88d104cdf9..6d6216e05b 100644 --- a/ModelicaTest/Utilities.mo +++ b/ModelicaTest/Utilities.mo @@ -379,33 +379,505 @@ 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(); + 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; + 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 + 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 := getTime(); + 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.month)); + Streams.print(" year = " + String(now.year)); + dow := dayOfWeek(now); + Streams.print(" dow = " + weekDays[dow]); + + 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 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; + + 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"; + input Boolean test_past = false "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 dt0, dt1, dt2, dt3; + Duration d; + Integer rc "Dummy 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)"); + + 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); + + 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)"); + end if; + + // compare two DateTime records created from system time. dt2 should be a few seconds later than dt1 + dt1 :=DateTime(); + 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), + "constructor from system time failed (dt1 is younger than dt2)"); + + + // ---------------- // + // operator tests // + // ---------------- // + + 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(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"; + 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); + + // == + assert(dt1==dt1, "dt1==dt1 failed"); + + // <> + assert(dt1<>dt2, "dt1<>dt2 failed"); + + // > + assert(dt2>dt1, "dt2>dt1 failed"); + + // >= + assert(dt2>=dt1, "dt2>=dt1 failed"); + assert(dt1>=dt1, "dt1>=dt1 failed"); + + // < + assert(dt2"+act_s); + + ref_s :="25h 01' 01.001''"; + 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"); + + // <> + 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' + 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}; + 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(i, 0, 0, 0, 0) for i in 1:2) == Duration(3, 0, 0, 0, 0), "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; @@ -509,6 +981,10 @@ 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); + result := ModelicaTest.Utilities.Duration(logFile); + result := ModelicaTest.Utilities.DateTime(logFile); ok := true; end testAll; 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;