3

I am trying to get a datetime which has only 3 digits in the sub-second part.
Using timex I get the following result:

iex(12)>   {:ok, date} = Timex.format(Timex.shift(Timex.local, days: 16), "{ISO:Extended}")
{:ok, "2017-04-22T09:00:44.403879+03:00"}

How can I get something like this:
{:ok, "2017-04-22T09:00:44.403+03:00"} ?

Cœur
  • 37,241
  • 25
  • 195
  • 267
fay
  • 2,086
  • 2
  • 14
  • 36
  • Did you try using the format string, e.g: `{:ok, date} = Timex.format(Timex.shift(Timex.local, days: 16), "%FT%T%:z", :strftime)` – Zubair Nabi Apr 06 '17 at 07:45
  • @ZubairNabi, that gives me a result with no milliseconds, i can't figure out the right format string to get 3 milliseconds digits – fay Apr 06 '17 at 07:58

2 Answers2

9

Since Elixir 1.6.0 there is now the truncate/2 function present on modules Time, DateTime and NativeDateTime for this.

Here is an example using DateTime.truncate/2

iex(1)> dt = Timex.now()
#DateTime<2018-02-16 19:03:51.430946Z>

iex(2)> dt2 = DateTime.truncate(dt, :millisecond)
#DateTime<2018-02-16 19:03:51.430Z>
Marius Butuc
  • 17,781
  • 22
  • 77
  • 111
Alexandre L Telles
  • 3,375
  • 29
  • 26
6

DateTime has a microsecond field which is a tuple containing the value and precision. If you change the precision to 3, you'll get 3 digits in the microsecond output. I couldn't find any function in Timex which does this, but you can modify the value manually:

iex(1)> dt = %{microsecond: {us, precision}} = Timex.now
#<DateTime(2017-04-06T08:26:24.041004Z Etc/UTC)>
iex(2)> precision
6
iex(3)> dt2 = %{dt | microsecond: {us, 3}}
#<DateTime(2017-04-06T08:26:24.041Z Etc/UTC)>
iex(4)> dt2 |> Timex.format!("{ISO:Extended}")
"2017-04-06T08:26:24.041+00:00"
Dogbert
  • 212,659
  • 41
  • 396
  • 397
  • This is a great idea, but you should zero-pad the microsecond value `us`. I ran into an issue where the formatted timestamp's millisecond portion was shifted left one digit. For example, if `dt` is `#DateTime<2015-11-02 03:08:57.071819Z>`, `dt.microsecond` is `{71819, 6}`. Doing `%{dt | microsecond: {71819, 3}} |> Timex.format("{ISO:Extended:Z}")` gives `"2015-11-02T03:08:57.718Z"`. Notice that the millisecond part is now 718 instead of 071. To fix this, zero pad the value: `%{dt | microsecond: {"071819", 3}} |> Timex.format!("{ISO:Extended:Z}")` "2015-11-02T03:08:57.071Z" – Gordon Zheng Sep 28 '17 at 16:05
  • 1
    @GordonZheng I think the right solution here is to divide `us` by `10^`, e.g. 6 -> 3 would be `%{dt | microsecond: {div(us, 1000), 3}} |> Timex.format("{ISO:Extended:Z}")`. Since this works, I'm guessing the behavior of my answer and your comment is a bug -- the value of `us` should be no more than `10^precision`. – Dogbert Sep 28 '17 at 16:22
  • Ah, that's a much better solution. Thanks @Dogbert! – Gordon Zheng Sep 28 '17 at 16:34