std::time: SystemTime, Duration
Библиотека chrono
: DateTime, таймзоны
Замер времени выполнения
Форматирование и парсинг дат
Примеры: таймер, логирование с метками времени
Упражнение: Написать таймер с логированием
Практические советы
Проверочное задание
Добро пожаловать в раздел 14 нашего курса по Rust! Сегодня мы подробно разберём всё, что связано с работой со временем и датами в этом языке программирования. Мы начнём с базовых инструментов стандартной библиотеки std::time
, затем углубимся в мощную стороннюю библиотеку chrono
, научимся измерять время выполнения кода, форматировать и парсить даты, а также рассмотрим практические примеры и упражнение. Эта лекция будет максимально самодостаточной, так что даже если вы новичок, вы сможете всё понять и применить на практике.
std::time
Стандартная библиотека Rust предоставляет модуль std::time
для работы с системным временем и длительностями. Это базовый инструмент, который полезен для простых задач, таких как замер времени выполнения или работа с временными интервалами.
SystemTime
SystemTime
— это тип, представляющий момент времени относительно системных часов. Обычно он отсчитывается от так называемой "эпохи UNIX" (1 января 1970 года, 00:00:00 UTC).
Пример использования:
use std::time::{SystemTime, UNIX_EPOCH};
fn main() {
// Получаем текущее системное время
let now = SystemTime::now();
// Вычисляем, сколько времени прошло с эпохи UNIX
match now.duration_since(UNIX_EPOCH) {
Ok(duration) => println!("Секунд с эпохи UNIX: {}", duration.as_secs()),
Err(e) => println!("Ошибка: {:?}", e),
}
}
Объяснение:
SystemTime::now()
возвращает текущий момент времени.duration_since
вычисляет разницу между двумя моментами времени (now
и UNIX_EPOCH
).Result
, потому что системное время может быть "в прошлом" относительно точки стоимости (например, если часы сбились).Важно: SystemTime
зависит от системных часов, которые могут быть неточными или изменяться (например, при синхронизации времени). Для задач, где нужна монотонность (например, замер времени выполнения), лучше использовать Instant
(о нём ниже).
Duration
Duration
— это структура для представления промежутков времени. Она измеряется в секундах и наносекундах.
Пример:
use std::time::Duration;
fn main() {
let five_seconds = Duration::from_secs(5); // 5 секунд
let millis = Duration::from_millis(250); // 250 миллисекунд
let total = five_seconds + millis;
println!("Общая длительность: {} секунд", total.as_secs_f64());
}
Объяснение:
Duration::from_secs
создаёт длительность в секундах.Duration::from_millis
— в миллисекундах.as_secs_f64
конвертирует длительность в секунды с плавающей точкой (например, 5.25 с).Совет: Используйте Duration
для любых операций с временными интервалами — это надёжный и безопасный способ.
Instant
для замера времениДля измерения времени выполнения кода лучше использовать Instant
, так как он монотонен (не зависит от скачков системных часов).
Пример:
use std::time::Instant;
fn main() {
let start = Instant::now();
// Имитация долгой работы
for _ in 0..1_000_000 {
// Пустой цикл
// Просто перебирает числа от 0 до 999_999 (всего 1 миллион итераций)
// но поскольку переменная _ не используется, это пример пустого цикла
// для тестирования производительности или ожидания.
// Без тела цикла {} он ничего не делает, кроме итераций.
}
let duration = start.elapsed();
println!("Время выполнения: {:?}", duration);
}
Объяснение:
for _ in
— это конструкция цикла for
, где _
используется как переменная, которая не будет задействована в теле цикла (подчёркивание означает, что значение итератора игнорируется).0..1_000_000
— это диапазон (range) от 0
(включительно) до 1_000_000
(не включительно). Число 1_000_000
записано с подчёркиваниями для удобства чтения, что является валидным синтаксисом в Rust; подчёркивания игнорируются компилятором и эквивалентны записи 1000000
.Instant::now()
фиксирует текущий момент.elapsed()
возвращает Duration
— сколько прошло времени с момента создания Instant
.chrono
Стандартная библиотека Rust покрывает только базовые потребности. Для более сложных задач (даты, таймзоны, форматирование) используется популярная библиотека chrono
. Добавьте её в ваш проект через Cargo.toml
:
[dependencies]
chrono = "0.4"
DateTime
и основные операцииDateTime
из chrono
позволяет работать с датами и временем с учётом часовых поясов.
Пример:
use chrono::{Utc, DateTime, TimeZone};
fn main() {
// Текущее время в UTC
let now = Utc::now();
println!("Текущее время в UTC: {}", now);
// Конкретная дата и время
let specific_date = Utc.with_ymd_and_hms(2025, 3, 26, 12, 0, 0).unwrap();
println!("Конкретная дата: {}", specific_date);
// Разница между датами
let difference = now - specific_date;
println!("Разница: {:?}", difference);
}
Объяснение:
Utc::now()
возвращает текущую дату и время в UTC.with_ymd_and_hms
создаёт конкретный момент времени (год, месяц, день, часы, минуты, секунды).DateTime
возвращает Duration
.chrono
поддерживает работу с таймзонами через тип DateTime<Tz>
, где Tz
— это часовой пояс.
Пример:
use chrono::{Utc, FixedOffset};
fn main() {
// Время в UTC
let utc_time = Utc::now();
// Смещение +03:00 (например, Москва)
let offset = FixedOffset::east_opt(3 * 3600).unwrap(); // 3 часа в секундах
let local_time = utc_time.with_timezone(&offset);
println!("Время в +03:00: {}", local_time);
}
Объяснение:
FixedOffset::east_opt
создаёт смещение в секундах (восточное направление).with_timezone
конвертирует время в другой часовой пояс.Для работы с именованными таймзонами (например, "Europe/Moscow") нужно подключить chrono-tz
:
[dependencies]
chrono-tz = "0.8"
Пример с chrono-tz
:
use chrono::{Utc};
use chrono_tz::Europe::Moscow;
fn main() {
let utc_time = Utc::now();
let moscow_time = utc_time.with_timezone(&Moscow);
println!("Время в Москве: {}", moscow_time);
}
Мы уже видели Instant
для замера времени. Давайте создадим более практичный пример — функцию, которая измеряет время выполнения другой функции.
Пример:
use std::time::Instant;
fn measure_time<F>(task: F) -> Duration
where F: FnOnce() {
let start = Instant::now();
task();
start.elapsed()
}
fn main() {
let duration = measure_time(|| {
for _ in 0..1_000_000 {
// Долгая операция
}
});
println!("Задача выполнена за: {:?}", duration);
}
Объяснение:
fn measure_time<F>(task: F) -> Duration
— объявление функции с именем measure_time
, которая принимает параметр task
типа F
и возвращает значение типа Duration
(длительность времени из стандартной библиотеки Rust).where F: FnOnce()
— ограничение типа F
, указывающее, что task
должен быть замыканием (closure), реализующим трейт FnOnce
. FnOnce()
означает, что замыкание может быть вызвано один раз и не принимает аргументов (пустые скобки ()
).let start = Instant::now();
— создание переменной start
, которая фиксирует текущий момент времени с помощью Instant::now()
из модуля std::time
. Instant
— это тип для измерения времени с высокой точностью.task();
— вызов переданного замыкания task
. Поскольку оно типа FnOnce
, оно выполняется ровно один раз.start.elapsed()
— вычисление времени, прошедшего с момента start
до текущего момента. Метод elapsed()
возвращает Duration
, представляющий разницу во времени.Итого: эта функция принимает замыкание (например, кусок кода, который нужно измерить), засекает время перед его выполнением, выполняет его, а затем возвращает, сколько времени заняло выполнение.
chrono
позволяет форматировать даты в строки и парсить строки в даты.
Пример:
use chrono::Utc;
fn main() {
let now = Utc::now();
// Простой формат
println!("Простой: {}", now.format("%Y-%m-%d %H:%M:%S"));
// Пользовательский формат
println!("Кастом: {}", now.format("Дата: %d %B %Y, %A, %H:%M"));
}
Объяснение:
%Y
— год, %m
— месяц, %d
— день, %H
— часы, %M
— минуты, %S
— секунды.%B
— полное название месяца, %A
— день недели.Полный список спецификаторов: смотрите документацию chrono
(strftime).
Пример:
use chrono::{DateTime, Utc};
fn main() {
let date_str = "2025-03-26 12:00:00";
let parsed_date = DateTime::parse_from_str(date_str, "%Y-%m-%d %H:%M:%S").unwrap();
println!("Спарсенная дата: {}", parsed_date);
}
Объяснение:
parse_from_str
преобразует строку в DateTime
.Создадим простой таймер с обратным отсчётом:
use std::time::{Duration, Instant};
use std::thread::sleep;
fn countdown(seconds: u64) {
let duration = Duration::from_secs(seconds);
let start = Instant::now();
while start.elapsed() < duration {
let remaining = duration - start.elapsed();
println!("Осталось: {} секунд", remaining.as_secs());
sleep(Duration::from_secs(1));
}
println!("Таймер завершён!");
}
fn main() {
countdown(5);
}
В Rust функция sleep
не принимает число напрямую. Она принимает аргумент типа std::time::Duration
, который представляет промежуток времени. Чтобы указать время в секундах, миллисекундах или других единицах, нужно использовать методы создания Duration
, такие как Duration::from_secs
или Duration::from_millis
.
Пример логирования с использованием chrono
:
use chrono::Utc;
fn log(message: &str) {
let timestamp = Utc::now().format("%Y-%m-%d %H:%M:%S");
println!("[{}] {}", timestamp, message);
}
fn main() {
log("Программа запущена");
log("Выполняется операция...");
log("Программа завершена");
}
Напишите программу, которая:
use std::time::{Duration, Instant};
use std::thread::sleep;
use chrono::Utc;
fn log(message: &str) {
let timestamp = Utc::now().format("%Y-%m-%d %H:%M:%S");
println!("[{}] {}", timestamp, message);
}
fn countdown_with_logging(seconds: u64) {
log("Таймер запущен");
let duration = Duration::from_secs(seconds);
let start = Instant::now();
while start.elapsed() < duration {
let remaining = duration - start.elapsed();
log(&format!("Осталось {} секунд", remaining.as_secs()));
sleep(Duration::from_secs(1));
}
log("Таймер завершён!");
}
fn main() {
countdown_with_logging(10);
}
Разбор:
log
добавляет метку времени к каждому сообщению.countdown_with_logging
объединяет логику таймера и логирование.format!
для создания строки с оставшимся временем.Проверка:
Запустите программу и убедитесь, что она выводит сообщения с метками времени каждую секунду в течение 10 секунд.
Instant
для замера времени выполнения.SystemTime
для работы с реальным временем.chrono
для дат, таймзон и форматирования.Result
при работе с SystemTime
и парсингом дат.Instant::now()
или Utc::now()
в горячих циклах — это может быть затратно.chrono
с дополнительными библиотеками, такими как chrono-locale
.Подсказка: Используйте std::env::args
для аргументов и chrono
для парсинга и вычислений.
На этом лекция завершена! Вы теперь знаете, как работать со временем и датами в Rust на уровне от новичка до эксперта. Практикуйтесь и экспериментируйте!