Skip to content
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

refactor: improve leap year calculation for issue #48 #49

Merged
merged 1 commit into from
Jan 16, 2025

Conversation

majiidd
Copy link
Owner

@majiidd majiidd commented Jan 16, 2025

Replace the old leap year calculation method with a more accurate algorithm based on ICU4X's Persian calendar implementation. The new method uses a 33-year cycle rule with specific corrections for known exceptions.

Summary by CodeRabbit

  • Version Updates

    • Updated pre-commit repository versions for pyupgrade and bandit
    • Updated development package versions in Pipfile (coverage, pytest, tomli)
    • Incremented persiantools library version from 5.1.0 to 5.1.1
    • Updated copyright year to 2025
  • Improvements

    • Enhanced Jalali calendar leap year calculation logic
    • Added type hints to internal methods
    • Introduced new constants for leap year corrections
  • Testing

    • Expanded test cases for Jalali date conversions
    • Added more comprehensive leap year validation tests

Replace the old leap year calculation method with a more accurate algorithm
based on ICU4X's Persian calendar implementation. The new method uses a
33-year cycle rule with specific corrections for known exceptions.
Copy link

coderabbitai bot commented Jan 16, 2025

Walkthrough

This pull request introduces minor updates across multiple files in the persiantools project. The changes include version bumps for pre-commit hooks and development packages, an update to the library's version and copyright year, and significant improvements to the Jalali calendar's leap year calculation logic. The modifications enhance the library's accuracy and test coverage, with a focus on precise date conversion and leap year detection.

Changes

File Change Summary
.pre-commit-config.yaml Updated pyupgrade from v3.19.0 to v3.19.1 and bandit from 1.7.9 to 1.8.2
Pipfile Updated package versions: coverage to 7.6.10, pytest to 8.3.4, tomli to 2.2.1
persiantools/__init__.py Bumped version to 5.1.1 and updated copyright year to 2025
persiantools/jdatetime.py Added NON_LEAP_CORRECTION list, updated _is_ascii_digit method signature, introduced new constants for leap year calculations
tests/test_jalalidate.py Added new test cases for Jalali date conversions and leap year validation

Sequence Diagram

sequenceDiagram
    participant JalaliDate
    participant LeapYearCalculator
    
    JalaliDate->>LeapYearCalculator: Check if year is leap
    LeapYearCalculator-->>JalaliDate: Validate against NON_LEAP_CORRECTION
    LeapYearCalculator-->>JalaliDate: Apply 33-year leap year rule
    JalaliDate->>JalaliDate: Determine final leap year status
Loading

Poem

🐰 Leap years dance, a Persian delight,
Calendars twirl with mathematical might
Versions bump, tests shine bright
Code hops forward with rabbit's insight
Precision leaps to a new height! 🗓️

Finishing Touches

  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 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. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @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: 0

🧹 Nitpick comments (2)
tests/test_jalalidate.py (1)

206-304: Consider restructuring the leap year tests for better maintainability.

The test cases provide excellent coverage of the leap year calculation logic, including:

  • Multiple 33-year cycles
  • Edge cases and known exceptions
  • Historical dates
  • Known non-leap years

However, consider improving the test structure:

@pytest.mark.parametrize("test_id,year,expected", [
    # First 33-year cycle
    ("first_cycle_1", 1210, True),
    ("first_cycle_2", 1214, True),
    # ... more cases ...
    
    # Second 33-year cycle
    ("second_cycle_1", 1247, True),
    # ... more cases ...
    
    # Known non-leap years
    ("non_leap_1", 1206, False),
    # ... more cases ...
])
def test_is_leap(test_id, year, expected):
    assert JalaliDate.is_leap(year) == expected

This structure would:

  • Make it easier to add/remove test cases
  • Provide better error messages
  • Allow running specific test cases using test IDs
  • Improve test report readability
persiantools/jdatetime.py (1)

385-409: Consider enhancing the documentation of the 33-year rule.

The implementation is excellent:

  • Efficient handling of special cases
  • Clear control flow with early returns
  • Proper validation of input range
  • Correct use of the new constants

However, the documentation could be enhanced to explain the mathematical basis of the formula (25 * year + 11) % 33 < 8.

Add this explanation to the docstring:

"""
Determines if a given Persian year is a leap year using the 33-year rule,
with corrections for specific years that deviate from the rule.

The 33-year rule is based on the fact that the Persian calendar has 8 leap years
in every 33-year cycle. The formula (25 * year + 11) % 33 < 8 determines if a
year is a leap year by:
1. Multiplying the year by 25 and adding 11 to distribute leap years
2. Taking modulo 33 to work within the cycle
3. Comparing with 8 (the number of leap years in the cycle)

This function is based on the Rust implementation from the ICU4X project:
https://github.com/unicode-org/icu4x/blob/main/utils/calendrical_calculations/src/persian.rs

Args:
    year (int): The Persian year to check.

Returns:
    bool: True if the year is a leap year, False otherwise.
"""
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 200b060 and 0ae50a3.

⛔ Files ignored due to path filters (1)
  • Pipfile.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • .pre-commit-config.yaml (2 hunks)
  • Pipfile (1 hunks)
  • persiantools/__init__.py (1 hunks)
  • persiantools/jdatetime.py (2 hunks)
  • tests/test_jalalidate.py (4 hunks)
✅ Files skipped from review due to trivial changes (2)
  • persiantools/init.py
  • .pre-commit-config.yaml
🔇 Additional comments (5)
Pipfile (1)

10-14: Verify package versions exist and check for security advisories.

The specified versions are from 2025. Let's verify their existence and security status.

✅ Verification successful

All specified package versions exist and are secure

The package versions are valid on PyPI and have no known security vulnerabilities. These changes appear to be routine development dependency updates.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify package versions exist and check for security advisories

# Function to check PyPI for package version
check_package() {
    local package=$1
    local version=$2
    echo "Checking $package==$version..."
    curl -s "https://pypi.org/pypi/$package/$version/json" | jq -r '.info.version // "Not found"'
}

# Check each package version
check_package "coverage" "7.6.10"
check_package "pytest" "8.3.4"
check_package "tomli" "2.2.1"

# Check for security advisories
for package in "coverage" "pytest" "tomli"; do
    echo "Checking security advisories for $package..."
    gh api graphql -f query='{
        securityVulnerabilities(first: 5, ecosystem: PIP, package: "'$package'") {
            nodes {
                advisory {
                    summary
                    severity
                    publishedAt
                }
                vulnerableVersionRange
                firstPatchedVersion {
                    identifier
                }
            }
        }
    }'
done

Length of output: 2528

tests/test_jalalidate.py (2)

15-17: LGTM! Good test coverage for historical dates.

The added test cases for years 1210-1211 help validate the conversion accuracy for historical dates, which is particularly important for the new leap year calculation algorithm.


74-76: LGTM! Good bidirectional test coverage.

The added test cases ensure accurate bidirectional conversion between Gregorian and Jalali calendars for historical dates, which helps validate the robustness of the conversion logic.

persiantools/jdatetime.py (2)

134-218: LGTM! Well-structured constants for leap year calculation.

The new constants are well-designed:

  • NON_LEAP_CORRECTION provides an ordered list of exception years
  • NON_LEAP_CORRECTION_SET enables O(1) lookups using a frozenset
  • MIN_NON_LEAP_CORRECTION caches the first exception year for optimization

221-221: LGTM! Clear type hints.

The added type hints improve code clarity and enable better IDE support.

@majiidd majiidd merged commit 0c0e801 into master Jan 16, 2025
23 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Jan 17, 2025
@majiidd majiidd deleted the fix-issue-48 branch January 17, 2025 20:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant