Skip to content

Add date and time utilities #3247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 66 commits into
base: master
Choose a base branch
from

Conversation

beutlich
Copy link
Member

@beutlich beutlich commented Dec 2, 2019

Summary by CodeRabbit

  • New Features

    • Introduced comprehensive date and time utilities with improved formatting, parsing (including millisecond support), and conversion capabilities.
    • Added a conversion tool to assist in upgrading libraries between versions.
    • Updated real number rounding for cleaner integer conversion.
  • Improvements

    • Enhanced graphical icons for clarity in operator representations.
    • Refined documentation and contributor acknowledgments within the library.
  • Tests

    • Expanded test suite to validate new time, date, and duration functionalities as well as rounding behavior.

@beutlich beutlich added enhancement New feature or enhancement L: Utilities Issue addresses Modelica.Utilities labels Dec 2, 2019
@beutlich beutlich added this to the MSL4.0.0 milestone Dec 2, 2019
@beutlich beutlich self-assigned this Dec 2, 2019
@HansOlsson
Copy link
Contributor

The general idea is good, but:

  • I don't like the abbreviations in the record (and as outputs): min, mon, ms, even if consistent with "struct tm". (As far as I understand the names should be singular: second, minute, hour, year.)
  • I am unsure if we should have leap-year utilities. The reason is that it leads to people doing their own leap-year-calculations and that can lead to annoying bugs. Having a full set of utilities to avoid the need for that might be a better solution (that includes conversions between date-time and number, and handling of date-time-differences).

I tried to look at the Buildings-library. It uses full names in the records, but unfortunately it contains its own leap-year handling (hopefully correct):
https://github.com/lbl-srg/modelica-buildings/blob/master/Buildings/Utilities/Time/CalendarTime.mo

@beutlich
Copy link
Member Author

beutlich commented Dec 3, 2019

I started with an operator record for TimeType to also allow addition, subtraction etc. That meant, we also would need to consider a TimeDeltaType and conversion to absolute time, which is not straight-forward. Therefore, I rejected this first idea and went for the TimeType record as absolute point in time and without numerical conversion. (String conversion is still to do, but without handling of leap-seconds).

I don't like the abbreviations in the record (and as outputs): min, mon, ms, even if consistent with "struct tm". (As far as I understand the names should be singular: second, minute, hour, year.)

OK, will rename.

I am unsure if we should have leap-year utilities.

Hm, @GallLeo what do you think? @mwetter considers leap years only in range [2020, 2030] which might not be sufficient in a general purpose library.

@beutlich beutlich requested a review from GallLeo December 3, 2019 09:33
@sjoelund
Copy link
Member

sjoelund commented Dec 6, 2019

If you want to consider leap years, daylight savings, etc... You need to work on some absolute unit of s/etc relative from 1970... I think that's the only sane way of dealing with any kind of delta and ambiguity... Just convert between tm and time_t if you want to use that (and run the calculation in external C).

@beutlich
Copy link
Member Author

Here is a first (and incomplete) draft for conversion of date/time to string. Not sure if it is good enough ❔ . Any feedback is appreciated.

@dietmarw dietmarw removed their request for review December 11, 2019 07:19
@beutlich beutlich removed this from the MSL4.0.0 milestone Jan 7, 2020
@beutlich
Copy link
Member Author

beutlich commented Apr 1, 2020

@HansOlsson @DagBruck @t-sommer @StephanZiegler Yesterday, I noticed by chance that there are Testing.Utilities.Time.{DateTime, Duration} available in Dymola as operator records. This is exactly the way I wanted to go here for MSL, too. I believe these utilities came up as necessary side-product when meaasuring time in Modelica. Now, that I am aware of this library, I am also biased with the further development. My question is, if you could think of contributing or sharing Modelica code for further development as OSS, such that I am sure, no copyright will be violated?

@m-kessler
Copy link
Collaborator

We would be happy to move the operator records from the Testing library to the MSL.
@beutlich How shall we proceed? My recommendation would be, that I first push a purely MSL-based version of the operator records to this pull request. Then we can review the code and merge it with your work.

@beutlich beutlich force-pushed the add-time-utilities branch from b201bac to 9575f16 Compare April 1, 2020 11:07
@CLAassistant
Copy link

CLAassistant commented Apr 1, 2020

CLA assistant check
All committers have signed the CLA.

@beutlich
Copy link
Member Author

beutlich commented Apr 1, 2020

@m-kessler Thank you for the generous offer. I rebased the commits of this PR on master and resolved the merge conficts. This PR branch is now ready to be used. I added you as contributor (with access role = Triage) such that you can directly push to this branch (hopefully).

I already created a TimeType record. This can be now enhanced by using the two different operator records from the Testing package. It would be good if we can use the exsting names of the TimeType record components (which are different than the Testing package.)

@beutlich beutlich force-pushed the add-time-utilities branch from 9575f16 to 362c49d Compare April 9, 2020 21:08
@beutlich beutlich marked this pull request as ready for review April 10, 2020 21:01
@beutlich beutlich changed the title WIP: Add date and time utilities Add date and time utilities Apr 10, 2020
@beutlich beutlich force-pushed the add-time-utilities branch 4 times, most recently from 8cc2d55 to 47d29e8 Compare April 11, 2020 20:48
@beutlich beutlich added the L: C-Sources Issue addresses Modelica/Resources/C-Sources label Apr 11, 2020
beutlich and others added 27 commits April 9, 2025 12:10
Creating a Duration does not work anymore that way, due to "e547700 - Make Duration constructors unambiguous"
Co-Authored-By: Thomas Beutlich <modelica@tbeu.de>
... as the one we had did not catch negative durations with multiple days.
... as this is not supported at the moment.
... to fix inheritance, as its not allowed to extend from an operator record in Modelica 3.4
Conversion for MSL v4.0.0 no longer is suitable, hence migrate to next major version.
@beutlich beutlich force-pushed the add-time-utilities branch from 6c74cc5 to a37706a Compare April 9, 2025 10:10
Copy link

coderabbitai bot commented Apr 9, 2025

Walkthrough

This pull request introduces new time-handling functionalities across both external C sources and Modelica libraries. It adds implementation and tests for date and time formatting/parsing functions, integrates a new nearest-integer function in math blocks, and refines icon graphics. Build scripts, license files, conversion scripts, and test suites have been updated accordingly. Additionally, new packages and functions for time manipulation have been incorporated into the internal utilities, while deprecated implementations have been removed.

Changes

Files Change Summary
.CI/Test/ModelicaTime.c, .../Resources/C-Sources/ModelicaTime.c, .../Resources/C-Sources/ModelicaTime.h Added new time test program and implementations for date/time handling (epoch, difftime, localtime, strftime, strptime)
.../CI/Test/test.sh, .../BuildProjects/{CMake,test.cmake,autotools/Makefile.am,gcc/Makefile} Integrated ModelicaTime into build/test processes; added compilation commands, external source registrations, and object file rules
Modelica/Blocks/Math.mo, Modelica/Math/nearestInteger.mo, .../Modelica/Math/package.order Updated RealToInteger block to use Modelica.Math.nearestInteger; introduced the new nearestInteger function and updated package order
.../Modelica/Icons.mo, Complex.mo Modified icon graphics formatting; added the OperatorRecord class and standardized numerical property representations
.../Resources/Licenses/{LICENSE_ModelicaTime.txt,Third-party/LICENSE_repl_str.txt,Third-party/LICENSE_strptime.txt} Added new license files for ModelicaTime, repl_str, and strptime components
.../Resources/Scripts/Conversion/ConvertModelica_from_4.0.0_to_5.0.0.mos Introduced a conversion script to update Modelica code from version 4.0.0 to 5.0.0
Modelica/Utilities/{Internal.mo,System.mo,Time.mo,package.mo,package.order} Added new time utility packages and functions; removed getTime from System; updated documentation and acknowledgments
ModelicaTest/{Math.mo,Utilities.mo}, ModelicaTestConversion5.mo Enhanced tests with assertions for time functions and nearestInteger behavior; introduced a new conversion test package

Sequence Diagram(s)

sequenceDiagram
    participant TestApp
    participant ModelicaTime_C
    TestApp->>ModelicaTime_C: Initialize time components
    TestApp->>ModelicaTime_C: Call ModelicaTime_strftime(...)
    ModelicaTime_C-->>TestApp: Return formatted string
    TestApp->>ModelicaTime_C: Call ModelicaTime_strptime(formatted string)
    ModelicaTime_C-->>TestApp: Return parsed time components
Loading
sequenceDiagram
    participant User
    participant ModelicaTime_Internal
    User->>ModelicaTime_Internal: Call getTime
    ModelicaTime_Internal-->>User: Return DateTime record
    User->>ModelicaTime_Internal: Call dayOfWeek(DateTime)
    ModelicaTime_Internal-->>User: Return day of week
Loading

Poem

In fields of code I love to hop,
New time functions make my heart go “pop!”
I nibble bytes and crunch each test,
With rounded numbers, I feel so blessed.
The build runs smooth, the changes gleam,
A rabbit's dream in a coder's stream! 🐰

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🔭 Outside diff range comments (1)
Modelica/Resources/Scripts/Conversion/ConvertModelica_from_4.0.0_to_5.0.0.mos (1)

1-6: 💡 Verification agent

🧩 Analysis chain

Function relocation handled, but what about return type changes?

The conversion script correctly handles the relocation of the getTime function from System to Internal.Time. However, according to the PR objectives, getTime now returns a record instead of its previous form.

Are additional conversions needed for code that uses the return value of getTime? This might require updates to existing code that expects the old return type.


🏁 Script executed:

#!/bin/bash
# Search for usages of getTime in the codebase to assess potential impact
echo "Searching for usages of Modelica.Utilities.System.getTime..."
rg -l "Modelica\.Utilities\.System\.getTime" --glob "*.mo"

echo "Checking if there are examples of how the return value is used..."
rg "getTime\(\).*" --glob "*.mo"

Length of output: 995


Action Required: Update Destructuring for New Return Type

The conversion script in Modelica/Resources/Scripts/Conversion/ConvertModelica_from_4.0.0_to_5.0.0.mos correctly relocates the getTime function call from Modelica.Utilities.System to Modelica.Utilities.Internal.Time. However, as verified by the search results, several files (such as ModelicaTestConversion5.mo, ModelicaTest/Utilities.mo, Modelica/Utilities/Time.mo, and Modelica/Utilities/Internal.mo) still use destructuring assignments with getTime() that expect multiple discrete values. With the updated getTime now returning a record (e.g., a DateTime record), these usages are no longer compatible.

Next Steps:

  • Review and update destructuring assignments: Modify code that deconstructs the return value (e.g., (ms, sec, min, hour, day, mon, year) = getTime();) to extract the appropriate record fields.
  • Ensure downstream compatibility: Validate that any references to individual time components map correctly to the corresponding fields within the new record.
🧹 Nitpick comments (11)
.CI/Test/ModelicaTime.c (1)

7-29: Good test coverage for date/time formatting and parsing functions.

The test verifies both the formatting of date/time values to a string and parsing a string back to date/time components with appropriate assertions to validate the results.

Two suggestions for improvement:

  1. Consider adding tests for edge cases (leap years, month boundaries, etc.)
  2. Consider testing with different format strings to ensure all format specifiers work correctly
ModelicaTest/Utilities.mo (1)

984-987: Integration into testAll.

Including the new tests (Time, Duration, DateTime) ensures they run alongside the existing suite. Consider aggregating the boolean results (e.g., using logical AND) if you wish to fail fast upon any test’s failure. Otherwise, this is a welcome addition.

Modelica/Resources/C-Sources/ModelicaTime.h (2)

55-76: Macros for Non-null Pointers
The approach to annotate function parameters with nonnull attributes is effective. However, please ensure that all compilers and build environments you target support these macros. Otherwise, you may encounter compiler warnings or ignored attributes.


86-98: Consider Additional Error Documentation for Function Prototypes
While the function prototypes are clearly declared, consider adding short in-header documentation (e.g., with Doxygen-style comments) to clarify expected parameter ranges (e.g., valid year range) and error behavior. This will help maintain clarity for external integrators.

Modelica/Utilities/Internal.mo (1)

214-221: Check for Missing Error Handling in getNumberOfFiles
The external C call might fail if the directory does not exist or is inaccessible, but the function signature does not indicate how errors are handled or reported in Modelica. Consider providing at least a tested fallback or error message for invalid directories.

Modelica/Resources/C-Sources/ModelicaTime.c (2)

49-71: epoch Function Handling Before 1970
The logic calls mktime(&tres) and raises ModelicaFormatError on failure. As noted in prior discussions, mktime may fail or behave unexpectedly for years before 1970 on certain OS/toolchains. If strictly needed, consider documenting or gracefully handling years < 1970.


105-155: ModelicaTime_localtime Thread Safety
Under some build conditions, the code can fall back to localtime rather than localtime_r or localtime_s, which is not fully thread-safe. If you plan to call this function concurrently from multiple threads, consider reinforcing thread-safety at the platform-specific fallback level.

Modelica/Utilities/Time.mo (4)

213-228: Lack of input validation in 'fromReadable'.
Similar to the previous comment, this constructor simply sets the fields without verification. If invalid day-month-year combinations are passed in, the record becomes inconsistent. Consider adding at least a documented disclaimer or minimal checks to gracefully handle erroneous inputs.


500-500: Grammar fix in documentation.
The doc string says "Check if DateTime dt1 is later as dt2," but standard phrasing is "later than dt2." Consider updating for clarity and consistency.

- "Check if DateTime dt1 is later as dt2"
+ "Check if DateTime dt1 is later than dt2"

509-539: Consider simplifying the multi-level comparison logic.
These nested if-statements correctly compare date-time fields but are verbose. An alternative approach is to convert both DateTime objects to a numeric epoch value (e.g., using DateTime.epoch) and compare them directly. This may improve readability and consistency with other date/time operations.


637-644: Clarify doc for subtract_DateTime.
The documentation line says "Return time delta between dt2 and dt1 as Duration," whereas the function effectively calculates (dt1 - dt2). The call to fromDateTimes(dt2, dt1) is reversed to achieve that result, which can be confusing. Consider rewording the doc comment to explicitly mention that the function returns dt1 - dt2 or swapping the parameters for clarity.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between be949b2 and a37706a.

📒 Files selected for processing (28)
  • .CI/Test/ModelicaTime.c (1 hunks)
  • .CI/Test/test.sh (2 hunks)
  • Complex.mo (1 hunks)
  • Modelica/Blocks/Math.mo (2 hunks)
  • Modelica/Icons.mo (1 hunks)
  • Modelica/Math/nearestInteger.mo (1 hunks)
  • Modelica/Math/package.order (1 hunks)
  • Modelica/Resources/BuildProjects/CMake/src.cmake (1 hunks)
  • Modelica/Resources/BuildProjects/CMake/test.cmake (1 hunks)
  • Modelica/Resources/BuildProjects/autotools/Makefile.am (1 hunks)
  • Modelica/Resources/BuildProjects/gcc/Makefile (2 hunks)
  • Modelica/Resources/C-Sources/ModelicaTime.c (1 hunks)
  • Modelica/Resources/C-Sources/ModelicaTime.h (1 hunks)
  • Modelica/Resources/C-Sources/readme.txt (2 hunks)
  • Modelica/Resources/C-Sources/repl_str.h (1 hunks)
  • Modelica/Resources/C-Sources/strptime.h (1 hunks)
  • Modelica/Resources/Licenses/LICENSE_ModelicaTime.txt (1 hunks)
  • Modelica/Resources/Licenses/Third-party/LICENSE_repl_str.txt (1 hunks)
  • Modelica/Resources/Licenses/Third-party/LICENSE_strptime.txt (1 hunks)
  • Modelica/Resources/Scripts/Conversion/ConvertModelica_from_4.0.0_to_5.0.0.mos (1 hunks)
  • Modelica/Utilities/Internal.mo (3 hunks)
  • Modelica/Utilities/System.mo (0 hunks)
  • Modelica/Utilities/Time.mo (1 hunks)
  • Modelica/Utilities/package.mo (2 hunks)
  • Modelica/Utilities/package.order (1 hunks)
  • ModelicaTest/Math.mo (1 hunks)
  • ModelicaTest/Utilities.mo (2 hunks)
  • ModelicaTestConversion5.mo (1 hunks)
💤 Files with no reviewable changes (1)
  • Modelica/Utilities/System.mo
🧰 Additional context used
🧬 Code Graph Analysis (4)
.CI/Test/ModelicaTime.c (1)
Modelica/Resources/C-Sources/ModelicaTime.h (1)
  • ModelicaTime_strftime (91-93)
Modelica/Resources/C-Sources/repl_str.h (1)
Modelica/Resources/C-Sources/zlib/gzguts.h (2)
  • malloc (124-124)
  • free (125-125)
Modelica/Resources/C-Sources/ModelicaTime.c (3)
Modelica/Resources/C-Sources/ModelicaTime.h (4)
  • ModelicaTime_difftime (86-87)
  • ModelicaTime_localtime (88-90)
  • ModelicaTime_strftime (91-93)
  • ModelicaTime_strptime (94-97)
Modelica/Resources/C-Sources/repl_str.h (2)
  • repl_str (18-109)
  • repl_str (18-18)
Modelica/Resources/C-Sources/strptime.h (2)
  • strptime_ms (98-520)
  • strptime_ms (99-99)
Modelica/Resources/C-Sources/ModelicaTime.h (1)
Modelica/Resources/C-Sources/ModelicaTime.c (2)
  • ModelicaTime_difftime (73-103)
  • char (157-219)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: external_c_checks_cmake_windows-msvc
🔇 Additional comments (39)
Modelica/Resources/Licenses/LICENSE_ModelicaTime.txt (1)

1-27: License file looks correct and appropriate.

The license file uses a standard 3-clause BSD license, which is appropriate for the new ModelicaTime component. The copyright year range (2020-2024) is current and matches the project's conventions.

Modelica/Resources/C-Sources/readme.txt (2)

9-9: Addition of ModelicaTime.c is appropriate.

The addition of ModelicaTime.c to the ModelicaExternalC library list is consistent with the PR's objective of adding date and time utilities.


57-57: Date updated appropriately.

The date has been updated to reflect when these changes were made.

Modelica/Math/nearestInteger.mo (1)

1-44: Implementation of nearestInteger function looks correct.

The function correctly implements the "round half away from zero" rule using the appropriate mathematical operations for both positive and negative numbers. The documentation is thorough with clear examples and proper references to related components.

Modelica/Icons.mo (2)

530-546: Formatting changes in Record class coordinates look good.

The changes to remove decimal points from integer coordinates (e.g., changing from origin={0.0,-25.0} to origin={0,-25}) improve consistency and readability without affecting functionality.


553-582: Addition of OperatorRecord icon class is appropriate.

The new OperatorRecord class provides a consistent visual representation for operator records, matching the existing design patterns in the library. This addition supports the PR objective of enhancing date and time utilities, which likely use operator records.

Modelica/Utilities/package.order (1)

7-7: LGTM! New Time package added to the package order.

This addition aligns with the PR objectives to introduce new date and time utilities to the Modelica Standard Library. The Time package is correctly positioned in the package order file.

Modelica/Math/package.order (1)

13-13: LGTM! New nearestInteger function added to Math package.

Adding the nearestInteger function to the Math package makes sense for time-related calculations. Based on the summary, this function will replace conditional logic using floor and ceil in the RealToInteger block, which is a good simplification.

Modelica/Resources/Licenses/Third-party/LICENSE_repl_str.txt (1)

1-5: LGTM! Proper license documentation for third-party code.

The license file properly documents the usage of the repl_str function with appropriate attribution and licensing information. This is important for maintaining proper open-source compliance.

Modelica/Resources/Licenses/Third-party/LICENSE_strptime.txt (1)

1-26: License inclusion looks good.

The BSD license for the strptime implementation from NetBSD Foundation is properly included. This satisfies legal requirements for incorporating third-party code.

Modelica/Resources/BuildProjects/CMake/test.cmake (1)

9-9: Appropriate test addition.

Adding ModelicaTime to the MODELICA_TESTS list properly integrates the new time functionality into the testing framework.

Modelica/Resources/BuildProjects/gcc/Makefile (2)

14-15: ModelicaTime.o added correctly to object list.

The ModelicaTime module has been properly added to the EXTC_OBJS list.


97-98: Compilation rule for ModelicaTime looks good.

The compilation rule for ModelicaTime.o follows the same pattern as other modules.

Modelica/Resources/BuildProjects/CMake/src.cmake (1)

73-74: New time-related source files correctly added.

The addition of ModelicaTime.c, ModelicaTime.h, repl_str.h, and strptime.h to the EXTC_SOURCES list properly integrates the new time functionality into the CMake build system.

The additions align with the PR objectives to enhance date and time functionalities in the Modelica Standard Library.

Also applies to: 76-76, 79-79

Complex.mo (1)

21-36: Improved icon graphics formatting for better readability.

The restructuring of the Icon graphics annotation with proper indentation and line breaks makes the code more maintainable while maintaining the same functionality.

ModelicaTest/Math.mo (1)

36-41: Test cases added for the new nearestInteger function.

These assertions properly verify the rounding behavior of the Math.nearestInteger function for different inputs, including positive and negative values, which ensures the function works as expected.

Note: There's a commented-out test for a border case (1.49999999999999999999) that would fail with the current implementation.

.CI/Test/ModelicaTime.c (1)

1-6: LGTM: Appropriate header files included.

The includes are appropriate for a test file that will be checking the time-related functions in Modelica.

.CI/Test/test.sh (2)

13-14: Added static linking test for ModelicaTime.

The ModelicaTime test is appropriately integrated into the static linking test section.


36-37: Added dynamic linking test for ModelicaTime.

The ModelicaTime test is appropriately integrated into the dynamic linking test section.

Modelica/Resources/BuildProjects/autotools/Makefile.am (1)

2-2: Added ModelicaTime.c to the build system.

The file ModelicaTime.c has been added to the list of source files for the libModelicaExternalC library. This addition is consistent with the PR objectives of enhancing date and time functionalities in the Modelica Standard Library.

Modelica/Resources/C-Sources/repl_str.h (3)

1-6: Added string replacement utility with proper attribution.

A string replacement utility function has been introduced with appropriate attribution to its original source and release information. This is good practice for incorporating third-party code.


7-17: Proper header guards and standard library inclusions.

The header file includes proper header guards and includes the necessary standard libraries. The conditional inclusion of stdint.h for C99 or later is a good compatibility practice.


18-108: Well-implemented string replacement function.

The repl_str function is well-implemented with:

  • Clear parameter documentation
  • Configurable cache sizing parameters for performance tuning
  • Proper memory management
  • Handling of both C++ and C compilation
  • Error handling for memory allocation failures
  • Special case handling for when no replacements are needed

This implementation balances performance and memory usage effectively and will be useful for string manipulation in time formatting operations.

Modelica/Blocks/Math.mo (2)

1942-1942: Improved RealToInteger implementation using nearestInteger.

The implementation of the RealToInteger block has been simplified by using the Modelica.Math.nearestInteger function instead of the previous conditional expression with floor and ceil. This change improves code maintainability and standardizes the rounding behavior.


1962-1966: Updated documentation to reflect implementation change.

The documentation has been properly updated to reflect the implementation change, now referencing the nearestInteger function that's being used. This ensures consistency between implementation and documentation.

ModelicaTestConversion5.mo (1)

23-36: Added proper package documentation and licensing information.

The package includes appropriate annotations for version dependencies, copyright information, and licensing under the 3-Clause BSD license. The documentation clearly explains the purpose of this test package.

Modelica/Utilities/package.mo (2)

128-145: Clarity of acknowledgments looks good.

The revised text properly credits additional contributors (Marco Kessler and Thomas Beutlich). This ensures transparency regarding project authorship.


214-215:

✅ Verification successful

New Time sublibrary reference.

Thank you for explicitly documenting the new Time sublibrary. This helps users discover the added date/time functionalities. Please verify that this link (modelica://Modelica.Utilities.Time) is fully functional across all supported tools.


🏁 Script executed:

#!/bin/bash
# Checking for references to "Modelica.Utilities.Time" in the codebase:
rg --context 3 "Modelica.Utilities.Time"

Length of output: 21826


Link Verification Complete: New "Time" Sublibrary Reference is Consistent

Thank you for explicitly documenting the new Time sublibrary. Our verification confirms that the link (modelica://Modelica.Utilities.Time) is referenced consistently across the codebase (e.g., in Modelica/Utilities/package.mo, Modelica/Utilities/Time.mo, and related test files). There are no discrepancies observed in its usage that could impact functionality across supported tools.

Modelica/Resources/C-Sources/strptime.h (1)

1-574: Overall implementation appears correct and complete.

This file provides extensive parsing routines for date and time, including milliseconds support. A few considerations:

  • Ensure that any null inputs (e.g., buf, fmt, or tm) are guarded or documented.
  • Confirm thread-safety if these routines are used in concurrent contexts (static variables, global data, etc. are not modified).
  • Validate that the license headers and disclaimers comply with your repository’s guidelines.

Otherwise, this looks solid and aligns with typical BSD-derived strptime implementations.

ModelicaTest/Utilities.mo (4)

393-437: Function Time tests look comprehensive.

Good job covering dayOfWeek, leap year logic, and negative year range. These tests help validate the date/time utility thoroughly.


714-880: Duration constructor and operator tests.

All main duration operations are validated: addition, subtraction, comparisons, and formatting. Negative durations and boundary cases are handled. This coverage is solid.


387-387:

❓ Verification inconclusive

Check cross-platform availability of getPid.

Using Modelica.Utilities.System.getPid() is straightforward. Just ensure it behaves consistently on all supported platforms (Windows, Linux, macOS, etc.).


🏁 Script executed:

#!/bin/bash
# Searching references and documentation for getPid usage
rg "getPid"

Length of output: 894


Revisit cross-platform consistency for Modelica.Utilities.System.getPid usage

Our grep results confirm that getPid is defined and widely used throughout the Modelica library (see references in Modelica/Utilities/System.mo, among others). However, note the comment in Modelica/Math/Random.mo mentioning that getPid (and getTime) might not be available on some targets. Please double-check that the implementation of Modelica.Utilities.System.getPid() behaves consistently across all supported platforms (Windows, Linux, macOS, etc.). If there are any platform-specific limitations, consider adding conditional handling or updating the documentation accordingly.


439-713:

✅ Verification successful

DateTime tests are well-structured.

The coverage includes leap years, century logic, large date shifts, and negative epoch scenarios. One subtle point: confirm that the epoch offsets for certain far-future years (e.g., 2100) match actual calculations. Discrepancies could arise from manual or approximate references.


🏁 Script executed:

#!/bin/bash
# Cross-check the stated epoch references for the 2100 date
# Search for the lines referencing `2100, 2, 28`
rg -A 5 "2100,\s*2,\s*28"

Length of output: 362


Verification: DateTime Test Cases Confirmed

The DateTime tests are comprehensive, covering leap years, century rules, large date shifts, and negative epoch scenarios. The far-future case—with a DateTime of 2100-02-28 23:59:59 and an expected epoch offset of 951782399—is implemented using a custom reference epoch (epoch_year=2070) and the verification confirms that the code reflects this intended behavior.

Modelica/Resources/C-Sources/ModelicaTime.h (1)

1-30: File License Header Looks Good.
All standard clauses appear correctly outlined. No immediate changes needed.

Modelica/Utilities/Internal.mo (3)

244-260: Validate Unit Test Coverage for diffTime
The new function diffTime is central to time offset computations. Consider verifying its behavior for edge cases like month = 2, day = 29, or years before 1970. This will help ensure reliability.

Do you have test cases covering leap year transitions and earlier than 1970 date usage?


265-343: Impure Function getTime
Calling an external function in an impure context is correct for retrieving system time. However, ensure that repeated calls in a tight loop are safe. Some embedded or specialized systems might have constraints on system time retrieval.


399-418: dayOfWeek Algorithm Implementation
Tomohiko Sakamoto’s algorithm is a compact and efficient approach. Ensure you have tested at boundary years (e.g., 1900 or 2100) to confirm correct weekday calculations around century transitions, especially since tm_year offsets might differ in some locales.

Modelica/Resources/C-Sources/ModelicaTime.c (1)

73-103: ModelicaTime_difftime Special Cases
The conditional avoidance of mktime for New Year 1970 is a clever workaround. However, be sure that drastically different time zones or daylight-saving boundaries near Jan 1, 1970 do not produce inconsistencies. You might consider a per-platform test harness around this logic.

Modelica/Utilities/Time.mo (1)

200-207: Consider validating combined date fields.
The current approach neither validates that a provided day value is valid for its corresponding month nor checks year-month-day consistency (e.g., February 30). While this might be acceptable in Modelica or handled by external functions, adding an explicit validation (or documenting that invalid dates are unsupported) can reduce user errors.

Would you like a verification script that searches the codebase for any usage of DateTime that might implicitly assume valid inputs?

Comment on lines +1 to +22
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="<html>
<p>
Conversion test for <a href=\"https://github.com/modelica/ModelicaStandardLibrary/issues/3247\">#3247</a>.
</p>
</html>"));
end Issue3247;
end Utilities;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Created conversion test model for Issue #3247.

A test model has been created to verify the conversion script's handling of the changes to the time utilities. The model calls Modelica.Utilities.System.getTime() and assigns the returned values to time component variables.

However, this model may need to be updated since the PR indicates that getTime has been moved from the System package to a new Time package, and it now returns a record instead of individual values.

The conversion script should handle updating this call to use the new Time package and handle the returned record properly. Please verify that the conversion script correctly transforms:

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

to the new API format.

Comment on lines +221 to +259
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
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Graceful Failure Handling in ModelicaTime_strptime
Upon parsing failure, the function sets all outputs to 0. Consider logging an error or providing a conditional return code for better traceability, especially if invalid user input is expected or more advanced error handling is desired.

Comment on lines +157 to +219
_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
}
Copy link

@coderabbitai coderabbitai bot Apr 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Memory Leak Risk on Formatting Failure
When retLen is zero or exceeds maxSize, the function returns "" (line 215) without freeing timePtr. This leads to a memory leak. Consider freeing timePtr before returning, or returning a dynamically allocated empty string that is freed by the caller.

-            return "";
+            free(timePtr);
+            return "";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_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
}
_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 {
free(timePtr);
return "";
}
}
#endif
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

timePtr was allocated by ModelicaAllocateString and thus its lifetime is managed by the runtime. It must not be freed using free.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@beutlich Thank you for the correction. You're absolutely right - memory allocated with ModelicaAllocateString is managed by the runtime and should not be manually freed with free(). My suggestion was incorrect and could have introduced memory corruption issues. I appreciate you clarifying this important distinction about the memory management approach in this codebase.

✏️ Learnings added
Learnt from: beutlich
PR: modelica/ModelicaStandardLibrary#3247
File: Modelica/Resources/C-Sources/ModelicaTime.c:157-219
Timestamp: 2025-04-11T16:43:22.169Z
Learning: In Modelica, memory allocated with `ModelicaAllocateString` is managed by the runtime system and must not be freed manually using `free()`. Attempting to free this memory manually would lead to memory corruption or double-free errors.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or enhancement L: C-Sources Issue addresses Modelica/Resources/C-Sources L: Utilities Issue addresses Modelica.Utilities
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants