# DateTime Curiosity: Dates.epochms2datetime

**URL:** https://discourse.julialang.org/t/datetime-curiosity-dates-epochms2datetime/104205
**Category:** New to Julia
**Tags:** question, dates, time, datetime
**Created:** [September 25, 2023, 1:14am UTC](https://discourse.julialang.org/t/datetime-curiosity-dates-epochms2datetime/104205 "2023-09-25T01:14:00Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![g-gundam](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/g-gundam/32/47593_2.png) [@g-gundam](https://discourse.julialang.org/u/g-gundam)
#### Post date: [September 25, 2023, 1:14am UTC](https://discourse.julialang.org/t/datetime-curiosity-dates-epochms2datetime/104205/1 "2023-09-25T01:14:00Z")

</div>

I’m new to Julia but not new to programming, and I’m wondering why [`Dates.epochms2datetime`](https://docs.julialang.org/en/v1/stdlib/Dates/#Dates.epochms2datetime) returned a negative value.

```julia
using Dates

unix_0 = unix2datetime(0)
# 1970-01-01T00:00:00
# So far, so good.

Dates.value(unix_0)
# 62135683200000
# OK.

epoch_0 = Dates.epochms2datetime(0)
# 0000-01-01T00:00:00
# Alright.

Dates.value(epoch_0)
# -31536000000
# Whoa, why is this negative?

```

## Why is `Dates.value(epoch_0)` negative?

I can work with this, but I’m still wondering why `Dates.value(epoch_0)` returned a negative value. If anyone could enlighten me, I would appreciate it.

## Context

I have a lot of timestamps that are stored as milliseconds since the Unix epoch, and I wanted to create DateTime instances out of them. That led to experimenting with `epochms2datetime` and being surprised by its behavior.

```julia
epoch_unix_fail = Dates.epochms2datetime(Dates.value(unix_0))
# 1969-01-01T00:00:00
# This is 1 year before the real Unix epoch.

epoch_unix = Dates.epochms2datetime(
	Dates.value(unix_0) + abs(Dates.value(epoch_0))
)
# 1970-01-01T00:00:00
# It became correct after I added abs(Dates.value(epoch_0)) .

epoch_unix == unix_0
# true

```

---

<div class="post-metadata">

### Author: ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)
#### Post date: [September 25, 2023, 5:03am UTC](https://discourse.julialang.org/t/datetime-curiosity-dates-epochms2datetime/104205/2 "2023-09-25T05:03:49Z")

</div>

Because it is.

fyi, often there is a better (safer, more robust) way than calling Dates.value directly – Dates.value exists for internal support and its use requires very careful implementation, it throws away units and with dates, times that can be trouble.

```julia
using Dates
# the zero for the unix calendarclock
# remember unix timepoints are floating point values
# for timestamping, integer quantities are much preferred
unixtime0 = unix2datetime(0.0) # 1970-01-01T00:00:00

# Dates.epochms2datetime is an internal (non-exported) function
# again, often there is a better (safer, more robust) way than using internals
#=
   Dates.epochms2datetime
      take a number, one that must be a raw count of milliseconds,
      implicitly, this number is an offset from this time point rata2datetime(1),
=#
ratatime0 = rata2datetime(0)
ratatime1 = rata2datetime(1)
canonicalize(ratatime1 - ratatime0) # 1 day
# rata values are calendric, sure the underlying implementation uses millis
# and that is another good reason not to use Dates.value.
#=
   meanwhile the unixtime0 offset is a floating point value, sure
   the integer part counts seconds and the fractional part counts milliseconds.
   however it does not count them as (second::Int, millisecond::Int),
   so with expansive ranges or unexpected corner cases,
   mixing integer valued temporal offsets with float value offsets
   is strongly disrecommended.
=#

```

For accurate timestamping and other nice capabilities,  
I wrote [NanoDates.jl](https://github.com/JuliaTime/NanoDates.jl)

---

<div class="post-metadata">

### Author: ![g-gundam](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/g-gundam/32/47593_2.png) [@g-gundam](https://discourse.julialang.org/u/g-gundam)
#### Post date: [September 25, 2023, 5:45am UTC](https://discourse.julialang.org/t/datetime-curiosity-dates-epochms2datetime/104205/3 "2023-09-25T05:45:34Z")

</div>

@JeffreySarnoff - Thank you for your detailed answer.

## unix2datetime

- I didn’t realize it took a floating point parameter.
- I assumed it took integer seconds.
- I would prefer to stay integer-only if possible like you said.

## Dates.epochms2datetime

- Although it wasn’t exported, it showed up in the documentation, so I thought it was fair game. (I wasn’t trying to be hacky.)
- At first glance, it looked like what I needed.
  - I wanted to turn milliseconds into DateTimes.

- It surprised me though.

## NanoDates.jl

- I looked at your library, and I think I understand some of your motivations for writing it.
  - It looks like you correctly unified Date and Time without losing any precision on the time side.

- The [Regulatory Note](https://juliatime.github.io/NanoDates.jl/dev/appropriate/advantages/) was interesting.
  - I’m not a high frequency trader, but it’s good to know about those regulations.

- Is there a way to create a NanoDate from milliseconds?
  - I didn’t see it in the docs, but I may have missed it.

---

<div class="post-metadata">

### Author: ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)
#### Post date: [September 25, 2023, 6:18am UTC](https://discourse.julialang.org/t/datetime-curiosity-dates-epochms2datetime/104205/4 "2023-09-25T06:18:48Z")

</div>

you’ll like this

```julia
using Dates, NanoDates

const Time0 = NanoDate(DateTime(1970,1,1)) # UNIX 

millis2nanodate(millis::Millisecond) = Time0 + millis

millis_today = Millisecond( now() - Time0)
millis_lastyear = Millisecond( (now() - Year(1)) - Time0)

today = millis2nanodate(millis_today)
lastyear = millis2nanodate(millis_lastyear)

typeof(today), typeof(lastyear)

```

---

<div class="post-metadata">

### Author: ![g-gundam](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/g-gundam/32/47593_2.png) [@g-gundam](https://discourse.julialang.org/u/g-gundam)
#### Post date: [September 25, 2023, 6:20am UTC](https://discourse.julialang.org/t/datetime-curiosity-dates-epochms2datetime/104205/5 "2023-09-25T06:20:00Z")

</div>

That’s exactly what I needed. I’ll give NanoDates.jl a spin.

@JeffreySarnoff - I wonder if that’s a useful enough function to include in your library. You were able to easily implement `millis2nanodate`, because you’re familiar with your own library, but it would have took me a little while to figure out how to do it. Before you posted your code, I had actually spent some time writing a similar `unixms2datetime`.

```julia
_unix_epoch_ms =
    Dates.value(unix2datetime(0)) +
    abs(Dates.value(Dates.epochms2datetime(0)))

function _unixms2datetime(ms)
    Dates.epochms2datetime(_unix_epoch_ms + ms)
end

```

The function may be simple, but arriving at the right values to add wasn’t obvious and took some time. Including `millis2nanodate` may save future users of your library a lot of time too.

My use case is dealing with timestamps coming from crypto exchanges, and they often come in milliseconds since the Unix epoch. It’s actually the only kind of timestamp I’ve encountered so far.

---

<div class="post-metadata">

### Author: ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)
#### Post date: [September 25, 2023, 8:45am UTC](https://discourse.julialang.org/t/datetime-curiosity-dates-epochms2datetime/104205/6 "2023-09-25T08:45:08Z")

</div>

Thank you for the suggestion. I am adding it to the docs as an example.  
And may export it once I have good tests for the function.
