Paste number 49404: SMPP time decoding

Paste number 49404: SMPP time decoding
Pasted by: archaelus
When:1 year, 8 months ago
Share:Tweet this! | http://paste.lisp.org/+124C
Channel:#erlang
Paste contents:
Raw Source | XML | Display As
pdu_timing(Pdu) ->
    RawDelivery = extract_delivery(Pdu),
    RawExpiry = extract_expiry(Pdu),
    smpp_timing(RawDelivery, RawExpiry).

smpp_timing(undefined, undefined) ->
    {undefined, undefined};
smpp_timing(undefined, RawExpiry) ->
    {undefined, smpptime_to_universal(RawExpiry)};
smpp_timing(RawDelivery, undefined) ->
    {smpptime_to_universal(RawDelivery), undefined};
smpp_timing(RawDelivery, RawExpiry) ->
    DeliveryDT = smpptime_to_universal(RawDelivery),
    {DeliveryDT,
     adjust_expiry(DeliveryDT,
                   smpptime_to_universal(RawExpiry))}.

adjust_expiry(BaseDT, ExpiryDT) ->
    adjust_expiry(BaseDT, ExpiryDT, erlang:universaltime()).

adjust_expiry(BaseDT, ExpiryDT, NowDT) ->
    datetime_add(BaseDT, datetime_diff(NowDT, ExpiryDT)).

extract_expiry(Pdu) ->
    case dict:find(validity_period, Pdu) of
        {ok, Time} when Time /= [] ->
            Time;
        _ ->
            undefined
    end.

extract_delivery(Pdu) ->
    case dict:find(schedule_delivery_time, Pdu) of
        {ok, Time} when Time /= [] ->
            Time;
        _ ->
            undefined
    end.

smpptime_to_universal(Raw) ->
    smpptime_to_universal(Raw, erlang:universaltime()).

smpptime_to_universal([Yh,Yl, Mh,Ml, Dh, Dl,
                       Hh, Hl, Mih, Mil, Sh, Sl, _St,
                       _Nh, _Nl, $R], NowDT) ->
    [_Year, _Month, Day, Hour, Min, Sec] =
        smpptime_parse([Yh,Yl],[Mh,Ml],[Dh,Dl],
                       [Hh, Hl],[Mih, Mil], [Sh,Sl]),
    % How many days are there in a month? or a year?
    Secs = (Day * 24 * 60 * 60) + (Hour * 60 * 60) + (Min * 60) + Sec,
    datetime_add(NowDT, Secs);
smpptime_to_universal([Yh,Yl, Mh,Ml, Dh, Dl,
                       Hh, Hl, Mih, Mil, Sh, Sl, St,
                       Nh, Nl, P], _NowDT) ->
    Time = smpptime_parse([Yh,Yl],[Mh,Ml],[Dh,Dl],
                          [Hh, Hl],[Mih, Mil], [Sh,Sl], St),
    DT = list_to_datetime(Time),
    smpptime_adjust(DT, [P,Nh,Nl]).

smpptime_parse(Year, Month, Day, Hour, Min, Sec) ->
    [list_to_integer(Year),
     list_to_integer(Month),
     list_to_integer(Day),
     list_to_integer(Hour),
     list_to_integer(Min),
     list_to_integer(Sec)].

smpptime_parse(Year, Month, Day, Hour, Min, Sec, Tenths) ->
    [list_to_integer(Year),
     list_to_integer(Month),
     list_to_integer(Day),
     list_to_integer(Hour),
     list_to_integer(Min),
     round(list_to_float(Sec ++ [$.,Tenths]))].

list_to_datetime([Year, Month, Day, Hour, Min, Sec]) ->
    {{smpptime_year_normalise(Year), Month, Day}, {Hour, Min, Sec}}.

smpptime_adjust(DT, [_,$0,$0]) ->
    DT;
smpptime_adjust(DT, TZoffset) ->
    TZo = list_to_integer(TZoffset),
    TZoSecs = 60 * 15 * TZo,
    AdjSecs = calendar:datetime_to_gregorian_seconds(DT) - TZoSecs,
    calendar:gregorian_seconds_to_datetime(AdjSecs).


datetime_add(T, Interval) ->
    S = calendar:datetime_to_gregorian_seconds(T),
    calendar:gregorian_seconds_to_datetime(S + Interval).

datetime_diff(T1, T2) ->
    calendar:datetime_to_gregorian_seconds(T2) -
        calendar:datetime_to_gregorian_seconds(T1).

universal_to_smpptime({{Year,Month,Day},{Hour,Min,Sec}}) ->
    Fmt = "~2.10.0B~2.10.0B~2.10.0B~2.10.0B~2.10.0B~2.10.0B000+",
    lists:flatten(io_lib:format(Fmt,
                                [Year, Month, Day, Hour, Min, Sec])).

smpptime_year_normalise_test() ->
    ?assert(smpptime_year_normalise(0) == 2000),
    ?assert(smpptime_year_normalise(70) == 1970),
    ?assert(smpptime_year_normalise(1970) == 1970),
    ?assert(smpptime_year_normalise(2000) == 2000),
    ?assert(lists:all(fun (N) -> 
                              smpptime_year_normalise(N) >= 1970
                      end,
                      lists:seq(0,170))).
    

smpptime_relative_test() ->
    Now = {{2007,05,17},{15,58,00}},
    ?assert(smpptime_to_universal("000000000000000R",
                                  Now) ==
            {{2007,05,17},{15,58,00}}),
    ?assert(smpptime_to_universal("000000000200000R",
                                  Now) ==
            {{2007,05,17},{16,00,00}}),
    ?assert(smpptime_to_universal("000000010301000R",
                                  Now) ==
            {{2007,05,17},{17,01,01}}),
    ?assert(smpptime_to_universal("000019010301000R",
                                  Now) ==
            {{2007,06,05},{17,01,01}}),
    ?assert(smpptime_to_universal("000000000001000R",
                                  Now) ==
            {{2007,05,17},{15,58,01}}).

smpptime_absolute_test() ->
    ?assertMatch({{2007,05,17},{15,58,00}},
                 smpptime_to_universal("070517155800000+")),
    ?assertMatch({{2007,05,17},{15,58,00}},
                 smpptime_to_universal("070517155800000-")),
    ?assertMatch({{2007,5,17},{3,58,0}},
                 smpptime_to_universal("070517155800048+")).

This paste has no annotations.

Colorize as:
Show Line Numbers

Lisppaste pastes can be made by anyone at any time. Imagine a fearsomely comprehensive disclaimer of liability. Now fear, comprehensively.