epgsql_fdatetime.erl 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  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>>) -> 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) -> <<4:?int32, (date2j(D) - ?postgres_epoc_jdate):1/big-signed-unit:32>>;
  18. encode(time, T) -> <<8:?int32, (time2f(T)):1/big-float-unit:64>>;
  19. encode(timetz, {T, TZ}) -> <<12:?int32, (time2f(T)):1/big-float-unit:64, TZ:?int32>>;
  20. encode(timestamp, TS = {_, _, _}) -> <<8:?int32, (now2f(TS)):1/big-float-unit:64>>;
  21. encode(timestamp, TS) -> <<8:?int32, (timestamp2f(TS)):1/big-float-unit:64>>;
  22. encode(timestamptz, TS = {_, _, _}) -> <<8:?int32, (now2f(TS)):1/big-float-unit:64>>;
  23. encode(timestamptz, TS) -> <<8:?int32, (timestamp2f(TS)):1/big-float-unit:64>>;
  24. encode(interval, {T, D, M}) -> <<16:?int32, (time2f(T)):1/big-float-unit:64, D:?int32, M:?int32>>.
  25. j2date(N) ->
  26. J = N + 32044,
  27. Q1 = J div 146097,
  28. Extra = (J - Q1 * 146097) * 4 + 3,
  29. J2 = J + 60 + Q1 * 3 + Extra div 146097,
  30. Q2 = J2 div 1461,
  31. J3 = J2 - Q2 * 1461,
  32. Y = J3 * 4 div 1461,
  33. J4 = case Y of
  34. 0 -> ((J3 + 306) rem 366) + 123;
  35. _ -> ((J3 + 305) rem 365) + 123
  36. end,
  37. Year = (Y + Q2 * 4) - 4800,
  38. Q3 = J4 * 2141 div 65536,
  39. Day = J4 - 7834 * Q3 div 256,
  40. Month = (Q3 + 10) rem 12 + 1,
  41. {Year, Month, Day}.
  42. date2j({Y, M, D}) ->
  43. M2 = case M > 2 of
  44. true ->
  45. M + 1;
  46. false ->
  47. M + 13
  48. end,
  49. Y2 = case M > 2 of
  50. true ->
  51. Y + 4800;
  52. false ->
  53. Y + 4799
  54. end,
  55. C = Y2 div 100,
  56. J1 = Y2 * 365 - 32167,
  57. J2 = J1 + (Y2 div 4 - C + C div 4),
  58. J2 + 7834 * M2 div 256 + D.
  59. f2time(N) ->
  60. {R1, Hour} = tmodulo(N, ?secs_per_hour),
  61. {R2, Min} = tmodulo(R1, ?secs_per_minute),
  62. {R3, Sec} = tmodulo(R2, 1.0),
  63. case timeround(R3) of
  64. US when US >= 1.0 -> f2time(ceiling(N));
  65. US -> {Hour, Min, Sec + US}
  66. end.
  67. time2f({H, M, S}) ->
  68. ((H * ?mins_per_hour + M) * ?secs_per_minute) + S.
  69. f2timestamp(N) ->
  70. case tmodulo(N, ?secs_per_day) of
  71. {T, D} when T < 0 -> f2timestamp2(D - 1 + ?postgres_epoc_jdate, T + ?secs_per_day);
  72. {T, D} -> f2timestamp2(D + ?postgres_epoc_jdate, T)
  73. end.
  74. f2timestamp2(D, T) ->
  75. {_H, _M, S} = Time = f2time(T),
  76. Date = j2date(D),
  77. case tsround(S - trunc(S)) of
  78. N when N >= 1.0 ->
  79. case ceiling(T) of
  80. T2 when T2 > ?secs_per_day -> f2timestamp2(D + 1, 0.0);
  81. T2 -> f2timestamp2(T2, D)
  82. end;
  83. _ -> ok
  84. end,
  85. {Date, Time}.
  86. timestamp2f({Date, Time}) ->
  87. D = date2j(Date) - ?postgres_epoc_jdate,
  88. D * ?secs_per_day + time2f(Time).
  89. now2f({MegaSecs, Secs, MicroSecs}) ->
  90. MegaSecs * 1000000 + Secs + MicroSecs / 1000000.0 - ?postgres_epoc_secs.
  91. tmodulo(T, U) ->
  92. Q = case T < 0 of
  93. true -> ceiling(T / U);
  94. false -> flooring(T / U)
  95. end,
  96. case Q of
  97. 0 -> {T, Q};
  98. _ -> {T - rint(Q * U), Q}
  99. end.
  100. rint(N) -> round(N) * 1.0.
  101. timeround(J) -> rint(J * 10000000000.0) / 10000000000.0.
  102. tsround(J) -> rint(J * 1000000.0) / 1000000.0.
  103. flooring(X) ->
  104. T = erlang:trunc(X),
  105. case (X - T) of
  106. N when N < 0 -> T - 1;
  107. N when N > 0 -> T;
  108. _ -> T
  109. end.
  110. ceiling(X) ->
  111. T = erlang:trunc(X),
  112. case (X - T) of
  113. N when N < 0 -> T;
  114. N when N > 0 -> T + 1;
  115. _ -> T
  116. end.