(Background: reasonably experienced at programming in general but not a Rust expert, so anything I recommend you may have perfectly good reasons for not doing.)
You don't solve problem 2 - there's nothing here that takes a NaiveDateTime as input. Presumably the answer would be just to use .timestamp_nanos() as u64.
Chrono::Utc isn't used.
I would be inclined as a general stylistic point to replace this
let ns = unix_ns - (secs * 1_000_000_000);
with
let ns = unix_ns % 1_000_000_000;
I also wonder why you go to the trouble of allocating a temporary variable when you can just end this second function with
NaiveDateTime::from_timestamp(secs as i64, ns as u32)
You could go further and dump the other temporary variables too:
fn get_naive_date_time(unix_ns: u64) -> NaiveDateTime {
NaiveDateTime::from_timestamp(
(unix_ns / 1_000_000_000) as i64,
(unix_ns % 1_000_000_000) as u32,
)
}
Similarly the first function could become
fn get_unix_nanos(secs: u64, ns: u32) -> u64 {
NaiveDateTime::from_timestamp(secs as i64, ns).timestamp_nanos() as u64
}
I'm not saying that these are better than your way of doing things but they're a way I suspect I'd find easier to understand when I was coming back to look at the code six months later.
On that head, I'd probably call the functions something like "sns_to_unixnanos" and "unixnanos_to_naivedatetime", i.e. naming their inputs as well as their outputs.