Библиотека chrono для Rust: полное руководство по работе с датами и временем
1. Что это и зачем нужна
Библиотека chrono — это де-факто стандарт для работы с датами и временем в экосистеме Rust. Она предоставляет мощный, типобезопасный и эргономичный API для парсинга, форматирования, арифметики и манипуляций с временными метками. В отличие от стандартной библиотеки Rust, которая предлагает лишь базовые возможности через std::time (только для работы с длительностями и системным временем), chrono покрывает практически все потребности разработчика: от работы с часовыми поясами до сложных календарных расчётов.
Зачем она нужна? Если вы пишете приложение, которое должно обрабатывать даты рождения, логировать события с точностью до наносекунд, строить расписания или работать с международными часовыми поясами — без chrono не обойтись. Она поддерживает форматы ISO 8601, RFC 2822, RFC 3339, а также позволяет создавать собственные форматтеры. Благодаря строгой системе типов Rust, chrono помогает избежать ошибок, связанных с невалидными датами (например, 30 февраля) или путаницей между UTC и локальным временем.
Библиотека активно поддерживается сообществом, имеет отличную документацию и используется в тысячах проектов, включая такие известные, как tokio, actix-web и diesel. Версия 0.4.x является стабильной и рекомендуется для использования в production.
2. Установка
Для добавления chrono в ваш проект на Rust используйте менеджер пакетов Cargo. Выполните в терминале в корневой директории проекта:
cargo add chrono
Или вручную добавьте зависимость в файл Cargo.toml:
[dependencies]
chrono = "0.4"
После этого выполните cargo build, чтобы загрузить библиотеку и её зависимости. Для работы с часовыми поясами (например, для парсинга названий городов) может потребоваться включить фичу serde или clock:
[dependencies]
chrono = { version = "0.4", features = ["serde"] }
3. Быстрый старт — минимальный рабочий пример
Создайте файл main.rs и вставьте следующий код:
use chrono::{Local, Utc, DateTime, Duration};
fn main() {
// Текущее время в UTC
let utc_now: DateTime = Utc::now();
println!("Текущее время UTC: {}", utc_now);
// Текущее локальное время
let local_now: DateTime = Local::now();
println!("Текущее локальное время: {}", local_now);
// Форматирование даты
println!("Форматированная дата: {}", local_now.format("%Y-%m-%d %H:%M:%S"));
// Арифметика: добавляем 2 дня
let future = utc_now + Duration::days(2);
println!("Через 2 дня (UTC): {}", future);
}
Запустите программу командой cargo run. Вы увидите текущее время в UTC, локальное время, отформатированную строку и дату через два дня.
4. Основные методы/функции
Ниже приведены 8 ключевых методов и типов из библиотеки chrono. Для каждого дана сигнатура, описание и пример кода.
4.1. Utc::now()
Сигнатура: pub fn now() -> DateTime<Utc>
Описание: Возвращает текущий момент времени в часовом поясе UTC. Это наиболее безопасный способ получить временную метку, так как он не зависит от системных настроек часового пояса.
use chrono::Utc;
fn main() {
let now = Utc::now();
println!("Текущее время в UTC: {}", now);
// Вывод: 2025-03-15 14:30:00.123456 UTC
}
4.2. Local::now()
Сигнатура: pub fn now() -> DateTime<Local>
Описание: Возвращает текущий момент времени в локальном часовом поясе, который определяется операционной системой. Полезно для отображения времени пользователю.
use chrono::Local;
fn main() {
let local = Local::now();
println!("Локальное время: {}", local);
// Вывод: 2025-03-15 17:30:00.123456 +03:00
}
4.3. DateTime::format()
Сигнатура: pub fn format<'a>(self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>>
Описание: Форматирует дату/время в строку согласно спецификаторам strftime. Возвращает объект, который при выводе через {} или to_string() даёт строку.
use chrono::Utc;
fn main() {
let now = Utc::now();
// Стандартный формат ISO 8601
println!("ISO 8601: {}", now.format("%Y-%m-%dT%H:%M:%S%.3fZ"));
// Только дата
println!("Дата: {}", now.format("%d.%m.%Y"));
// День недели и месяц
println!("День недели: {}, месяц: {}", now.format("%A"), now.format("%B"));
}
4.4. NaiveDate::from_ymd_opt()
Сигнатура: pub fn from_ymd_opt(year: i32, month: u32, day: u32) -> Option<NaiveDate>
Описание: Создаёт дату без времени из года, месяца и дня. Возвращает Some, если дата валидна (например, 30 февраля вернёт None). Это безопасная альтернатива паникующему методу from_ymd.
use chrono::NaiveDate;
fn main() {
let valid_date = NaiveDate::from_ymd_opt(2025, 3, 15);
let invalid_date = NaiveDate::from_ymd_opt(2025, 2, 30);
match valid_date {
Some(d) => println!("Валидная дата: {}", d),
None => println!("Невалидная дата"),
}
match invalid_date {
Some(d) => println!("Валидная дата: {}", d),
None => println!("Невалидная дата"), // Выведется это
}
}
4.5. NaiveDateTime::parse_from_str()
Сигнатура: pub fn parse_from_str(s: &str, fmt: &str) -> ParseResult<NaiveDateTime>
Описание: Парсит строку в объект NaiveDateTime (дата+время без часового пояса) по заданному формату strftime. Возвращает Result, что позволяет обработать ошибки парсинга.
use chrono::NaiveDateTime;
fn main() {
let date_str = "2025-03-15 14:30:00";
let parsed = NaiveDateTime::parse_from_str(date_str, "%Y-%m-%d %H:%M:%S");
match parsed {
Ok(dt) => println!("Распарсено: {}", dt),
Err(e) => println!("Ошибка парсинга: {}", e),
}
// Парсинг с миллисекундами
let date_str_ms = "2025-03-15T14:30:00.123";
let parsed_ms = NaiveDateTime::parse_from_str(date_str_ms, "%Y-%m-%dT%H:%M:%S%.3f");
println!("С миллисекундами: {:?}", parsed_ms);
}
4.6. DateTime::with_timezone()
Сигнатура: pub fn with_timezone<Tz2: TimeZone>(self, tz: Tz2) -> DateTime<Tz2>
Описание: Конвертирует дату/время из одного часового пояса в другой. Например, из UTC в локальное время. Часовой пояс указывается как тип, а не строка.
use chrono::{Utc, Local, DateTime};
fn main() {
let utc_now = Utc::now();
println!("UTC: {}", utc_now);
// Конвертируем в локальное время
let local_now: DateTime = utc_now.with_timezone(&Local);
println!("Локальное: {}", local_now);
// Можно также конвертировать обратно
let back_to_utc: DateTime = local_now.with_timezone(&Utc);
println!("Обратно в UTC: {}", back_to_utc);
}
4.7. Duration::days(), Duration::hours(), Duration::minutes()
Сигнатура: pub fn days(days: i64) -> Duration (аналогично для hours, minutes, seconds, milliseconds, microseconds, nanoseconds)
Описание: Создаёт объект Duration (длительность) из указанного количества дней, часов и т.д. Длительности можно складывать, вычитать и применять к DateTime.
use chrono::{Utc, Duration};
fn main() {
let now = Utc::now();
// Создаём длительности
let two_days = Duration::days(2);
let three_hours = Duration::hours(3);
let thirty_min = Duration::minutes(30);
// Комбинируем длительности
let total = two_days + three_hours + thirty_min;
// Применяем к дате
let future = now + total;
println!("Сейчас: {}", now);
println!("Через 2 дня 3 часа 30 минут: {}", future);
// Вычитание
let past = now - Duration::weeks(1);
println!("Неделю назад: {}", past);
}
4.8. DateTime::timestamp_nanos_opt()
Сигнатура: pub fn timestamp_nanos_opt(&self) -> Option<i64>
Описание: Возвращает количество наносекунд с 1 января 1970 года (Unix epoch). Возвращает None, если значение выходит за пределы i64 (что маловероятно для практических дат). Полезно для интеграции с системами, использующими Unix timestamps.
use chrono::Utc;
fn main() {
let now = Utc::now();
// Наносекунды с эпохи
if let Some(nanos) = now.timestamp_nanos_opt() {
println!("Наносекунды с 1970-01-01: {}", nanos);
}
// Секунды с эпохи (более распространённый формат)
let seconds = now.timestamp();
println!("Секунды с 1970-01-01: {}", seconds);
// Создание DateTime из timestamp
let from_secs = chrono::DateTime::from_timestamp(seconds, 0).unwrap();
println!("Из timestamp: {}", from_secs);
}
5. Реальный практический пример (100-200 строк кода)
Ниже представлен пример приложения для ведения лога событий с временными метками, поддержкой часовых поясов и форматированным выводом. Код включает обработку ошибок и демонстрирует типичные паттерны использования chrono.
use chrono::{DateTime, Utc, Local, Duration, NaiveDateTime, Datelike, Timelike};
use std::collections::HashMap;
use std::io::{self, Write};
/// Структура для хранения события лога
#[derive(Debug)]
struct LogEvent {
timestamp: DateTime,
level: String,
message: String,
}
/// Простая система логирования
struct Logger {
events: Vec,
timezone: String, // "UTC" или "Local"
}
impl Logger {
fn new(timezone: &str) -> Self {
Logger {
events: Vec::new(),
timezone: timezone.to_string(),
}
}
/// Добавление события с текущим временем
fn log(&mut self, level: &str, message: &str) {
let event = LogEvent {
timestamp: Utc::now(),
level: level.to_string(),
message: message.to_string(),
};
self.events.push(event);
}
/// Вывод всех событий с форматированием
fn display_events(&self) {
if self.events.is_empty() {
println!("Нет событий в логе.");
return;
}
println!("{:-^80}", " ЛОГ СОБЫТИЙ ");
println!("{:<30} | {:<10} | {}", "Время", "Уровень", "Сообщение");
println!("{}", "-".repeat(80));
for event in &self.events {
let display_time = if self.timezone == "Local" {
// Конвертируем UTC в локальное время
let local: DateTime = event.timestamp.with_timezone(&Local);
local.format("%Y-%m-%d %H:%M:%S%.3f").to_string()
} else {
event.timestamp.format("%Y-%m-%d %H:%M:%S%.3f UTC").to_string()
};
println!("{:<30} | {:<10} | {}", display_time, event.level, event.message);
}
}
/// Фильтрация событий по уровню
fn filter_by_level(&self, level: &str) -> Vec<&LogEvent> {
self.events.iter().filter(|e| e.level == level).collect()
}
/// Подсчёт событий за последние N минут
fn count_recent(&self, minutes: i64) -> usize {
let cutoff = Utc::now() - Duration::minutes(minutes);
self.events.iter().filter(|e| e.timestamp > cutoff).count()
} Также в библиотеке
WebSocket библиотеки Kotlin для создания реального времени в веб-приложениях
Swagger/OpenAPI генерация для C++: обзор инструментов и библиотек
RxJava для Java: полное руководство с примерами
Конфигурация приложений Swift (iOS/macOS): лучшие практики и инструменты
Elasticsearch клиент для Go (Golang): полное руководство по работе с ES