Repository: ariga/atlas
Pull request #686 (2022-04-06 20:05:22)

sql/postgres: use native pg time types


Patch:
diff --git a/doc/md/ddl/sql_types.md b/doc/md/ddl/sql_types.md
index 21713054..025a9dd8 100644
--- a/doc/md/ddl/sql_types.md
+++ b/doc/md/ddl/sql_types.md
@@ -1604,42 +1604,6 @@ enum "my_enum" &#123;
             </td>
         </tr>
         
-        <tr>
-            <td>time_with_time_zone</td>
-            <td>time with time zone</td>
-            <td>
-                <ul>
-                        <li>precision (int)</li>
-                </ul>
-            </td>
-            <td>
-                
-                <pre>
-                    type = time_with_time_zone(10)
-                </pre>
-                
-                
-            </td>
-        </tr>
-        
-        <tr>
-            <td>time_without_time_zone</td>
-            <td>time without time zone</td>
-            <td>
-                <ul>
-                        <li>precision (int)</li>
-                </ul>
-            </td>
-            <td>
-                
-                <pre>
-                    type = time_without_time_zone(10)
-                </pre>
-                
-                
-            </td>
-        </tr>
-        
         <tr>
             <td>timestamp</td>
             <td>timestamp</td>
@@ -1659,26 +1623,8 @@ enum "my_enum" &#123;
         </tr>
         
         <tr>
-            <td>timestamp_with_time_zone</td>
-            <td>timestamp with time zone</td>
-            <td>
-                <ul>
-                        <li>precision (int)</li>
-                </ul>
-            </td>
-            <td>
-                
-                <pre>
-                    type = timestamp_with_time_zone(10)
-                </pre>
-                
-                
-            </td>
-        </tr>
-        
-        <tr>
-            <td>timestamp_without_time_zone</td>
-            <td>timestamp without time zone</td>
+            <td>timestamptz</td>
+            <td>timestamptz</td>
             <td>
                 <ul>
                         <li>precision (int)</li>
@@ -1687,7 +1633,7 @@ enum "my_enum" &#123;
             <td>
                 
                 <pre>
-                    type = timestamp_without_time_zone(10)
+                    type = timestamptz(10)
                 </pre>
                 
                 
@@ -1695,8 +1641,8 @@ enum "my_enum" &#123;
         </tr>
         
         <tr>
-            <td>timestamptz</td>
-            <td>timestamptz</td>
+            <td>timetz</td>
+            <td>timetz</td>
             <td>
                 <ul>
                         <li>precision (int)</li>
@@ -1705,7 +1651,7 @@ enum "my_enum" &#123;
             <td>
                 
                 <pre>
-                    type = timestamptz(10)
+                    type = timetz(10)
                 </pre>
                 
                 
diff --git a/schema/schemaspec/schemahcl/hcl.go b/schema/schemaspec/schemahcl/hcl.go
index 841c61f5..0c53dc5e 100644
--- a/schema/schemaspec/schemahcl/hcl.go
+++ b/schema/schemaspec/schemahcl/hcl.go
@@ -355,6 +355,9 @@ func (s *State) findTypeSpec(t string) (*schemaspec.TypeSpec, bool) {
 }
 
 func hclType(spec *schemaspec.TypeSpec, typ *schemaspec.Type) (string, error) {
+	if spec.Format != nil {
+		return spec.Format(typ)
+	}
 	if len(typeFuncArgs(spec)) == 0 {
 		return spec.Name, nil
 	}
diff --git a/schema/schemaspec/spec.go b/schema/schemaspec/spec.go
index b8cf5b18..f5f50558 100644
--- a/schema/schemaspec/spec.go
+++ b/schema/schemaspec/spec.go
@@ -75,16 +75,19 @@ type (
 	TypeSpec struct {
 		// Name is the identifier for the type in an Atlas DDL document.
 		Name string
+
 		// T is the database identifier for the type.
 		T          string
 		Attributes []*TypeAttr
+
 		// RType is the reflect.Type of the schema.Type used to describe the TypeSpec.
 		// This field is optional and used to determine the TypeSpec in cases where the
 		// schema.Type does not have a `T` field.
 		RType reflect.Type
-		// Printer is an optional function used by the drivers to get a string representation of the
-		// represented Type, that can be parsed by the driver into a schema.Type.
-		Printer func(*Type) (string, error)
+
+		// Format is an optional formatting function.
+		// If exists, it will be used instead the registry one.
+		Format func(*Type) (string, error)
 	}
 
 	// TypeAttr describes an attribute of a TypeSpec, for example `varchar` fields
diff --git a/sql/internal/specutil/types.go b/sql/internal/specutil/types.go
index 8ce0491c..212194e6 100644
--- a/sql/internal/specutil/types.go
+++ b/sql/internal/specutil/types.go
@@ -21,9 +21,6 @@ func (r *TypeRegistry) PrintType(typ *schemaspec.Type) (string, error) {
 	if !ok {
 		return "", fmt.Errorf("specutil: type %q not found in registry", typ.T)
 	}
-	if spec.Printer != nil {
-		return spec.Printer(typ)
-	}
 	if len(spec.Attributes) == 0 {
 		return typ.T, nil
 	}
@@ -70,16 +67,31 @@ func (r *TypeRegistry) PrintType(typ *schemaspec.Type) (string, error) {
 
 // TypeRegistry is a collection of *schemaspec.TypeSpec.
 type TypeRegistry struct {
-	r         []*schemaspec.TypeSpec
-	formatter func(schema.Type) (string, error)
-	parser    func(string) (schema.Type, error)
+	r      []*schemaspec.TypeSpec
+	spec   func(schema.Type) (*schemaspec.Type, error)
+	parser func(string) (schema.Type, error)
 }
 
 // WithFormatter configures the registry to use a formatting function for printing
 // schema.Type as string.
-func WithFormatter(formatter func(schema.Type) (string, error)) RegistryOption {
+func WithFormatter(f func(schema.Type) (string, error)) RegistryOption {
+	return func(registry *TypeRegistry) error {
+		registry.spec = func(t schema.Type) (*schemaspec.Type, error) {
+			s, err := f(t)
+			if err != nil {
+				return nil, fmt.Errorf("specutil: cannot format type %T: %w", t, err)
+			}
+			return &schemaspec.Type{T: s}, nil
+		}
+		return nil
+	}
+}
+
+// WithSpecFunc configures the registry to use the given function for converting
+// a schema.Type to schemaspec.Type
+func WithSpecFunc(spec func(schema.Type) (*schemaspec.Type, error)) RegistryOption {
 	return func(registry *TypeRegistry) error {
-		registry.formatter = formatter
+		registry.spec = spec
 		return nil
 	}
 }
@@ -185,11 +197,7 @@ func (r *TypeRegistry) Convert(typ schema.Type) (*schemaspec.Type, error) {
 	}
 	typeSpec, ok := r.findType(rv)
 	if !ok {
-		t, err := r.formatter(typ)
-		if err != nil {
-			return nil, fmt.Errorf("specutil: cannot format type %T: %w", typ, err)
-		}
-		return &schemaspec.Type{T: t}, nil
+		return r.spec(typ)
 	}
 	s := &schemaspec.Type{T: typeSpec.T}
 	// Iterate the attributes in reverse order, so we can skip zero value and optional attrs.
@@ -293,10 +301,10 @@ func WithAttributes(attrs ...*schemaspec.TypeAttr) TypeSpecOption {
 	}
 }
 
-// WithPrinter returns a printer TypeSpecOption.
-func WithPrinter(p func(*schemaspec.Type) (string, error)) TypeSpecOption {
+// WithTypeFormatter allows overriding the Format function for the Type.
+func WithTypeFormatter(f func(*schemaspec.Type) (string, error)) TypeSpecOption {
 	return func(spec *schemaspec.TypeSpec) {
-		spec.Printer = p
+		spec.Format = f
 	}
 }
 
diff --git a/sql/postgres/convert.go b/sql/postgres/convert.go
index 802121df..9542968d 100644
--- a/sql/postgres/convert.go
+++ b/sql/postgres/convert.go
@@ -73,20 +73,9 @@ func FormatType(t schema.Type) (string, error) {
 			return "", fmt.Errorf("postgres: unexpected string type: %q", t.T)
 		}
 	case *schema.TimeType:
-		switch f = strings.ToLower(t.T); f {
-		// TIMESTAMPTZ is accepted as an abbreviation for TIMESTAMP WITH TIME ZONE.
-		case TypeTimestampTZ:
-			f = TypeTimestampWTZ
-		// TIME be equivalent to TIME WITHOUT TIME ZONE.
-		case TypeTime:
-			f = TypeTimeWOTZ
-		// TIMESTAMP be equivalent to TIMESTAMP WITHOUT TIME ZONE.
-		case TypeTimestamp:
-			f = TypeTimestampWOTZ
-		}
+		f = timeAlias(t.T)
 		if t.Precision != defaultTimePrecision && strings.HasPrefix(f, "time") {
-			p := strings.Split(f, " ")
-			f = fmt.Sprintf("%s(%d)%s", p[0], t.Precision, strings.Join(p[1:], " "))
+			f += fmt.Sprintf("(%d)", t.Precision)
 		}
 	case *schema.FloatType:
 		switch f = strings.ToLower(t.T); f {
@@ -168,7 +157,7 @@ func ParseType(typ string) (schema.Type, error) {
 		d = &columnDesc{typ: TypeArray, udt: t}
 	}
 	t := columnType(d)
-	// If the type is unknown (to us), we fallback to user-defined but expect
+	// If the type is unknown (to us), we fall back to user-defined but expect
 	// to improve this in future versions by ensuring this against the database.
 	if ut, ok := t.(*schema.UnsupportedType); ok {
 		t = &UserDefinedType{T: ut.T}
@@ -195,7 +184,7 @@ type columnDesc struct {
 	size          int64
 	udt           string
 	precision     int64
-	timePrecision int64
+	timePrecision *int64
 	scale         int64
 	typtype       string
 	typid         int64
@@ -241,21 +230,20 @@ func parseColumn(s string) (*columnDesc, error) {
 		c.precision = 53
 	case TypeReal, TypeFloat4:
 		c.precision = 24
-	case TypeTime, TypeTimestamp, TypeTimestampTZ:
+	case TypeTime, TypeTimeWOTZ, TypeTimeTZ, TypeTimeWTZ, TypeTimestamp, TypeTimestampWOTZ, TypeTimestampTZ, TypeTimestampWTZ:
+		t, p := timeAlias(c.parts[0]), int64(defaultTimePrecision)
 		// If the second part is only one digit it is the precision argument.
-		// For cases like "timestamp(4) with time zone" make sure to not drop the rest of the type definition.
-		offset := 1
+		// For cases like "timestamp(4) with time zone" make sure to not drop
+		// the rest of the type definition.
 		if len(parts) > 1 && reDigits.MatchString(parts[1]) {
-			offset = 2
-			c.timePrecision, err = strconv.ParseInt(parts[1], 10, 64)
+			i, err := strconv.ParseInt(parts[1], 10, 64)
 			if err != nil {
 				return nil, fmt.Errorf("postgres: parse time precision %q: %w", parts[1], err)
 			}
+			p = i
 		}
-		// Append time zone part (if present).
-		if len(parts) > offset {
-			c.typ = fmt.Sprintf("%s %s", c.typ, strings.Join(parts[offset:], " "))
-		}
+		c.typ = t
+		c.timePrecision = &p
 	default:
 		c.typ = s
 	}
@@ -305,3 +293,22 @@ func parseBitParts(parts []string, c *columnDesc) error {
 	c.size = size
 	return nil
 }
+
+// timeAlias returns the abbreviation for the given time type.
+func timeAlias(t string) string {
+	switch t = strings.ToLower(t); t {
+	// TIMESTAMPTZ be equivalent to TIMESTAMP WITH TIME ZONE.
+	case TypeTimestampWTZ:
+		t = TypeTimestampTZ
+	// TIMESTAMP be equivalent to TIMESTAMP WITHOUT TIME ZONE.
+	case TypeTimestampWOTZ:
+		t = TypeTimestamp
+	// TIME be equivalent to TIME WITHOUT TIME ZONE.
+	case TypeTimeWOTZ:
+		t = TypeTime
+	// TIMETZ be equivalent to TIME WITH TIME ZONE.
+	case TypeTimeWTZ:
+		t = TypeTimeTZ
+	}
+	return t
+}
diff --git a/sql/postgres/driver.go b/sql/postgres/driver.go
index c6987f2e..5f347ccf 100644
--- a/sql/postgres/driver.go
+++ b/sql/postgres/driver.go
@@ -183,7 +183,8 @@ const (
 	TypePoint   = "point"
 
 	TypeDate          = "date"
-	TypeTime          = "time" // time without time zone
+	TypeTime          = "time"   // time without time zone
+	TypeTimeTZ        = "timetz" // time with time zone
 	TypeTimeWTZ       = "time with time zone"
 	TypeTimeWOTZ      = "time without time zone"
 	TypeTimestamp     = "timestamp" // timestamp without time zone
diff --git a/sql/postgres/inspect.go b/sql/postgres/inspect.go
index 1703cc8d..f54495ce 100644
--- a/sql/postgres/inspect.go
+++ b/sql/postgres/inspect.go
@@ -175,11 +175,11 @@ func (i *inspect) addColumn(s *schema.Schema, rows *sql.Rows) error {
 		typ:           typ.String,
 		size:          maxlen.Int64,
 		udt:           udt.String,
-		precision:     precision.Int64,
-		timePrecision: timeprecision.Int64,
 		scale:         scale.Int64,
 		typtype:       typtype.String,
 		typid:         typid.Int64,
+		precision:     precision.Int64,
+		timePrecision: &timeprecision.Int64,
 	})
 	if defaults.Valid {
 		c.Default = defaultExpr(c, defaults.String)
@@ -231,9 +231,15 @@ func columnType(c *columnDesc) schema.Type {
 		typ = &NetworkType{T: t}
 	case TypeCircle, TypeLine, TypeLseg, TypeBox, TypePath, TypePolygon, TypePoint:
 		typ = &schema.SpatialType{T: t}
-	case TypeDate, TypeTime, TypeTimeWTZ, TypeTimeWOTZ,
-		TypeTimestamp, TypeTimestampTZ, TypeTimestampWTZ, TypeTimestampWOTZ:
-		typ = &schema.TimeType{T: t, Precision: int(c.timePrecision)}
+	case TypeDate:
+		typ = &schema.TimeType{T: t}
+	case TypeTime, TypeTimeWOTZ, TypeTimeTZ, TypeTimeWTZ, TypeTimestamp,
+		TypeTimestampTZ, TypeTimestampWTZ, TypeTimestampWOTZ:
+		t := &schema.TimeType{T: t, Precision: defaultTimePrecision}
+		if c.timePrecision != nil {
+			t.Precision = int(*c.timePrecision)
+		}
+		typ = t
 	case TypeInterval:
 		// TODO: get 'interval_type' from query above before implementing.
 		typ = &schema.UnsupportedType{T: t}
diff --git a/sql/postgres/sqlspec.go b/sql/postgres/sqlspec.go
index 063fc609..484eb0e3 100644
--- a/sql/postgres/sqlspec.go
+++ b/sql/postgres/sqlspec.go
@@ -379,7 +379,7 @@ func columnTypeSpec(t schema.Type) (*sqlspec.Column, error) {
 
 // TypeRegistry contains the supported TypeSpecs for the Postgres driver.
 var TypeRegistry = specutil.NewRegistry(
-	specutil.WithFormatter(FormatType),
+	specutil.WithSpecFunc(typeSpec),
 	specutil.WithParser(ParseType),
 	specutil.WithSpecs(
 		specutil.TypeSpec(TypeBit, specutil.WithAttributes(&schemaspec.TypeAttr{Name: "len", Kind: reflect.Int64})),
@@ -410,33 +410,10 @@ var TypeRegistry = specutil.NewRegistry(
 		specutil.TypeSpec(TypePath),
 		specutil.TypeSpec(TypePoint),
 		specutil.TypeSpec(TypeDate),
-		specutil.TypeSpec(TypeTime, specutil.WithAttributes(precisionTypeAttr())),
-		specutil.AliasTypeSpec(
-			"time_with_time_zone",
-			TypeTimeWTZ,
-			specutil.WithAttributes(precisionTypeAttr()),
-			specutil.WithPrinter(timePrinter),
-		),
-		specutil.AliasTypeSpec(
-			"time_without_time_zone",
-			TypeTimeWOTZ,
-			specutil.WithAttributes(precisionTypeAttr()),
-			specutil.WithPrinter(timePrinter),
-		),
-		specutil.TypeSpec(TypeTimestampTZ, specutil.WithAttributes(precisionTypeAttr())),
-		specutil.TypeSpec(TypeTimestamp, specutil.WithAttributes(precisionTypeAttr())),
-		specutil.AliasTypeSpec(
-			"timestamp_with_time_zone",
-			TypeTimestampWTZ,
-			specutil.WithAttributes(precisionTypeAttr()),
-			specutil.WithPrinter(timePrinter),
-		),
-		specutil.AliasTypeSpec(
-			"timestamp_without_time_zone",
-			TypeTimestampWOTZ,
-			specutil.WithAttributes(precisionTypeAttr()),
-			specutil.WithPrinter(timePrinter),
-		),
+		specutil.TypeSpec(TypeTime, specutil.WithAttributes(precisionTypeAttr()), formatTime()),
+		specutil.TypeSpec(TypeTimeTZ, specutil.WithAttributes(precisionTypeAttr()), formatTime()),
+		specutil.TypeSpec(TypeTimestampTZ, specutil.WithAttributes(precisionTypeAttr()), formatTime()),
+		specutil.TypeSpec(TypeTimestamp, specutil.WithAttributes(precisionTypeAttr()), formatTime()),
 		specutil.AliasTypeSpec("double_precision", TypeDouble),
 		specutil.TypeSpec(TypeReal),
 		specutil.TypeSpec(TypeFloat8),
@@ -467,19 +444,6 @@ func precisionTypeAttr() *schemaspec.TypeAttr {
 	}
 }
 
-func timePrinter(typ *schemaspec.Type) (string, error) {
-	a, ok := attr(typ, "precision")
-	if !ok {
-		return typ.T, nil
-	}
-	p, err := a.Int()
-	if err != nil {
-		return "", fmt.Errorf(`postgres: parsing attribute "precision": %w`, err)
-	}
-	parts := strings.Split(typ.T, " ")
-	return fmt.Sprintf("%s(%d)%s", parts[0], p, strings.Join(parts[1:], " ")), nil
-}
-
 func attr(typ *schemaspec.Type, key string) (*schemaspec.Attr, bool) {
 	for _, a := range typ.Attrs {
 		if a.K == key {
@@ -488,3 +452,33 @@ func attr(typ *schemaspec.Type, key string) (*schemaspec.Attr, bool) {
 	}
 	return nil, false
 }
+
+func typeSpec(t schema.Type) (*schemaspec.Type, error) {
+	if t, ok := t.(*schema.TimeType); ok && t.T != TypeDate {
+		spec := &schemaspec.Type{T: timeAlias(t.T)}
+		if t.Precision != defaultTimePrecision {
+			spec.Attrs = []*schemaspec.Attr{specutil.LitAttr("precision", strconv.Itoa(t.Precision))}
+		}
+		return spec, nil
+	}
+	s, err := FormatType(t)
+	if err != nil {
+		return nil, err
+	}
+	return &schemaspec.Type{T: s}, nil
+}
+
+// formatTime overrides the default printing logic done by schemahcl.hclType.
+func formatTime() specutil.TypeSpecOption {
+	return specutil.WithTypeFormatter(func(t *schemaspec.Type) (string, error) {
+		a, ok := attr(t, "precision")
+		if !ok {
+			return t.T, nil
+		}
+		p, err := a.Int()
+		if err != nil {
+			return "", fmt.Errorf(`postgres: parsing attribute "precision": %w`, err)
+		}
+		return FormatType(&schema.TimeType{T: t.T, Precision: p})
+	})
+}


Test patch:
diff --git a/internal/integration/testdata/postgres/column-time-precision.txt b/internal/integration/testdata/postgres/column-time-precision.txt
index d7894066..e4f06572 100644
--- a/internal/integration/testdata/postgres/column-time-precision.txt
+++ b/internal/integration/testdata/postgres/column-time-precision.txt
@@ -4,6 +4,9 @@ apply 1.hcl
 # Compare the result of "\d tbl" with the content of a file named '1.sql'.
 cmpshow tbl 1.sql
 
+apply 2.hcl
+cmpshow tbl 2.sql
+
 # Files
 -- 1.hcl --
 schema "$db" {}
@@ -22,7 +25,7 @@ table "tbl" {
   }
   column "timestamptz_4" {
     null    = false
-    type    = timestamp_with_time_zone(4)
+    type    = timestamptz(4)
     default = sql("CURRENT_TIMESTAMP(4)")
   }
 }
@@ -34,3 +37,55 @@ table "tbl" {
  precision_default | timestamp without time zone    |           | not null | CURRENT_TIMESTAMP
  timestamp_4       | timestamp(4) without time zone |           | not null | CURRENT_TIMESTAMP(4)
  timestamptz_4     | timestamp(4) with time zone    |           | not null | CURRENT_TIMESTAMP(4)
+
+-- 2.hcl --
+schema "$db" {}
+
+table "tbl" {
+  schema = schema.$db
+  column "c1" {
+    type = timestamptz(1)
+  }
+  column "c2" {
+    type = timestamptz
+  }
+  column "c3" {
+    type = timestamptz(0)
+  }
+  column "c4" {
+    type = time
+  }
+  column "c5" {
+    type = time(1)
+  }
+  column "c6" {
+    type = timestamp
+  }
+  column "c7" {
+    type = timestamp(5)
+  }
+  column "c8" {
+    type = timetz(0)
+  }
+  column "c9" {
+    type = timetz
+  }
+  column "c10" {
+    type = timetz(6)
+  }
+}
+
+-- 2.sql --
+                 Table "script_column_time_precision.tbl"
+ Column |              Type              | Collation | Nullable | Default
+--------+--------------------------------+-----------+----------+---------
+ c1     | timestamp(1) with time zone    |           | not null |
+ c2     | timestamp with time zone       |           | not null |
+ c3     | timestamp(0) with time zone    |           | not null |
+ c4     | time without time zone         |           | not null |
+ c5     | time(1) without time zone      |           | not null |
+ c6     | timestamp without time zone    |           | not null |
+ c7     | timestamp(5) without time zone |           | not null |
+ c8     | time(0) with time zone         |           | not null |
+ c9     | time with time zone            |           | not null |
+ c10    | time with time zone            |           | not null |
diff --git a/internal/integration/testdata/postgres/index-issue-557.txt b/internal/integration/testdata/postgres/index-issue-557.txt
index 51f5fc64..e53350d6 100644
--- a/internal/integration/testdata/postgres/index-issue-557.txt
+++ b/internal/integration/testdata/postgres/index-issue-557.txt
@@ -12,7 +12,7 @@ table "t1" {
   }
   column "b" {
     null = true
-    type = timestamp_without_time_zone(6)
+    type = timestamp(6)
   }
   index "t1_a_b" {
     on {
diff --git a/sql/internal/specutil/types_test.go b/sql/internal/specutil/types_test.go
index ed9bae7a..98ee81a2 100644
--- a/sql/internal/specutil/types_test.go
+++ b/sql/internal/specutil/types_test.go
@@ -1,7 +1,6 @@
 package specutil
 
 import (
-	"fmt"
 	"reflect"
 	"testing"
 
@@ -54,21 +53,6 @@ func TestTypePrint(t *testing.T) {
 			typ:      &schemaspec.Type{T: "varchar", Attrs: []*schemaspec.Attr{LitAttr("size", "255")}},
 			expected: "varchar(255)",
 		},
-		{
-			spec: &schemaspec.TypeSpec{
-				T:    "custom",
-				Name: "custom",
-				Attributes: []*schemaspec.TypeAttr{
-					{Name: "customAttr", Kind: reflect.Int, Required: true},
-				},
-				Printer: func(typ *schemaspec.Type) (string, error) {
-					v, _ := typ.Attrs[0].Int()
-					return fmt.Sprintf("%s(%d) and stuff", typ.T, v), nil
-				},
-			},
-			typ:      &schemaspec.Type{T: "custom", Attrs: []*schemaspec.Attr{LitAttr("customAttr", "3")}},
-			expected: "custom(3) and stuff",
-		},
 	} {
 		t.Run(tt.expected, func(t *testing.T) {
 			r := &TypeRegistry{}
diff --git a/sql/postgres/sqlspec_test.go b/sql/postgres/sqlspec_test.go
index 9fd6bba4..ea136cc7 100644
--- a/sql/postgres/sqlspec_test.go
+++ b/sql/postgres/sqlspec_test.go
@@ -410,12 +410,10 @@ func TestMarshalSpec_TimePrecision(t *testing.T) {
 			schema.NewTable("times").
 				AddColumns(
 					schema.NewTimeColumn("t_time_def", TypeTime),
-					schema.NewTimeColumn("t_time_with_time_zone", TypeTimeWTZ, schema.TimePrecision(2)),
-					schema.NewTimeColumn("t_time_without_time_zone", TypeTimeWOTZ, schema.TimePrecision(2)),
+					schema.NewTimeColumn("t_time_with_time_zone", TypeTimeTZ, schema.TimePrecision(2)),
+					schema.NewTimeColumn("t_time_without_time_zone", TypeTime, schema.TimePrecision(2)),
 					schema.NewTimeColumn("t_timestamp", TypeTimestamp, schema.TimePrecision(2)),
 					schema.NewTimeColumn("t_timestamptz", TypeTimestampTZ, schema.TimePrecision(2)),
-					schema.NewTimeColumn("t_timestamp_with_time_zone", TypeTimestampWTZ, schema.TimePrecision(2)),
-					schema.NewTimeColumn("t_timestamp_without_time_zone", TypeTimestampWOTZ, schema.TimePrecision(2)),
 				),
 		)
 	buf, err := MarshalSpec(s, hclState)
@@ -428,11 +426,11 @@ func TestMarshalSpec_TimePrecision(t *testing.T) {
   }
   column "t_time_with_time_zone" {
     null = false
-    type = time_with_time_zone(2)
+    type = timetz(2)
   }
   column "t_time_without_time_zone" {
     null = false
-    type = time_without_time_zone(2)
+    type = time(2)
   }
   column "t_timestamp" {
     null = false
@@ -442,14 +440,6 @@ func TestMarshalSpec_TimePrecision(t *testing.T) {
     null = false
     type = timestamptz(2)
   }
-  column "t_timestamp_with_time_zone" {
-    null = false
-    type = timestamp_with_time_zone(2)
-  }
-  column "t_timestamp_without_time_zone" {
-    null = false
-    type = timestamp_without_time_zone(2)
-  }
 }
 schema "test" {
 }
@@ -584,20 +574,8 @@ func TestTypes(t *testing.T) {
 			expected: &schema.TimeType{T: TypeTime, Precision: 4},
 		},
 		{
-			typeExpr: "time_with_time_zone",
-			expected: &schema.TimeType{T: TypeTimeWTZ, Precision: 6},
-		},
-		{
-			typeExpr: "time_with_time_zone(4)",
-			expected: &schema.TimeType{T: TypeTimeWTZ, Precision: 4},
-		},
-		{
-			typeExpr: "time_without_time_zone",
-			expected: &schema.TimeType{T: TypeTimeWOTZ, Precision: 6},
-		},
-		{
-			typeExpr: "time_without_time_zone(4)",
-			expected: &schema.TimeType{T: TypeTimeWOTZ, Precision: 4},
+			typeExpr: "timetz",
+			expected: &schema.TimeType{T: TypeTimeTZ, Precision: 6},
 		},
 		{
 			typeExpr: "timestamp",
@@ -615,22 +593,6 @@ func TestTypes(t *testing.T) {
 			typeExpr: "timestamptz(4)",
 			expected: &schema.TimeType{T: TypeTimestampTZ, Precision: 4},
 		},
-		{
-			typeExpr: "timestamp_with_time_zone",
-			expected: &schema.TimeType{T: TypeTimestampWTZ, Precision: 6},
-		},
-		{
-			typeExpr: "timestamp_with_time_zone(4)",
-			expected: &schema.TimeType{T: TypeTimestampWTZ, Precision: 4},
-		},
-		{
-			typeExpr: "timestamp_without_time_zone",
-			expected: &schema.TimeType{T: TypeTimestampWOTZ, Precision: 6},
-		},
-		{
-			typeExpr: "timestamp_without_time_zone(4)",
-			expected: &schema.TimeType{T: TypeTimestampWOTZ, Precision: 4},
-		},
 		{
 			typeExpr: "real",
 			expected: &schema.FloatType{T: TypeReal, Precision: 24},
@@ -675,7 +637,6 @@ func TestTypes(t *testing.T) {
 			typeExpr: "serial8",
 			expected: &SerialType{T: TypeSerial8},
 		},
-
 		{
 			typeExpr: "xml",
 			expected: &XMLType{T: TypeXML},
@@ -688,6 +649,10 @@ func TestTypes(t *testing.T) {
 			typeExpr: "jsonb",
 			expected: &schema.JSONType{T: TypeJSONB},
 		},
+		{
+			typeExpr: "timestamptz(3)",
+			expected: &schema.TimeType{T: TypeTimestampTZ, Precision: 3},
+		},
 		{
 			typeExpr: "uuid",
 			expected: &UUIDType{T: TypeUUID},