diff --git a/sqlparse/engine/grouping.py b/sqlparse/engine/grouping.py
index 175ae8e..2fb0a4c 100644
--- a/sqlparse/engine/grouping.py
+++ b/sqlparse/engine/grouping.py
@@ -91,13 +91,20 @@ def group_tzcasts(tlist):
     def match(token):
         return token.ttype == T.Keyword.TZCast
 
-    def valid(token):
+    def valid_prev(token):
         return token is not None
 
+    def valid_next(token):
+        return token is not None and (
+            token.is_whitespace
+            or token.match(T.Keyword, 'AS')
+            or token.match(*sql.TypedLiteral.M_CLOSE)
+        )
+
     def post(tlist, pidx, tidx, nidx):
         return pidx, nidx
 
-    _group(tlist, sql.Identifier, match, valid, valid, post)
+    _group(tlist, sql.Identifier, match, valid_prev, valid_next, post)
 
 
 def group_typed_literal(tlist):

diff --git a/tests/test_regressions.py b/tests/test_regressions.py
index 38d1840..4ffc69f 100644
--- a/tests/test_regressions.py
+++ b/tests/test_regressions.py
@@ -401,6 +401,15 @@ def test_issue489_tzcasts():
     assert p.tokens[-1].get_alias() == 'foo'
 
 
+def test_issue562_tzcasts():
+    # Test that whitespace between 'from' and 'bar' is retained
+    formatted = sqlparse.format(
+        'SELECT f(HOUR from bar AT TIME ZONE \'UTC\') from foo', reindent=True
+    )
+    assert formatted == \
+           'SELECT f(HOUR\n         from bar AT TIME ZONE \'UTC\')\nfrom foo'
+
+
 def test_as_in_parentheses_indents():
     # did raise NoneType has no attribute is_group in _process_parentheses
     formatted = sqlparse.format('(as foo)', reindent=True)

Space removed in the extract presto function after query formatting. 
```
sqlparse.format( "SELECT extract(HOUR from from_unixtime(hour_ts) AT TIME ZONE 'America/Los_Angeles') from table", reindent=True)
```
removes space between `from` and `from_unixtime(hour_ts` in the 0.3.1 version, works fine in 0.3.0

Related superset PR: https://github.com/apache/incubator-superset/pull/10165

Expected
```
SELECT extract(HOUR
               from from_unixtime(hour_ts) AT TIME ZONE 'America/Los_Angeles')
from table
```

Actual
```
SELECT extract(HOUR
               fromfrom_unixtime(hour_ts) AT TIME ZONE 'America/Los_Angeles')
from table
```
tests/test_regressions.py::test_issue562_tzcasts
tests/test_regressions.py::test_issue9 tests/test_regressions.py::test_issue13 tests/test_regressions.py::test_issue26[--hello] tests/test_regressions.py::test_issue26[-- tests/test_regressions.py::test_issue26[--hello\n] tests/test_regressions.py::test_issue26[--] tests/test_regressions.py::test_issue26[--\n] tests/test_regressions.py::test_issue34[create] tests/test_regressions.py::test_issue34[CREATE] tests/test_regressions.py::test_issue35 tests/test_regressions.py::test_issue38 tests/test_regressions.py::test_issue39 tests/test_regressions.py::test_issue40 tests/test_regressions.py::test_issue78[get_name-z-select tests/test_regressions.py::test_issue78[get_real_name-y-select tests/test_regressions.py::test_issue78[get_parent_name-x-select tests/test_regressions.py::test_issue78[get_alias-z-select tests/test_regressions.py::test_issue78[get_typecast-text-select tests/test_regressions.py::test_issue83 tests/test_regressions.py::test_comment_encoding_when_reindent tests/test_regressions.py::test_parse_sql_with_binary tests/test_regressions.py::test_dont_alias_keywords tests/test_regressions.py::test_format_accepts_encoding tests/test_regressions.py::test_stream tests/test_regressions.py::test_issue90 tests/test_regressions.py::test_except_formatting tests/test_regressions.py::test_null_with_as tests/test_regressions.py::test_issue190_open_file tests/test_regressions.py::test_issue193_splitting_function tests/test_regressions.py::test_issue194_splitting_function tests/test_regressions.py::test_issue186_get_type tests/test_regressions.py::test_issue212_py2unicode tests/test_regressions.py::test_issue213_leadingws tests/test_regressions.py::test_issue227_gettype_cte tests/test_regressions.py::test_issue207_runaway_format tests/test_regressions.py::test_token_next_doesnt_ignore_skip_cm tests/test_regressions.py::test_issue284_as_grouping[SELECT tests/test_regressions.py::test_issue284_as_grouping[AS] tests/test_regressions.py::test_issue315_utf8_by_default tests/test_regressions.py::test_issue322_concurrently_is_keyword tests/test_regressions.py::test_issue359_index_error_assignments[SELECT tests/test_regressions.py::test_issue469_copy_as_psql_command tests/test_regressions.py::test_issue485_split_multi tests/test_regressions.py::test_issue489_tzcasts tests/test_regressions.py::test_as_in_parentheses_indents tests/test_regressions.py::test_format_invalid_where_clause tests/test_regressions.py::test_splitting_at_and_backticks_issue588