2011-03-29 172 views
4

在Erlang的时区中,从特定时间戳添加/减去单位的最佳方式是什么?Erlang:带时区算法的时间戳

从我发现的情况来看,stdlib的日历可以与本地或UTC时区一起工作,不再需要。此外,建议仅在UTC时区使用算术(原因很明显)。

例如,如果我需要在{{2011,3,24},{11,13,15}}中添加1个月,我们可以说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做(不是那么难看;需要一些解析,因为erlang和日期之间的协议将是文本的)
  2. 端口(对于utc,不是所需的时区)
  3. 端口驱动程序或erl_interface到C使用其标准库(根本不丑陋;但我没有找到准备使用解决方案a nd我没那么擅长C写一个)

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

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

在此先感谢。

P. S.也有类似的问题,但没有很好的答案有Time zone list issue

+0

不明白为什么你不能戳转换为UTC,然后向CET - 你只需要正确地做到这一点。我不知道Erlang的模块,但你可以尝试用[TZ数据库](http://www.twinsun.com/tz/tz-link.htm)自己实现它。 – hdima 2011-03-29 07:47:55

+0

问题在于“正确执行”。为了从一个时区转换到另一个时区或者对时区进行算术运算,人们需要知道两个时区的偏移量和DST特征,这就是问题本身。即使有了这些知识,实施也不是那么简单。 – trytrytry 2011-03-29 08:36:02

+0

I. e。 “从零开始实施一切”是可以实现的,但与解析linux date util result IMHO相比,利润并不包括复杂性。 – trytrytry 2011-03-29 08:38:28

回答

4

希望能帮助别人。

如果有人指出有什么可以改进的地方,请多多指教。提前致谢!

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.