range_date

Crates.iorange_date
lib.rsrange_date
version0.2.3
created_at2025-09-15 14:46:48.065531+00
updated_at2025-11-26 03:06:52.840214+00
descriptionA powerful Rust crate for handling date periods with embedded data and comprehensive date range operations.
homepagehttps://github.com/GuoMonth/range_date
repositoryhttps://github.com/GuoMonth/range_date
max_upload_size
id1840152
size87,961
gpromote (GuoMonth)

documentation

https://docs.rs/range_date

README

range_date

Crates.io Documentation codecov License: MIT

A powerful Rust crate for handling date periods with embedded data and comprehensive date range operations.

Features

  • ๐Ÿ—“๏ธ Rich Date Periods: Enum-based periods with embedded year/index data - Year(u32), Quarter(u32, u32), Month(u32, u32), Daily(u32, u32)
  • ๐Ÿ”„ Type Conversion: String parsing and serialization support with validation
  • ๐Ÿ“… Date Range Operations: Calculate first/last days, check date containment
  • ๐ŸŽฏ Date Conversion: Convert NaiveDate to any period type
  • ๐Ÿ”€ Advanced Period Operations: Navigate between periods (succ/pred), decompose into sub-periods, aggregate to parent periods, and bulk navigation (succ_n/pred_n/offset_n)
  • ๐Ÿ“Š Range Generation: Generate all periods between two dates for comprehensive date range analysis
  • ๐Ÿ“š Comprehensive Documentation: 26+ doc tests with practical examples for all public methods
  • โšก High Performance: Built on top of the efficient chrono library
  • ๐Ÿ›ก๏ธ Type Safety: Complete validation with proper error handling
  • ๐Ÿงช Leap Year Support: Accurate leap year detection and day validation

Quick Start

Add this to your Cargo.toml:

[dependencies]
range_date = "0.2.3"

Usage Examples

Basic Usage

use range_date::range_type::DatePeriod;
use std::str::FromStr;

// Create date periods with validation
let q1_2024 = DatePeriod::quarter(2024, 1).unwrap();
let may_2024 = DatePeriod::month(2024, 5).unwrap();

// String representation
println!("{}", q1_2024); // Output: 2024Q1
println!("{}", may_2024); // Output: 2024M5

// Parse from string
let parsed = DatePeriod::from_str("2024M03").unwrap();
println!("{:?}", parsed); // DatePeriod::Month(2024, 3)

// Get date ranges
let first_day = q1_2024.get_first_day()?; // 2024-01-01
let last_day = q1_2024.get_last_day()?;   // 2024-03-31

Date Period Construction

use range_date::range_type::DatePeriod;

// Create with validation
let year_2024 = DatePeriod::year(2024);
let q2_2024 = DatePeriod::quarter(2024, 2).unwrap();   // Q2: April-June
let march_2024 = DatePeriod::month(2024, 3).unwrap();  // March
let day_60 = DatePeriod::daily(2024, 60).unwrap();     // 60th day of 2024

// Validation catches errors
assert!(DatePeriod::quarter(2024, 5).is_err()); // Quarter 5 doesn't exist
assert!(DatePeriod::month(2024, 13).is_err());  // Month 13 doesn't exist
assert!(DatePeriod::daily(2023, 366).is_err()); // Day 366 invalid in non-leap year

Date Conversion

use range_date::range_type::DatePeriod;
use chrono::NaiveDate;

let date = NaiveDate::from_ymd_opt(2024, 8, 15).unwrap(); // August 15, 2024

// Convert to different period types
let as_year = DatePeriod::from_date_as_year(date);     // 2024Y
let as_quarter = DatePeriod::from_date_as_quarter(date); // 2024Q3
let as_month = DatePeriod::from_date_as_month(date);     // 2024M8
let as_daily = DatePeriod::from_date_as_daily(date);     // 2024D228

Date Range Operations

use range_date::range_type::DatePeriod;
use chrono::NaiveDate;

let q1_2024 = DatePeriod::quarter(2024, 1).unwrap();

// Get date boundaries
let start = q1_2024.get_first_day()?; // 2024-01-01
let end = q1_2024.get_last_day()?;    // 2024-03-31

// Check date containment
let valentine = NaiveDate::from_ymd_opt(2024, 2, 14).unwrap();
let contains = q1_2024.contains_date(valentine); // true

Serialization Support

use range_date::range_type::DatePeriod;
use serde_json;

let quarter = DatePeriod::quarter(2024, 2).unwrap();
let json = serde_json::to_string(&quarter).unwrap();
println!("{}", json); // "2024Q2"

let deserialized: DatePeriod = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, DatePeriod::Quarter(2024, 2));

Advanced Period Operations [NEW]

use range_date::range_type::DatePeriod;
use chrono::NaiveDate;

// Range generation - Generate all periods between two dates
let start = NaiveDate::from_ymd_opt(2024, 1, 1).unwrap();
let end = NaiveDate::from_ymd_opt(2024, 6, 30).unwrap();
let quarters = DatePeriod::between_date_as_quarter(start, end).unwrap();
assert_eq!(quarters.len(), 2); // Q1 and Q2 2024

// Period navigation - Get next/previous periods
let q1 = DatePeriod::quarter(2024, 1).unwrap();
let q2 = q1.succ().unwrap(); // Next: 2024Q2
let back_to_q1 = q2.pred().unwrap(); // Previous: 2024Q1

// Bulk period navigation - Advance or retreat by multiple steps
let q4 = q1.succ_n(3).unwrap(); // Advance 3 quarters: 2024Q4
let back_to_q1_bulk = q4.pred_n(3).unwrap(); // Retreat 3 quarters: 2024Q1
let offset_q3 = q1.offset_n(2).unwrap(); // Unified offset: 2024Q3
let offset_back = q4.offset_n(-1).unwrap(); // Unified offset backward: 2024Q3

// Period decomposition - Break down into sub-periods
let year_2024 = DatePeriod::year(2024);
let quarters_in_year = year_2024.decompose();
assert_eq!(quarters_in_year.len(), 4); // 4 quarters in a year

// Period aggregation - Get parent period
let month = DatePeriod::month(2024, 5).unwrap();
let quarter = month.aggregate();
assert_eq!(quarter, DatePeriod::quarter(2024, 2).unwrap());

Period Information Queries

use range_date::range_type::DatePeriod;

let period = DatePeriod::quarter(2024, 2).unwrap();

// Query period information
assert_eq!(period.get_year(), 2024);
assert_eq!(period.value(), 2); // Quarter number
assert_eq!(period.short_name(), "Q");
assert_eq!(period.period_name(), "QUARTER");

Date Range Format

The crate uses a compact string format for date periods:

  • Format: YYYY[PERIOD][INDEX]
  • Examples:
    • 2024Y - Year 2024
    • 2024Q2 - Q2 2024 (April-June)
    • 2024M03 - March 2024
    • 2024D060 / 2024D60 - 60th day of 2024

Period Types

Period Constructor String Format Description
Year DatePeriod::year(2024) 2024Y Entire year
Quarter DatePeriod::quarter(2024, 1) 2024Q1 Q1: Jan-Mar, Q2: Apr-Jun, etc.
Month DatePeriod::month(2024, 3) 2024M3 Specific month (1-12)
Daily DatePeriod::daily(2024, 60) 2024D60 Specific day of year (1-366)

API Documentation

For complete API documentation, visit docs.rs.

Testing

Run the comprehensive test suite:

# Run all tests
cargo test

# Run specific test files
cargo test --test integration_tests
cargo test --test year_format_tests

# Run tests with output for manual verification
cargo test test_constructor_examples -- --nocapture
cargo test test_year_format -- --nocapture

License

This project is licensed under the MIT License - see the LICENSE file for details.

Contributing

We welcome contributions! Here are several ways you can help:

๐Ÿ› Found a Bug?

  • Open an issue with a clear description
  • Include code examples that reproduce the problem
  • Mention your Rust version and operating system

๐Ÿ’ก Have a Feature Request?

  • Check existing issues to avoid duplicates
  • Open a new issue describing your use case and proposed solution
  • Explain why this feature would be valuable

๐Ÿ”ง Want to Contribute Code?

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes with comprehensive tests
  4. Ensure all tests pass (cargo test)
  5. Run formatting (cargo fmt) and linting (cargo clippy)
  6. Commit your changes (git commit -am 'Add amazing feature')
  7. Push to the branch (git push origin feature/amazing-feature)
  8. Open a Pull Request

๐Ÿ“– Documentation Improvements?

  • Documentation improvements are always welcome
  • Update examples in README.md or code comments
  • Help improve API documentation

Have questions or suggestions? Please open an issue - we'd love to hear from you!

Commit count: 15

cargo fmt