Avatar ·

Как получить текущее время в миллисекундах в Rust?

Привет, коллеги! Я только начал осваивать Rust и столкнулся с задачей, которая кажется простой, но вызывает у меня ступор. Мне нужно замерить время выполнения небольшого участка кода или просто получить текущий timestamp в миллисекундах (unix-время с точностью до миллисекунд). В других языках (например, в JavaScript — `Date.now()`, в Python — `time.time() * 1000`) это делается одной строкой, но в Rust я запутался в типах и крейтах. Я написал такой код: ```rust use std::time::SystemTime; fn main() { let now = SystemTime::now(); // Как из now получить u64 или f64 в миллисекундах? println!("Текущее время: {:?}", now); } ``` Но `println!` выводит что-то вроде `SystemTime { tv_sec: 1234567, tv_nsec: 890123456 }`, а мне нужно просто число, например `1712345678890`. Я пробовал читать документацию по `SystemTime::duration_since` и `UNIX_EPOCH`, но там куча ошибок с типами `Result` и `Duration`. Плюс я не уверен, какой крейт лучше использовать: стандартная библиотека или `chrono`? Подскажите, как правильно и идиоматично получить текущее время в миллисекундах в Rust, чтобы потом использовать для логов или замеров производительности. Заранее спасибо! Я новичок, так что объясните, пожалуйста, как для дауна. 😅 <<>>

Используем стандартную библиотеку: самый простой и надёжный способ

В Rust получение текущего времени в миллисекундах — не такая тривиальная задача, как в интерпретируемых языках, но после понимания пары концепций всё становится просто. Основная сложность — работа с типами Result и Duration. Давай разберёмся по шагам.

Самый прямолинейный способ — использовать std::time::SystemTime и UNIX_EPOCH. Вот готовый код:

use std::time::{SystemTime, UNIX_EPOCH};

fn main() {
    let now = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("System time is before UNIX epoch!");
    
    let millis = now.as_millis(); // u128
    println!("Текущее время в миллисекундах: {}", millis);
}

Объяснение по шагам:

  • SystemTime::now() — получаем текущий момент времени (структура SystemTime).
  • .duration_since(UNIX_EPOCH) — вычисляем разницу между текущим временем и началом эпохи Unix (1 января 1970 года). Возвращает Result<Duration, SystemTimeError>.
  • .expect(...) — если время оказалось раньше 1970 года (что маловероятно), программа запаникует с понятным сообщением. Для продакшена лучше использовать unwrap() или обработку ошибок.
  • as_millis() — метод у Duration, который возвращает общее количество миллисекунд как u128 (от 0 до 2^128-1, что хватит на миллиарды лет).

Если тебе нужно именно u64 (например, для совместимости с API), можно преобразовать:

let millis_u64 = now.as_millis() as u64;

Или если нужны секунды с дробной частью (как в JavaScript):

let secs_f64 = now.as_secs_f64();
let millis_f64 = secs_f64 * 1000.0;

Альтернатива: крейт chrono (для сложных манипуляций с датами)

Если тебе нужно не только текущее время, но и форматирование, часовые пояса, парсинг — лучше использовать chrono. Добавь в Cargo.toml:

[dependencies]
chrono = "0.4"

Код для получения миллисекунд:

use chrono::Utc;

fn main() {
    let now = Utc::now();
    let millis = now.timestamp_millis(); // i64 (может быть отрицательным для дат до 1970 года)
    println!("Текущее время: {}", millis);
}

Обрати внимание: timestamp_millis() возвращает i64, а не u64, потому что chrono поддерживает даты с 0001 по 9999 год. Для современных дат это не проблема.

Какой способ выбрать?

  • Только замер времени или timestamp — используй стандартную библиотеку (быстрее, без зависимостей).
  • Работа с датами, форматирование, часовые пояса — бери chrono.
  • Замер производительности — лучше использовать std::time::Instant, он монотонный и не зависит от системных изменений времени.

Пример с Instant для замера:

use std::time::Instant;

let start = Instant::now();
// ... твой код ...
let duration = start.elapsed();
println!("Время выполнения: {} ms", duration.as_millis());

Надеюсь, теперь стало понятнее! Главное — запомнить цепочку: SystemTime::now() → duration_since(UNIX_EPOCH) → as_millis(). Удачи в изучении Rust!

<<>>

Бонус: получаем миллисекунды без unwrap и с обработкой ошибок

Если ты пишешь библиотеку или критичный к ошибкам код, лучше избегать expect и unwrap. Вот вариант с корректной обработкой:

use std::time::{SystemTime, UNIX_EPOCH};

fn get_millis() -> Option<u64> {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .ok()
        .map(|d| d.as_millis() as u64)
}

fn main() {
    match get_millis() {
        Some(millis) => println!("{}", millis),
        None => eprintln!("Ошибка: системное время раньше 1970 года"),
    }
}

Метод .ok() преобразует Result в Option, а .map() применяет функцию к значению внутри Some. Такой код безопаснее и не вызовет панику.

Ещё один лайфхак: если тебе нужно время в миллисекундах для логирования, часто проще использовать крейт log с форматтером, который сам добавляет timestamp. Но для ручного контроля — приведённый выше способ идеален.