epgsql_fdatetime.erl 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. %%% Copyright (C) 2008 - Will Glozer. All rights reserved.
  2. -module(epgsql_fdatetime).
  3. -export([decode/2, encode/2]).
  4. -include("protocol.hrl").
  5. -define(POSTGRES_EPOC_JDATE, 2451545).
  6. -define(POSTGRES_EPOC_SECS, 946684800).
  7. -define(MINS_PER_HOUR, 60).
  8. -define(SECS_PER_DAY, 86400.0).
  9. -define(SECS_PER_HOUR, 3600.0).
  10. -define(SECS_PER_MINUTE, 60.0).
  11. decode(date, <<J:1/big-signed-unit:32>>) -> epgsql_idatetime:j2date(?POSTGRES_EPOC_JDATE + J);
  12. decode(time, <<N:1/big-float-unit:64>>) -> f2time(N);
  13. decode(timetz, <<N:1/big-float-unit:64, TZ:?int32>>) -> {f2time(N), TZ};
  14. decode(timestamp, <<N:1/big-float-unit:64>>) -> f2timestamp(N);
  15. decode(timestamptz, <<N:1/big-float-unit:64>>) -> f2timestamp(N);
  16. decode(interval, <<N:1/big-float-unit:64, D:?int32, M:?int32>>) -> {f2time(N), D, M}.
  17. encode(date, D) -> <<(epgsql_idatetime:date2j(D) - ?POSTGRES_EPOC_JDATE):1/big-signed-unit:32>>;
  18. encode(time, T) -> <<(time2f(T)):1/big-float-unit:64>>;
  19. encode(timetz, {T, TZ}) -> <<(time2f(T)):1/big-float-unit:64, TZ:?int32>>;
  20. encode(timestamp, TS = {_, _, _}) -> <<(now2f(TS)):1/big-float-unit:64>>;
  21. encode(timestamp, TS) -> <<(timestamp2f(TS)):1/big-float-unit:64>>;
  22. encode(timestamptz, TS = {_, _, _}) -> <<(now2f(TS)):1/big-float-unit:64>>;
  23. encode(timestamptz, TS) -> <<(timestamp2f(TS)):1/big-float-unit:64>>;
  24. encode(interval, {T, D, M}) -> <<(time2f(T)):1/big-float-unit:64, D:?int32, M:?int32>>.
  25. f2time(N) ->
  26. {R1, Hour} = tmodulo(N, ?SECS_PER_HOUR),
  27. {R2, Min} = tmodulo(R1, ?SECS_PER_MINUTE),
  28. {R3, Sec} = tmodulo(R2, 1.0),
  29. case timeround(R3) of
  30. US when US >= 1.0 -> f2time(ceiling(N));
  31. US -> {Hour, Min, Sec + US}
  32. end.
  33. time2f({H, M, S}) ->
  34. ((H * ?MINS_PER_HOUR + M) * ?SECS_PER_MINUTE) + S.
  35. f2timestamp(N) ->
  36. case tmodulo(N, ?SECS_PER_DAY) of
  37. {T, D} when T < 0 -> f2timestamp2(D - 1 + ?POSTGRES_EPOC_JDATE, T + ?SECS_PER_DAY);
  38. {T, D} -> f2timestamp2(D + ?POSTGRES_EPOC_JDATE, T)
  39. end.
  40. f2timestamp2(D, T) ->
  41. {_H, _M, S} = Time = f2time(T),
  42. Date = epgsql_idatetime:j2date(D),
  43. case tsround(S - trunc(S)) of
  44. N when N >= 1.0 ->
  45. case ceiling(T) of
  46. T2 when T2 > ?SECS_PER_DAY -> f2timestamp2(D + 1, 0.0);
  47. T2 -> f2timestamp2(T2, D)
  48. end;
  49. _ -> ok
  50. end,
  51. {Date, Time}.
  52. timestamp2f({Date, Time}) ->
  53. D = epgsql_idatetime:date2j(Date) - ?POSTGRES_EPOC_JDATE,
  54. D * ?SECS_PER_DAY + time2f(Time).
  55. now2f({MegaSecs, Secs, MicroSecs}) ->
  56. MegaSecs * 1000000 + Secs + MicroSecs / 1000000.0 - ?POSTGRES_EPOC_SECS.
  57. tmodulo(T, U) ->
  58. Q = case T < 0 of
  59. true -> ceiling(T / U);
  60. false -> flooring(T / U)
  61. end,
  62. case Q of
  63. 0 -> {T, Q};
  64. _ -> {T - rint(Q * U), Q}
  65. end.
  66. rint(N) -> round(N) * 1.0.
  67. timeround(J) -> rint(J * 10000000000.0) / 10000000000.0.
  68. tsround(J) -> rint(J * 1000000.0) / 1000000.0.
  69. flooring(X) ->
  70. T = erlang:trunc(X),
  71. case (X - T) of
  72. N when N < 0 -> T - 1;
  73. N when N > 0 -> T;
  74. _ -> T
  75. end.
  76. ceiling(X) ->
  77. T = erlang:trunc(X),
  78. case (X - T) of
  79. N when N < 0 -> T;
  80. N when N > 0 -> T + 1;
  81. _ -> T
  82. end.