trading_calendar/lib.rs
1// Copyright (c) 2024 Trading Calendar Contributors
2// Licensed under the MIT License and Apache License, Version 2.0
3//
4//! # Trading Calendar
5//!
6//! A comprehensive trading calendar for global financial markets, providing holidays,
7//! trading hours, and early close information.
8//!
9//! ## Features
10//!
11//! - 🌍 **Multiple Markets**: NYSE, NASDAQ, LSE, TSE, TSX
12//! - ⏰ **Trading Hours**: Regular, pre-market, and after-hours sessions
13//! - 📅 **Holiday Detection**: All market holidays with weekend adjustments
14//! - 🕐 **Early Closes**: Half-day schedules (Christmas Eve, Black Friday, etc.)
15//! - 🌐 **Timezone Support**: Automatic handling of market timezones
16//! - 🚀 **Performance**: Efficient LRU caching
17//! - 🔒 **Thread Safe**: Concurrent access support
18//! - 📆 **2020-2030 Support**: Comprehensive holiday calendars
19//!
20//! ## Quick Start
21//!
22//! ```rust
23//! use trading_calendar::{TradingCalendar, Market};
24//!
25//! fn main() -> trading_calendar::Result<()> {
26//! let nyse = TradingCalendar::new(Market::NYSE)?;
27//!
28//! // Check if market is open
29//! if nyse.is_open_now()? {
30//! println!("NYSE is open for trading!");
31//! }
32//!
33//! // Get next market open
34//! let next_open = nyse.next_open()?;
35//! println!("NYSE opens: {}", next_open);
36//!
37//! // Check specific date
38//! let christmas = chrono::NaiveDate::from_ymd_opt(2025, 12, 25).unwrap();
39//! if !nyse.is_trading_day(christmas)? {
40//! println!("Market closed on Christmas");
41//! }
42//!
43//! Ok(())
44//! }
45//! ```
46//!
47//! ## Working with Different Markets
48//!
49//! ```rust
50//! use trading_calendar::{TradingCalendar, Market};
51//!
52//! // Create calendars for different markets
53//! let nyse = TradingCalendar::new(Market::NYSE)?;
54//! let lse = TradingCalendar::new(Market::LSE)?;
55//! let tse = TradingCalendar::new(Market::TSE)?;
56//!
57//! // Each market has its own timezone
58//! assert_eq!(nyse.timezone().name(), "America/New_York");
59//! assert_eq!(lse.timezone().name(), "Europe/London");
60//! assert_eq!(tse.timezone().name(), "Asia/Tokyo");
61//! # Ok::<(), trading_calendar::CalendarError>(())
62//! ```
63//!
64//! ## Handling Early Close Days
65//!
66//! ```rust
67//! use trading_calendar::{TradingCalendar, Market};
68//! use chrono::NaiveDate;
69//!
70//! let nyse = TradingCalendar::new(Market::NYSE)?;
71//! let christmas_eve = NaiveDate::from_ymd_opt(2025, 12, 24).unwrap();
72//!
73//! let hours = nyse.trading_hours(christmas_eve);
74//! if hours.is_early_close() {
75//! println!("Market closes early at {}", hours.market_close());
76//! }
77//! # Ok::<(), trading_calendar::CalendarError>(())
78//! ```
79//!
80//! ## Supported Markets
81//!
82//! | Market | Regular Hours (Local) | Pre-Market | After-Hours | Status |
83//! |--------|----------------------|------------|-------------|---------|
84//! | NYSE | 9:30 AM - 4:00 PM ET | 4:00 AM - 9:30 AM | 4:00 PM - 8:00 PM | ✅ Full Support |
85//! | NASDAQ | 9:30 AM - 4:00 PM ET | 4:00 AM - 9:30 AM | 4:00 PM - 8:00 PM | ✅ Full Support |
86//! | LSE | 8:00 AM - 4:30 PM GMT | - | - | ✅ Full Support |
87//! | TSE | 9:00 AM - 3:00 PM JST | - | - | ✅ Full Support |
88//! | TSX | 9:30 AM - 4:00 PM ET | - | - | ✅ Full Support |
89//!
90//! ## Thread Safety
91//!
92//! The `TradingCalendar` is thread-safe and can be shared across threads:
93//!
94//! ```rust
95//! use std::sync::Arc;
96//! use trading_calendar::{TradingCalendar, Market};
97//!
98//! fn main() -> trading_calendar::Result<()> {
99//! let calendar = Arc::new(TradingCalendar::new(Market::NYSE)?);
100//!
101//! // Share calendar across threads safely
102//! let cal_clone = Arc::clone(&calendar);
103//! std::thread::spawn(move || {
104//! let is_open = cal_clone.is_open_now().unwrap_or(false);
105//! });
106//!
107//! Ok(())
108//! }
109//! ```
110//!
111//! ## Error Handling
112//!
113//! The library uses proper error handling with `Result` types:
114//!
115//! ```rust
116//! use trading_calendar::{TradingCalendar, Market, CalendarError};
117//!
118//! fn main() -> trading_calendar::Result<()> {
119//! let calendar = TradingCalendar::new(Market::NYSE)?;
120//!
121//! // Check for unsupported years
122//! match calendar.is_trading_day(chrono::NaiveDate::from_ymd_opt(2019, 1, 1).unwrap()) {
123//! Ok(is_trading) => println!("Is trading day: {}", is_trading),
124//! Err(CalendarError::DateOutOfRange(date)) => println!("Date {} not supported", date),
125//! Err(e) => eprintln!("Error: {}", e),
126//! }
127//!
128//! Ok(())
129//! }
130//! ```
131//!
132//! ## Performance
133//!
134//! The library uses efficient caching to ensure optimal performance:
135//!
136//! - Holiday calculations are cached per year using LRU cache
137//! - Thread-safe concurrent access with proper eviction
138//! - Minimal allocations with optimized data structures
139//!
140//! ## License
141//!
142//! Licensed under either of:
143//!
144//! - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE))
145//! - MIT license ([LICENSE-MIT](LICENSE-MIT))
146//!
147//! at your option.
148
149#![deny(unsafe_code)]
150#![forbid(unsafe_code)]
151
152pub mod calendar;
153pub mod constants;
154pub mod error;
155pub mod markets;
156pub mod schedule;
157pub mod utils;
158
159// Re-export main types
160pub use calendar::TradingCalendar;
161pub use error::{CalendarError, Result};
162pub use markets::Market;
163pub use schedule::{Session, TradingHours};
164
165// Re-export chrono types for convenience
166pub use chrono::{DateTime, NaiveDate, NaiveTime, Utc};
167
168// Global constants
169/// Minimum supported year
170pub const MIN_YEAR: i32 = 2020;
171/// Maximum supported year
172pub const MAX_YEAR: i32 = 2030;
173
174/// Holiday information
175#[derive(Debug, Clone, PartialEq, Eq)]
176#[cfg_attr(
177 feature = "serialization",
178 derive(serde::Serialize, serde::Deserialize)
179)]
180pub struct Holiday {
181 /// The date of the holiday
182 pub date: NaiveDate,
183 /// The name of the holiday
184 pub name: String,
185 /// Whether the market is completely closed
186 pub market_closed: bool,
187 /// Early close time if applicable
188 pub early_close: Option<NaiveTime>,
189}
190
191impl Holiday {
192 /// Create a new holiday
193 pub fn new(date: NaiveDate, name: &str, market_closed: bool) -> Self {
194 Holiday {
195 date,
196 name: name.to_string(),
197 market_closed,
198 early_close: None,
199 }
200 }
201
202 /// Create a holiday with early close
203 pub fn with_early_close(date: NaiveDate, name: &str, early_close: NaiveTime) -> Self {
204 Holiday {
205 date,
206 name: name.to_string(),
207 market_closed: false,
208 early_close: Some(early_close),
209 }
210 }
211}