@@ -1004,19 +1004,32 @@ defmodule DateTime do
10041004 {:ok, %DateTime{calendar: Calendar.ISO, day: 23, hour: 22, microsecond: {211914, 3}, minute: 53,
10051005 month: 1, second: 43, std_offset: 0, time_zone: "Etc/UTC", utc_offset: 0,
10061006 year: 46302, zone_abbr: "UTC"}}
1007-
1007+
1008+ Negative Unix times are supported, up to -#{ @ unix_epoch } seconds,
1009+ which is equivalent to "0000-01-01T00:00:00Z" or 0 gregorian seconds.
1010+
1011+ iex> DateTime.from_unix(-12345678910)
1012+ {:ok, %DateTime{calendar: Calendar.ISO, day: 13, hour: 4, microsecond: {0, 0}, minute: 44,
1013+ month: 10, second: 50, std_offset: 0, time_zone: "Etc/UTC", utc_offset: 0,
1014+ year: 1578, zone_abbr: "UTC"}}
1015+
1016+ When a Unix time before that moment is passed to `from_unix/2`, `:error` will be returned.
10081017 """
1009- @ spec from_unix ( non_neg_integer , :native | System . time_unit ) :: { :ok , DateTime . t }
1010- def from_unix ( integer , unit \\ :seconds ) when is_integer ( integer ) and integer >= 0 do
1018+ @ spec from_unix ( integer , :native | System . time_unit ) :: { :ok , DateTime . t }
1019+ def from_unix ( integer , unit \\ :seconds ) when is_integer ( integer ) do
10111020 total = System . convert_time_unit ( integer , unit , :microseconds )
1012- microsecond = rem ( total , 1_000_000 )
1013- precision = precision_for_unit ( unit )
1014- { { year , month , day } , { hour , minute , second } } =
1015- :calendar . gregorian_seconds_to_datetime ( @ unix_epoch + div ( total , 1_000_000 ) )
1016-
1017- { :ok , % DateTime { year: year , month: month , day: day ,
1018- hour: hour , minute: minute , second: second , microsecond: { microsecond , precision } ,
1019- std_offset: 0 , utc_offset: 0 , zone_abbr: "UTC" , time_zone: "Etc/UTC" } }
1021+ if total < - @ unix_epoch * 1_000_000 do
1022+ :error
1023+ else
1024+ microsecond = rem ( total , 1_000_000 )
1025+ precision = precision_for_unit ( unit )
1026+ { { year , month , day } , { hour , minute , second } } =
1027+ :calendar . gregorian_seconds_to_datetime ( @ unix_epoch + div ( total , 1_000_000 ) )
1028+
1029+ { :ok , % DateTime { year: year , month: month , day: day ,
1030+ hour: hour , minute: minute , second: second , microsecond: { microsecond , precision } ,
1031+ std_offset: 0 , utc_offset: 0 , zone_abbr: "UTC" , time_zone: "Etc/UTC" } }
1032+ end
10201033 end
10211034
10221035 def precision_for_unit ( unit ) do
@@ -1034,6 +1047,13 @@ defmodule DateTime do
10341047 @ doc """
10351048 Converts the given Unix time to DateTime.
10361049
1050+ The integer can be given in different unit
1051+ according to `System.convert_time_unit/3` and it will
1052+ be converted to microseconds internally.
1053+
1054+ Unix times are always in UTC and therefore the DateTime
1055+ will be returned in UTC.
1056+
10371057 ## Examples
10381058
10391059 iex> DateTime.from_unix!(1464096368)
@@ -1046,6 +1066,15 @@ defmodule DateTime do
10461066 month: 5, second: 8, std_offset: 0, time_zone: "Etc/UTC", utc_offset: 0,
10471067 year: 2015, zone_abbr: "UTC"}
10481068
1069+ Negative Unix times are supported, up to -#{ @ unix_epoch } seconds,
1070+ which is equivalent to "0000-01-01T00:00:00Z" or 0 gregorian seconds.
1071+
1072+ iex> DateTime.from_unix(-12345678910)
1073+ {:ok, %DateTime{calendar: Calendar.ISO, day: 13, hour: 4, microsecond: {0, 0}, minute: 44,
1074+ month: 10, second: 50, std_offset: 0, time_zone: "Etc/UTC", utc_offset: 0,
1075+ year: 1578, zone_abbr: "UTC"}}
1076+
1077+ When a Unix time before that moment is passed to `from_unix!/2`, an ArgumentError will be raised.
10491078 """
10501079 @ spec from_unix! ( non_neg_integer , :native | System . time_unit ) :: DateTime . t
10511080 def from_unix! ( integer , unit \\ :seconds ) when is_atom ( unit ) do
@@ -1057,7 +1086,7 @@ defmodule DateTime do
10571086 Converts the given DateTime to Unix time.
10581087
10591088 The DateTime is expected to be using the ISO calendar
1060- with a year greater than or equal to 1970 .
1089+ with a year greater than or equal to 0 .
10611090
10621091 It will return the integer with the given unit,
10631092 according to `System.convert_time_unit/3`.
@@ -1073,11 +1102,19 @@ defmodule DateTime do
10731102 iex> DateTime.to_unix(dt)
10741103 1416517099
10751104
1105+ iex> flamel = %DateTime{calendar: Calendar.ISO, day: 22, hour: 8, microsecond: {527771, 6},
1106+ ...> minute: 2, month: 3, second: 25, std_offset: 0, time_zone: "Etc/UTC",
1107+ ...> utc_offset: 0, year: 1418, zone_abbr: "UTC"}
1108+ iex> DateTime.to_unix(flamel)
1109+ -17412508655
1110+
10761111 """
10771112 @ spec to_unix ( DateTime . t , System . time_unit ) :: non_neg_integer
1113+ def to_unix ( datetime , unit \\ :seconds )
1114+
10781115 def to_unix ( % DateTime { calendar: Calendar.ISO , std_offset: std_offset , utc_offset: utc_offset ,
10791116 hour: hour , minute: minute , second: second , microsecond: { microsecond , _ } ,
1080- year: year , month: month , day: day } , unit \\ :seconds ) when year >= 1970 do
1117+ year: year , month: month , day: day } , unit ) when year >= 0 do
10811118 seconds =
10821119 :calendar . datetime_to_gregorian_seconds ( { { year , month , day } , { hour , minute , second } } )
10831120 |> Kernel . - ( utc_offset )
0 commit comments