{"action":"created","comment":{"url":"https://api.github.com/repos/elixir-lang/elixir/pulls/comments/232670040","pull_request_review_id":173915426,"id":232670040,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIzMjY3MDA0MA==","diff_hunk":"@@ -172,20 +172,129 @@ defmodule DateTime do\n   Converts the given `NaiveDateTime` to `DateTime`.\n \n   It expects a time zone to put the NaiveDateTime in.\n-  Currently it only supports \"Etc/UTC\" as time zone.\n+\n+  It only supports \"Etc/UTC\" as time zone if a `TimeZoneDatabase`\n+  is not provided as a third argument.\n \n   ## Examples\n \n       iex> {:ok, datetime} = DateTime.from_naive(~N[2016-05-24 13:26:08.003], \"Etc/UTC\")\n       iex> datetime\n       #DateTime<2016-05-24 13:26:08.003Z>\n \n+  When the datetime is ambiguous - for instance during changing from summer\n+  to winter time - the two possible valid datetimes are returned. First the one\n+  that happens first, then the one that happens after.\n+\n+      iex> {:ambiguous, first_dt, second_dt} = DateTime.from_naive(~N[2018-10-28 02:30:00], \"Europe/Copenhagen\", FakeTimeZoneDatabase)\n+      iex> first_dt\n+      #DateTime<2018-10-28 02:30:00+02:00 CEST Europe/Copenhagen>\n+      iex> second_dt\n+      #DateTime<2018-10-28 02:30:00+01:00 CET Europe/Copenhagen>\n+\n+  When there is a gap in wall time - for instance in spring when the clocks are\n+  turned forward - the latest valid datetime just before the gap and the first\n+  valid datetime just after the gap.\n+\n+      iex> {:gap, just_before, just_after} = DateTime.from_naive(~N[2019-03-31 02:30:00], \"Europe/Copenhagen\", FakeTimeZoneDatabase)\n+      iex> just_before\n+      #DateTime<2019-03-31 01:59:59.999999+01:00 CET Europe/Copenhagen>\n+      iex> just_after\n+      #DateTime<2019-03-31 03:00:00+02:00 CEST Europe/Copenhagen>\n+\n+  Most of the time there is one, and just one, valid datetime for a certain\n+  date and time in a certain time zone.\n+\n+      iex> {:ok, datetime} = DateTime.from_naive(~N[2018-07-28 12:30:00], \"Europe/Copenhagen\", FakeTimeZoneDatabase)\n+      iex> datetime\n+      #DateTime<2018-07-28 12:30:00+02:00 CEST Europe/Copenhagen>\n+\n   \"\"\"\n   @doc since: \"1.4.0\"\n-  @spec from_naive(NaiveDateTime.t(), Calendar.time_zone()) :: {:ok, t}\n-  def from_naive(naive_datetime, time_zone)\n+  @spec from_naive(\n+          NaiveDateTime.t(),\n+          Calendar.time_zone(),\n+          TimeZoneDatabaseClient.tz_db_or_config()\n+        ) ::\n+          {:ok, t}\n+          | {:ambiguous, t, t}\n+          | {:gap, t, t}\n+          | {:error, :time_zone_not_found}\n+          | {:error, :incompatible_calendars}\n+          | {:error, :no_time_zone_database}\n+\n+  def from_naive(naive_datetime, time_zone, tz_db_or_config \\\\ :from_config)\n+\n+  def from_naive(naive_datetime, \"Etc/UTC\", _) do\n+    utc_period = %{std_offset: 0, utc_offset: 0, zone_abbr: \"UTC\"}\n+    {:ok, from_naive_with_period(naive_datetime, \"Etc/UTC\", utc_period)}\n+  end\n+\n+  def from_naive(%{calendar: Calendar.ISO} = naive_datetime, time_zone, tz_db_or_config) do\n+    case TimeZoneDatabaseClient.time_zone_periods_from_wall_datetime(\n+           naive_datetime,\n+           time_zone,\n+           tz_db_or_config\n+         ) do\n+      {:single, period} ->\n+        {:ok, from_naive_with_period(naive_datetime, time_zone, period)}\n+\n+      {:ambiguous, first_period, second_period} ->\n+        first_datetime = from_naive_with_period(naive_datetime, time_zone, first_period)\n+        second_datetime = from_naive_with_period(naive_datetime, time_zone, second_period)\n+        {:ambiguous, first_datetime, second_datetime}\n+\n+      {:gap, {first_period, first_period_until_wall}, {second_period, second_period_from_wall}} ->\n+        # `until_wall` is not valid, but any time just before is.\n+        # So by subtracting a second and adding .999999 seconds\n+        # we get the last microsecond just before.\n+        before_naive =\n+          first_period_until_wall\n+          |> Map.put(:microsecond, {999_999, 6})\n+          |> NaiveDateTime.add(-1)","path":"lib/elixir/lib/calendar/datetime.ex","position":86,"original_position":86,"commit_id":"b116d833f693fec734af0806725950d8e9006296","original_commit_id":"b116d833f693fec734af0806725950d8e9006296","user":{"login":"lau","id":12623,"node_id":"MDQ6VXNlcjEyNjIz","avatar_url":"https://avatars1.githubusercontent.com/u/12623?v=4","gravatar_id":"","url":"https://api.github.com/users/lau","html_url":"https://github.com/lau","followers_url":"https://api.github.com/users/lau/followers","following_url":"https://api.github.com/users/lau/following{/other_user}","gists_url":"https://api.github.com/users/lau/gists{/gist_id}","starred_url":"https://api.github.com/users/lau/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lau/subscriptions","organizations_url":"https://api.github.com/users/lau/orgs","repos_url":"https://api.github.com/users/lau/repos","events_url":"https://api.github.com/users/lau/events{/privacy}","received_events_url":"https://api.github.com/users/lau/received_events","type":"User","site_admin":false},"body":"Documenting this for posterity. José and I just talked about this and agreed to keep the handling here for the following reason:\r\n\r\nWhen the gap starts, the start of the gap is invalid. What we want here is the last possible valid time just before a gap. In this case it is 6 nines (.999999). If the timezone database was to decide it, it would have to know about what precision Elixir is using. Today it is 6 nines. So we would have to change the interface in order to pass a fractional seconds with a lot of nines just for this purpose. And the timezone database or interface would decide how many nines. Instead it is simpler to let Elixir itself decide this.","created_at":"2018-11-12T14:10:02Z","updated_at":"2018-11-12T14:10:02Z","html_url":"https://github.com/elixir-lang/elixir/pull/8383#discussion_r232670040","pull_request_url":"https://api.github.com/repos/elixir-lang/elixir/pulls/8383","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/elixir-lang/elixir/pulls/comments/232670040"},"html":{"href":"https://github.com/elixir-lang/elixir/pull/8383#discussion_r232670040"},"pull_request":{"href":"https://api.github.com/repos/elixir-lang/elixir/pulls/8383"}},"in_reply_to_id":232615389},"pull_request":{"url":"https://api.github.com/repos/elixir-lang/elixir/pulls/8383","id":230094814,"node_id":"MDExOlB1bGxSZXF1ZXN0MjMwMDk0ODE0","html_url":"https://github.com/elixir-lang/elixir/pull/8383","diff_url":"https://github.com/elixir-lang/elixir/pull/8383.diff","patch_url":"https://github.com/elixir-lang/elixir/pull/8383.patch","issue_url":"https://api.github.com/repos/elixir-lang/elixir/issues/8383","number":8383,"state":"open","locked":false,"title":"Timezone support","user":{"login":"josevalim","id":9582,"node_id":"MDQ6VXNlcjk1ODI=","avatar_url":"https://avatars0.githubusercontent.com/u/9582?v=4","gravatar_id":"","url":"https://api.github.com/users/josevalim","html_url":"https://github.com/josevalim","followers_url":"https://api.github.com/users/josevalim/followers","following_url":"https://api.github.com/users/josevalim/following{/other_user}","gists_url":"https://api.github.com/users/josevalim/gists{/gist_id}","starred_url":"https://api.github.com/users/josevalim/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/josevalim/subscriptions","organizations_url":"https://api.github.com/users/josevalim/orgs","repos_url":"https://api.github.com/users/josevalim/repos","events_url":"https://api.github.com/users/josevalim/events{/privacy}","received_events_url":"https://api.github.com/users/josevalim/received_events","type":"User","site_admin":false},"body":"Continuation of #7914.\r\n\r\nIt removes leap seconds support, so we can have a more fine grained discussion on the topic. It also streamlines the `shift_zone` operation.\r\n\r\nWe still need to discuss the API for the timezone client.","created_at":"2018-11-12T11:00:55Z","updated_at":"2018-11-12T14:10:02Z","closed_at":null,"merged_at":null,"merge_commit_sha":null,"assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/elixir-lang/elixir/pulls/8383/commits","review_comments_url":"https://api.github.com/repos/elixir-lang/elixir/pulls/8383/comments","review_comment_url":"https://api.github.com/repos/elixir-lang/elixir/pulls/comments{/number}","comments_url":"https://api.github.com/repos/elixir-lang/elixir/issues/8383/comments","statuses_url":"https://api.github.com/repos/elixir-lang/elixir/statuses/b116d833f693fec734af0806725950d8e9006296","head":{"label":"elixir-lang:jv-lau-timezone","ref":"jv-lau-timezone","sha":"b116d833f693fec734af0806725950d8e9006296","user":{"login":"elixir-lang","id":1481354,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE0ODEzNTQ=","avatar_url":"https://avatars1.githubusercontent.com/u/1481354?v=4","gravatar_id":"","url":"https://api.github.com/users/elixir-lang","html_url":"https://github.com/elixir-lang","followers_url":"https://api.github.com/users/elixir-lang/followers","following_url":"https://api.github.com/users/elixir-lang/following{/other_user}","gists_url":"https://api.github.com/users/elixir-lang/gists{/gist_id}","starred_url":"https://api.github.com/users/elixir-lang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/elixir-lang/subscriptions","organizations_url":"https://api.github.com/users/elixir-lang/orgs","repos_url":"https://api.github.com/users/elixir-lang/repos","events_url":"https://api.github.com/users/elixir-lang/events{/privacy}","received_events_url":"https://api.github.com/users/elixir-lang/received_events","type":"Organization","site_admin":false},"repo":{"id":1234714,"node_id":"MDEwOlJlcG9zaXRvcnkxMjM0NzE0","name":"elixir","full_name":"elixir-lang/elixir","private":false,"owner":{"login":"elixir-lang","id":1481354,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE0ODEzNTQ=","avatar_url":"https://avatars1.githubusercontent.com/u/1481354?v=4","gravatar_id":"","url":"https://api.github.com/users/elixir-lang","html_url":"https://github.com/elixir-lang","followers_url":"https://api.github.com/users/elixir-lang/followers","following_url":"https://api.github.com/users/elixir-lang/following{/other_user}","gists_url":"https://api.github.com/users/elixir-lang/gists{/gist_id}","starred_url":"https://api.github.com/users/elixir-lang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/elixir-lang/subscriptions","organizations_url":"https://api.github.com/users/elixir-lang/orgs","repos_url":"https://api.github.com/users/elixir-lang/repos","events_url":"https://api.github.com/users/elixir-lang/events{/privacy}","received_events_url":"https://api.github.com/users/elixir-lang/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/elixir-lang/elixir","description":"Elixir is a dynamic, functional language designed for building scalable and maintainable applications","fork":false,"url":"https://api.github.com/repos/elixir-lang/elixir","forks_url":"https://api.github.com/repos/elixir-lang/elixir/forks","keys_url":"https://api.github.com/repos/elixir-lang/elixir/keys{/key_id}","collaborators_url":"https://api.github.com/repos/elixir-lang/elixir/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/elixir-lang/elixir/teams","hooks_url":"https://api.github.com/repos/elixir-lang/elixir/hooks","issue_events_url":"https://api.github.com/repos/elixir-lang/elixir/issues/events{/number}","events_url":"https://api.github.com/repos/elixir-lang/elixir/events","assignees_url":"https://api.github.com/repos/elixir-lang/elixir/assignees{/user}","branches_url":"https://api.github.com/repos/elixir-lang/elixir/branches{/branch}","tags_url":"https://api.github.com/repos/elixir-lang/elixir/tags","blobs_url":"https://api.github.com/repos/elixir-lang/elixir/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/elixir-lang/elixir/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/elixir-lang/elixir/git/refs{/sha}","trees_url":"https://api.github.com/repos/elixir-lang/elixir/git/trees{/sha}","statuses_url":"https://api.github.com/repos/elixir-lang/elixir/statuses/{sha}","languages_url":"https://api.github.com/repos/elixir-lang/elixir/languages","stargazers_url":"https://api.github.com/repos/elixir-lang/elixir/stargazers","contributors_url":"https://api.github.com/repos/elixir-lang/elixir/contributors","subscribers_url":"https://api.github.com/repos/elixir-lang/elixir/subscribers","subscription_url":"https://api.github.com/repos/elixir-lang/elixir/subscription","commits_url":"https://api.github.com/repos/elixir-lang/elixir/commits{/sha}","git_commits_url":"https://api.github.com/repos/elixir-lang/elixir/git/commits{/sha}","comments_url":"https://api.github.com/repos/elixir-lang/elixir/comments{/number}","issue_comment_url":"https://api.github.com/repos/elixir-lang/elixir/issues/comments{/number}","contents_url":"https://api.github.com/repos/elixir-lang/elixir/contents/{+path}","compare_url":"https://api.github.com/repos/elixir-lang/elixir/compare/{base}...{head}","merges_url":"https://api.github.com/repos/elixir-lang/elixir/merges","archive_url":"https://api.github.com/repos/elixir-lang/elixir/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/elixir-lang/elixir/downloads","issues_url":"https://api.github.com/repos/elixir-lang/elixir/issues{/number}","pulls_url":"https://api.github.com/repos/elixir-lang/elixir/pulls{/number}","milestones_url":"https://api.github.com/repos/elixir-lang/elixir/milestones{/number}","notifications_url":"https://api.github.com/repos/elixir-lang/elixir/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/elixir-lang/elixir/labels{/name}","releases_url":"https://api.github.com/repos/elixir-lang/elixir/releases{/id}","deployments_url":"https://api.github.com/repos/elixir-lang/elixir/deployments","created_at":"2011-01-09T08:43:57Z","updated_at":"2018-11-12T13:16:50Z","pushed_at":"2018-11-12T13:16:45Z","git_url":"git://github.com/elixir-lang/elixir.git","ssh_url":"git@github.com:elixir-lang/elixir.git","clone_url":"https://github.com/elixir-lang/elixir.git","svn_url":"https://github.com/elixir-lang/elixir","homepage":"http://elixir-lang.org/","size":38226,"stargazers_count":14174,"watchers_count":14174,"language":"Elixir","has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2050,"mirror_url":null,"archived":false,"open_issues_count":26,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":2050,"open_issues":26,"watchers":14174,"default_branch":"master"}},"base":{"label":"elixir-lang:master","ref":"master","sha":"43a4dca94ab605a0a5795d43e43e5671f4a49071","user":{"login":"elixir-lang","id":1481354,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE0ODEzNTQ=","avatar_url":"https://avatars1.githubusercontent.com/u/1481354?v=4","gravatar_id":"","url":"https://api.github.com/users/elixir-lang","html_url":"https://github.com/elixir-lang","followers_url":"https://api.github.com/users/elixir-lang/followers","following_url":"https://api.github.com/users/elixir-lang/following{/other_user}","gists_url":"https://api.github.com/users/elixir-lang/gists{/gist_id}","starred_url":"https://api.github.com/users/elixir-lang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/elixir-lang/subscriptions","organizations_url":"https://api.github.com/users/elixir-lang/orgs","repos_url":"https://api.github.com/users/elixir-lang/repos","events_url":"https://api.github.com/users/elixir-lang/events{/privacy}","received_events_url":"https://api.github.com/users/elixir-lang/received_events","type":"Organization","site_admin":false},"repo":{"id":1234714,"node_id":"MDEwOlJlcG9zaXRvcnkxMjM0NzE0","name":"elixir","full_name":"elixir-lang/elixir","private":false,"owner":{"login":"elixir-lang","id":1481354,"node_id":"MDEyOk9yZ2FuaXphdGlvbjE0ODEzNTQ=","avatar_url":"https://avatars1.githubusercontent.com/u/1481354?v=4","gravatar_id":"","url":"https://api.github.com/users/elixir-lang","html_url":"https://github.com/elixir-lang","followers_url":"https://api.github.com/users/elixir-lang/followers","following_url":"https://api.github.com/users/elixir-lang/following{/other_user}","gists_url":"https://api.github.com/users/elixir-lang/gists{/gist_id}","starred_url":"https://api.github.com/users/elixir-lang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/elixir-lang/subscriptions","organizations_url":"https://api.github.com/users/elixir-lang/orgs","repos_url":"https://api.github.com/users/elixir-lang/repos","events_url":"https://api.github.com/users/elixir-lang/events{/privacy}","received_events_url":"https://api.github.com/users/elixir-lang/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/elixir-lang/elixir","description":"Elixir is a dynamic, functional language designed for building scalable and maintainable applications","fork":false,"url":"https://api.github.com/repos/elixir-lang/elixir","forks_url":"https://api.github.com/repos/elixir-lang/elixir/forks","keys_url":"https://api.github.com/repos/elixir-lang/elixir/keys{/key_id}","collaborators_url":"https://api.github.com/repos/elixir-lang/elixir/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/elixir-lang/elixir/teams","hooks_url":"https://api.github.com/repos/elixir-lang/elixir/hooks","issue_events_url":"https://api.github.com/repos/elixir-lang/elixir/issues/events{/number}","events_url":"https://api.github.com/repos/elixir-lang/elixir/events","assignees_url":"https://api.github.com/repos/elixir-lang/elixir/assignees{/user}","branches_url":"https://api.github.com/repos/elixir-lang/elixir/branches{/branch}","tags_url":"https://api.github.com/repos/elixir-lang/elixir/tags","blobs_url":"https://api.github.com/repos/elixir-lang/elixir/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/elixir-lang/elixir/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/elixir-lang/elixir/git/refs{/sha}","trees_url":"https://api.github.com/repos/elixir-lang/elixir/git/trees{/sha}","statuses_url":"https://api.github.com/repos/elixir-lang/elixir/statuses/{sha}","languages_url":"https://api.github.com/repos/elixir-lang/elixir/languages","stargazers_url":"https://api.github.com/repos/elixir-lang/elixir/stargazers","contributors_url":"https://api.github.com/repos/elixir-lang/elixir/contributors","subscribers_url":"https://api.github.com/repos/elixir-lang/elixir/subscribers","subscription_url":"https://api.github.com/repos/elixir-lang/elixir/subscription","commits_url":"https://api.github.com/repos/elixir-lang/elixir/commits{/sha}","git_commits_url":"https://api.github.com/repos/elixir-lang/elixir/git/commits{/sha}","comments_url":"https://api.github.com/repos/elixir-lang/elixir/comments{/number}","issue_comment_url":"https://api.github.com/repos/elixir-lang/elixir/issues/comments{/number}","contents_url":"https://api.github.com/repos/elixir-lang/elixir/contents/{+path}","compare_url":"https://api.github.com/repos/elixir-lang/elixir/compare/{base}...{head}","merges_url":"https://api.github.com/repos/elixir-lang/elixir/merges","archive_url":"https://api.github.com/repos/elixir-lang/elixir/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/elixir-lang/elixir/downloads","issues_url":"https://api.github.com/repos/elixir-lang/elixir/issues{/number}","pulls_url":"https://api.github.com/repos/elixir-lang/elixir/pulls{/number}","milestones_url":"https://api.github.com/repos/elixir-lang/elixir/milestones{/number}","notifications_url":"https://api.github.com/repos/elixir-lang/elixir/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/elixir-lang/elixir/labels{/name}","releases_url":"https://api.github.com/repos/elixir-lang/elixir/releases{/id}","deployments_url":"https://api.github.com/repos/elixir-lang/elixir/deployments","created_at":"2011-01-09T08:43:57Z","updated_at":"2018-11-12T13:16:50Z","pushed_at":"2018-11-12T13:16:45Z","git_url":"git://github.com/elixir-lang/elixir.git","ssh_url":"git@github.com:elixir-lang/elixir.git","clone_url":"https://github.com/elixir-lang/elixir.git","svn_url":"https://github.com/elixir-lang/elixir","homepage":"http://elixir-lang.org/","size":38226,"stargazers_count":14174,"watchers_count":14174,"language":"Elixir","has_issues":true,"has_projects":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2050,"mirror_url":null,"archived":false,"open_issues_count":26,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":2050,"open_issues":26,"watchers":14174,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/elixir-lang/elixir/pulls/8383"},"html":{"href":"https://github.com/elixir-lang/elixir/pull/8383"},"issue":{"href":"https://api.github.com/repos/elixir-lang/elixir/issues/8383"},"comments":{"href":"https://api.github.com/repos/elixir-lang/elixir/issues/8383/comments"},"review_comments":{"href":"https://api.github.com/repos/elixir-lang/elixir/pulls/8383/comments"},"review_comment":{"href":"https://api.github.com/repos/elixir-lang/elixir/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/elixir-lang/elixir/pulls/8383/commits"},"statuses":{"href":"https://api.github.com/repos/elixir-lang/elixir/statuses/b116d833f693fec734af0806725950d8e9006296"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"lau"}}