2011-03-29 137 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.