datetime - Erlang:带有时区算法的时间戳

标签 datetime math erlang timezone timestamp

在 Erlang 中相对于时区的特定时间戳添加/减去单位的最佳方法是什么?

据我所知,stdlib 的日历可以使用本地或 UTC 时区,仅此而已。此外,建议仅在 UTC 时区进行算术(原因很明显)。

例如,如果我需要将 1 个月添加到 {{2011,3,24},{11,13,15}},比如说 CET(中欧时间)和本地时间(系统)时区不是CET?这甚至与将此时间戳转换为 UTC、添加 31 * 24 * 60 * 60 秒并转换回 CET 不同(这将给出 {{2011,4,24},{12,13,15}} 而不是{{2011,4,24},{11,13,15}})。顺便说一下,如果 CET 不是 stdlib 的本地时区,我们甚至不能做这样的事情。

我在谷歌上找到的答案是:

  1. setenv 使本地时区 = 所需时区(这首先非常难看;然后它只允许将所需时区转换为 utc 并针对 utc 进行算术,而不是所需时区)
  2. open_port 到 linux date util 并在那里做算术(不是那么难看;相当慢;需要一些解析,因为 erlang 和 date 之间的协议(protocol)将是文本的)
  3. 使用其标准库将驱动程序或 erl_interface 移植到 C(一点也不难看;但我没有找到现成的解决方案,而且我不太擅长用 C 编写解决方案)

理想的解决方案是使用操作系统时区信息在 Erlang 中编写的内容,但我没有找到任何解决方案。

现在我坚持解决方案 2(open_port to date util)。有没有更好的办法?

提前致谢。

P。 S.有一个类似的问题,但没有好的答案Time zone list issue

最佳答案

port_helper.erl

-module(port_helper).
-export([get_stdout/1]).
get_stdout(Port) ->
    loop(Port, []).
loop(Port, DataAcc) ->
    receive
        {Port, {data, Data}} ->
            loop(Port, DataAcc ++ Data);
        {Port, eof} ->
            DataAcc
    end.

timestamp_with_time_zone.erl

-module(timestamp_with_time_zone).
-export([to_time_zone/2, to_universal_time/1, modify/2]).
to_time_zone({{{Year, Month, Day}, {Hour, Minute, Second}}, TimeZone}, OutputTimeZone) ->
    InputPattern = "~4.10.0B-~2.10.0B-~2.10.0B ~2.10.0B:~2.10.0B:~2.10.0B",
    InputDeep = io_lib:format(InputPattern, [Year, Month, Day, Hour, Minute, Second]),
    Input = lists:flatten(InputDeep),
    {external_date(Input, TimeZone, OutputTimeZone), OutputTimeZone}.
to_universal_time({{{Year, Month, Day}, {Hour, Minute, Second}}, TimeZone}) ->
    {Timestamp, "UTC"} = to_time_zone({{{Year, Month, Day}, {Hour, Minute, Second}}, TimeZone}, "UTC"),
    Timestamp.
modify({{{Year, Month, Day}, {Hour, Minute, Second}}, TimeZone}, {Times, Unit}) ->
    if
        Times > 0 ->
            TimesModifier = "";
        Times < 0 ->
            TimesModifier = " ago"
    end,
    InputPattern = "~4.10.0B-~2.10.0B-~2.10.0B ~2.10.0B:~2.10.0B:~2.10.0B ~.10B ~s~s",
    InputDeep = io_lib:format(InputPattern, [Year, Month, Day, Hour, Minute, Second, abs(Times), Unit, TimesModifier]),
    Input = lists:flatten(InputDeep),
    external_date(Input, TimeZone, TimeZone).

external_date(Input, InputTimeZone, OutputTimeZone) ->
    CmdPattern = "date --date 'TZ=\"~s\" ~s' +%Y%m%d%H%M%S",
    CmdDeep = io_lib:format(CmdPattern, [InputTimeZone, Input]),
    Cmd = lists:flatten(CmdDeep),
    Port = open_port({spawn, Cmd}, [{env, [{"TZ", OutputTimeZone}]}, eof, stderr_to_stdout]),
    ResultString = port_helper:get_stdout(Port),
    case io_lib:fread("~4d~2d~2d~2d~2d~2d", ResultString) of
        {ok, [YearNew, MonthNew, DayNew, HourNew, MinuteNew, SecondNew], _LeftOverChars} ->
            {{YearNew, MonthNew, DayNew}, {HourNew, MinuteNew, SecondNew}}
    end.

关于datetime - Erlang:带有时区算法的时间戳,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5468791/

相关文章:

unicode - 如何将小端 UTF-16 unicode 转换为 erlang 字符串

sockets - Erlang同时连接1M客户端

erlang - 使用 ets :foldl as a poor man's forEach on every record

sql - 查询返回具有匹配值的连续行

sql - Postgresql中行之间的时间戳差异

math - 如何测试随机性(例如 - 洗牌)

math - 超平面之间的距离

sql - 按日期分区一段时间 SQL BigQuery

javascript - 如何在javascript中获取两个日期时间之间的时差?

java - 计算一系列值的斜率