Elixir中2个DateTime(小时)之间的差异

时间:2017-10-24 05:20:44

标签: datetime elixir

我的目标是找到现在和一些提供的date_time之间的小时差异。我试图这样做:

pry(1)> dt1
#DateTime<2017-10-24 05:12:46.000000Z>

pry(2)> Ecto.DateTime.to_erl(dt1)
** (FunctionClauseError) no function clause matching in Ecto.DateTime.to_erl/1    

    The following arguments were given to Ecto.DateTime.to_erl/1:

        # 1
        #DateTime<2017-10-24 05:12:46.000000Z>

    Attempted function clauses (showing 1 out of 1):

        def to_erl(%Ecto.DateTime{year: year, month: month, day: day, hour: hour, min: min, sec: sec})

    (ecto) lib/ecto/date_time.ex:608: Ecto.DateTime.to_erl/1
    # ............

如何解决这个问题? 还是有更好的方法来实现我的目标吗?

注意我不会使用timex并且不会使用它,也不会使用任何第三方库。只有内置于Elixir / Erlang / Phoenix的内容。

3 个答案:

答案 0 :(得分:8)

您可以使用DateTime.diff/3

iex> dt1 = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "AMT",
...>                 hour: 23, minute: 0, second: 7, microsecond: {0, 0},
...>                 utc_offset: -14400, std_offset: 0, time_zone: "America/Manaus"}
iex> dt2 = %DateTime{year: 2000, month: 2, day: 29, zone_abbr: "CET",
...>                 hour: 23, minute: 0, second: 7, microsecond: {0, 0},
...>                 utc_offset: 3600, std_offset: 0, time_zone: "Europe/Warsaw"}
iex> DateTime.diff(dt1, dt2)
18000
iex> DateTime.diff(dt2, dt1)
-18000

由于DateTime.diff/3返回秒数,您必须计算结果的小时数,如下所示:

result_in_hours = result_in_seconds/(60*60)

答案 1 :(得分:2)

在主题作者编辑他的问题之前添加了这个答案,并从范围中排除了外部库。但是,我没有删除它,因为我发现Timex非常有用,也许有人也会对使用它感兴趣(我与Timex创建者无关)

我强烈建议您使用Timex库。它非常适合使用不同格式和时区进行日期/时间计算。 因此,在您的情况下,您只需要轻松计算小时差:

Timex.diff(dt1, DateTime.utc_now, :hours)

您可以找到diff / 3 docs here

答案 2 :(得分:1)

完全忽略时区(要做到这一点,最简单的方法是将所有内容转换为UTC,然后转换为本地的),纯粹的Erlang方式就是沿着这些方向做点什么。

1> Event = {{2017,05,23},{13,11,23}}.
{{2017,5,23},{13,11,23}}
2> Now = calendar:local_time().
{{2017,10,24},{14,44,1}}
3> EventGSec = calendar:datetime_to_gregorian_seconds(Event).
63662764283
4> NowGSec = calendar:datetime_to_gregorian_seconds(Now).
63676075441
5> Elapsed = NowGSec - EventGSec.
13311158     
6> calendar:seconds_to_daystime(Elapsed).
{154,{1,32,38}}

确切的解决方案当然取决于您需要的分辨率。将其提取到返回形式{Days, {Hours, Minutes, Seconds}}的元组的函数中,可以得到:

-spec elapsed_time(calendar:datetime()) -> {Days, Time}
    when Days :: non_neg_integer(),
         Time :: calendar:time().

elapsed_time(Event) ->
    Now = calendar:local_time(),
    EventGSec = calendar:datetime_to_gregorian_seconds(Event),
    NowGSec = calendar:datetime_to_gregorian_seconds(Now),
    Elapsed = NowGSec - EventGSec,
    calendar:seconds_to_daystime(Elapsed).

(当然,以上内容可以由一行组成 - 但why would you ever do that?

我确信有一些wazoo Elixir库以完全不同的方式处理事情。和那些玩得开心。此答案仅解决底层本机库。 Erlang标准库中的大多数时间函数现在基于基于整数的时间单位(秒,毫秒或纳秒),而不是旧的erlang:now()时间戳形式(现在已弃用)。你应该编写上述函数版本的确切方式取决于你需要什么样的分辨率和原始输入的格式(例如,我自己处理的时间戳类型的Unix时代纳秒很常见 - 但是不适用于日期时间数据类型)。

请记住:时间计算是棘手的,微妙的,难以在边缘情况下正确。大多数语言的标准库实际上得到了相当多的TZ和时间差异问题 - 如果你的情况可以,那就不用担心了。在任何情况下,我建议至少略读标准文档的Time and Time Correction in Erlang页面 - 即使它不适用于您当前的情况,最终您可能会遇到微妙的计时问题。< / p>