NPDateTime Unified Project Structure¶
Combining lookup tables (fast, accurate) with astronomical calculations (future-proof, educational).
📁 Complete Directory Structure¶
npdatetime/
├── Cargo.toml # Fixed configuration
├── build.rs # Compile-time CSV → Rust conversion
├── README.md
├── LICENSE
├── .gitignore
│
├── CONTRIBUTING.md # Guide for contributors
│
├── data/
│ ├── calendar_bs.csv # Source of truth (2000-2090 BS)
│ ├── calendar_bs.json # Generated for reference
│ └── validation/
│ ├── known_events.json # Known Sankranti dates for validation
│ └── test_cases.csv # Test cases for both methods
│
├── src/
│ ├── lib.rs # Main library entry
│ │
│ ├── lookup/ # Fast lookup table approach
│ │ ├── mod.rs # Lookup module
│ │ ├── data.rs # Embedded calendar data
│ │ ├── converter.rs # BS ↔ AD conversion
│ │ └── compressed.rs # Optional: compressed storage
│ │
│ ├── astronomical/ # Astronomical calculation approach
│ │ ├── mod.rs # Astronomical module
│ │ ├── core/
│ │ │ ├── mod.rs
│ │ │ ├── constants.rs # Astronomical constants
│ │ │ └── time.rs # Julian Day, time conversions
│ │ ├── solar/
│ │ │ ├── mod.rs
│ │ │ ├── position.rs # Sun position (VSOP87)
│ │ │ └── sankranti.rs # Solar events
│ │ ├── lunar/
│ │ │ ├── mod.rs
│ │ │ ├── position.rs # Moon position (ELP-2000)
│ │ │ └── tithi.rs # Lunar days
│ │ └── calendar.rs # Month length calculator
│ │
│ ├── core/ # Shared core types
│ │ ├── mod.rs
│ │ ├── date.rs # NepaliDate struct
│ │ ├── datetime.rs # NepaliDateTime struct
│ │ ├── error.rs # Error types
│ │ └── format.rs # Formatting & parsing
│ │
│ └── utils/
│ ├── mod.rs
│ ├── validation.rs # Validation utilities
│ └── interpolation.rs # Math utilities
│
├── bindings/
│ ├── python/ # Python bindings (PyO3)
│ │ ├── Cargo.toml
│ │ ├── pyproject.toml
│ │ ├── src/
│ │ │ └── lib.rs
│ │ └── tests/
│ │ └── test_python.py
│ │
│ ├── javascript/ # JavaScript/WASM bindings
│ │ ├── Cargo.toml
│ │ ├── package.json
│ │ ├── src/
│ │ │ └── lib.rs
│ │ └── examples/
│ │ └── react-example/
│ │
│ │── java/ # Java JNI bindings
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ └── lib.rs
│ │ └── java/
│ │ └── NepaliDate.java
│ │
│ │── php/ # PHP bindings
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ └── lib.rs
│ │ └── examples/
│ │ └── example.php
│ │
│
├── examples/
│ ├── basic_usage.rs # Basic usage with lookup
│ ├── conversion_demo.rs # BS ↔ AD conversions
│ ├── formatting.rs # Date formatting
│ ├── compare_methods.rs # Compare lookup vs astronomical
│ │
│ └── astronomical/ # Astronomical examples
│ ├── calculate_sankranti.rs
│ ├── calculate_tithi.rs
│ └── generate_calendar.rs
│
├── tests/
│ ├── integration_tests.rs # Integration tests
│ ├── lookup_tests.rs # Lookup table tests
│ ├── astronomical_tests.rs # Astronomical calculation tests
│ └── validation_tests.rs # Cross-validation tests
│
├── benches/
│ ├── date_conversion.rs # Benchmark conversions
│ └── astronomical_bench.rs # Benchmark calculations
│
└── docs/
├── ROADMAP.md # Project status and future plans
├── DEVELOPMENT_GUIDE.md # Implementation guidelines
├── IMPLEMENTATION_GUIDE.md # Step-by-step implementation guide
├── PROJECT_STRUCTURE.md # This file
└── ASTRONOMY.md # Theory behind calculations
🔧 Key Files¶
build.rs (Compile-time Data Embedding)¶
// build.rs
use std::env;
use std::fs::{File, create_dir_all};
use std::io::Write;
use std::path::Path;
fn main() {
println!("cargo:rerun-if-changed=data/calendar_bs.csv");
let out_dir = env::var("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("calendar_data.rs");
// Read CSV and generate Rust code
let mut reader = csv::Reader::from_path("data/calendar_bs.csv")
.expect("Failed to read calendar_bs.csv");
let mut data = Vec::new();
for result in reader.records() {
let record = result.expect("Failed to parse CSV record");
let year: i32 = record[0].parse().expect("Invalid year");
let month: u8 = record[1].parse().expect("Invalid month");
let days: u8 = record[2].parse().expect("Invalid days");
data.push((year, month, days));
}
// Generate Rust source code
let mut f = File::create(&dest_path).unwrap();
writeln!(f, "// Auto-generated from calendar_bs.csv").unwrap();
writeln!(f, "// DO NOT EDIT MANUALLY").unwrap();
writeln!(f, "").unwrap();
writeln!(f, "pub const CALENDAR_DATA: &[(i32, u8, u8)] = &[").unwrap();
for (year, month, days) in data {
writeln!(f, " ({}, {}, {}),", year, month, days).unwrap();
}
writeln!(f, "];").unwrap();
println!("cargo:warning=Generated calendar data with {} entries",
reader.position().line());
}
src/lib.rs (Main Entry Point)¶
rust
//! NPDateTime - High-performance Nepali (Bikram Sambat) datetime library
//!
//! This library provides two approaches:
//! 1. **Lookup Tables** (default): Fast, accurate, works offline
//! 2. **Astronomical Calculations** (optional): Future-proof, educational
//!
//! # Quick Start
//!
//!rust
//! use npdatetime::NepaliDate;
//!
//! // Create a date
//! let date = NepaliDate::new(2077, 5, 19)?;
//!
//! // Convert to Gregorian
//! let (year, month, day) = date.to_gregorian()?;
//!
//! // Format
//! println!("{}", date.format("%Y-%m-%d"));
//! ```
![cfg_attr(not(feature = "std"), no_std)]¶
![cfg_attr(docsrs, feature(doc_cfg))]¶
// Core modules (always available) pub mod core; pub use core::{NepaliDate, NepaliDateTime, NpdatetimeError, Result};
// Lookup table module (default)
[cfg(feature = "lookup-tables")]¶
[cfg_attr(docsrs, doc(cfg(feature = "lookup-tables")))]¶
pub mod lookup;
// Astronomical calculation module (optional)
[cfg(feature = "astronomical")]¶
[cfg_attr(docsrs, doc(cfg(feature = "astronomical")))]¶
pub mod astronomical;
// Utilities pub mod utils;
/// Library version pub const VERSION: &str = env!("CARGO_PKG_VERSION");
/// Prelude for common imports pub mod prelude { pub use crate::core::{NepaliDate, NepaliDateTime}; pub use crate::core::NpdatetimeError;
#[cfg(feature = "lookup-tables")]
pub use crate::lookup::BsCalendar;
#[cfg(feature = "astronomical")]
pub use crate::astronomical::{SolarCalculator, LunarCalculator};
}
[cfg(test)]¶
mod tests { use super::*;
#[test]
fn test_version()