github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/sem/tree/interval_test.go (about)

     1  // Copyright 2017 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package tree
    12  
    13  import (
    14  	"testing"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    17  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    18  )
    19  
    20  var (
    21  	dayToHourITM = types.IntervalTypeMetadata{
    22  		DurationField: types.IntervalDurationField{
    23  			FromDurationType: types.IntervalDurationType_DAY,
    24  			DurationType:     types.IntervalDurationType_HOUR,
    25  		},
    26  	}
    27  	minuteToSecondITM = types.IntervalTypeMetadata{
    28  		DurationField: types.IntervalDurationField{
    29  			FromDurationType: types.IntervalDurationType_MINUTE,
    30  			DurationType:     types.IntervalDurationType_SECOND,
    31  		},
    32  	}
    33  )
    34  
    35  func TestValidSQLIntervalSyntax(t *testing.T) {
    36  	defer leaktest.AfterTest(t)()
    37  	testData := []struct {
    38  		input  string
    39  		itm    types.IntervalTypeMetadata
    40  		output string
    41  	}{
    42  		{`0:1`, types.IntervalTypeMetadata{}, `00:01:00`},
    43  		{`0:1.0`, types.IntervalTypeMetadata{}, `00:00:01`},
    44  		{`1`, types.IntervalTypeMetadata{}, `00:00:01`},
    45  		{`1.0:0:0`, types.IntervalTypeMetadata{}, `1 day`},
    46  		{`1.2`, types.IntervalTypeMetadata{}, `00:00:01.2`},
    47  		{`:3:4`, types.IntervalTypeMetadata{}, `03:04:00`},
    48  		{`:-3:4`, types.IntervalTypeMetadata{}, `-03:04:00`},
    49  		{`:3:4.1`, types.IntervalTypeMetadata{}, `00:03:04.1`},
    50  		{`1.2:1:1.2`, types.IntervalTypeMetadata{}, `1 day 04:49:01.2`},
    51  		{`1.2:+1:1.2`, types.IntervalTypeMetadata{}, `1 day 04:49:01.2`},
    52  		{`1.2:-1:1.2`, types.IntervalTypeMetadata{}, `1 day 04:46:58.8`},
    53  		{`1:0:0`, types.IntervalTypeMetadata{}, `01:00:00`},
    54  		{`1:1.2`, types.IntervalTypeMetadata{}, `00:01:01.2`},
    55  		{`1:2`, types.IntervalTypeMetadata{}, `01:02:00`},
    56  		{`1:2.3`, types.IntervalTypeMetadata{}, `00:01:02.3`},
    57  		{`1:2:3`, types.IntervalTypeMetadata{}, `01:02:03`},
    58  		{`1234:56:54`, types.IntervalTypeMetadata{}, `1234:56:54`},
    59  		{`-0:1`, types.IntervalTypeMetadata{}, `-00:01:00`},
    60  		{`-0:1.0`, types.IntervalTypeMetadata{}, `-00:00:01`},
    61  		{`-1`, types.IntervalTypeMetadata{}, `-00:00:01`},
    62  		{`-1.2`, types.IntervalTypeMetadata{}, `-00:00:01.2`},
    63  		{`-1:0:0`, types.IntervalTypeMetadata{}, `-01:00:00`},
    64  		{`-1:1.2`, types.IntervalTypeMetadata{}, `-00:01:01.2`},
    65  		{`-1:2`, types.IntervalTypeMetadata{}, `-01:02:00`},
    66  		{`-1:2.3`, types.IntervalTypeMetadata{}, `-00:01:02.3`},
    67  		{`-1:2:3`, types.IntervalTypeMetadata{}, `-01:02:03`},
    68  		{`-1234:56:54`, types.IntervalTypeMetadata{}, `-1234:56:54`},
    69  		{`1-2`, types.IntervalTypeMetadata{}, `1 year 2 mons`},
    70  		{`-1-2`, types.IntervalTypeMetadata{}, `-1 years -2 mons`},
    71  		{`1-2 3`, types.IntervalTypeMetadata{}, `1 year 2 mons 00:00:03`},
    72  		{`1-2 3`, types.IntervalTypeMetadata{
    73  			DurationField: types.IntervalDurationField{
    74  				DurationType: types.IntervalDurationType_YEAR,
    75  			},
    76  		}, `4 years 2 mons`}, // this gets truncated later to 4 years
    77  		{`1-2 3`, types.IntervalTypeMetadata{
    78  			DurationField: types.IntervalDurationField{
    79  				DurationType: types.IntervalDurationType_MONTH,
    80  			},
    81  		}, `1 year 5 mons`},
    82  		{`1-2 3`, types.IntervalTypeMetadata{
    83  			DurationField: types.IntervalDurationField{
    84  				DurationType: types.IntervalDurationType_DAY,
    85  			},
    86  		}, `1 year 2 mons 3 days`},
    87  		{`1-2 3`, types.IntervalTypeMetadata{
    88  			DurationField: types.IntervalDurationField{
    89  				DurationType: types.IntervalDurationType_HOUR,
    90  			},
    91  		}, `1 year 2 mons 03:00:00`},
    92  		{`1-2 3`, types.IntervalTypeMetadata{
    93  			DurationField: types.IntervalDurationField{
    94  				DurationType: types.IntervalDurationType_MINUTE,
    95  			},
    96  		}, `1 year 2 mons 00:03:00`},
    97  		{`1-2 3`, types.IntervalTypeMetadata{
    98  			DurationField: types.IntervalDurationField{
    99  				DurationType: types.IntervalDurationType_SECOND,
   100  			},
   101  		}, `1 year 2 mons 00:00:03`},
   102  		{`1-2 -3`, types.IntervalTypeMetadata{}, `1 year 2 mons -00:00:03`},
   103  		{`-1-2 -3`, types.IntervalTypeMetadata{}, `-1 years -2 mons -00:00:03`},
   104  		{`2 4:08`, types.IntervalTypeMetadata{}, `2 days 04:08:00`},
   105  		{`2.5 4:08`, types.IntervalTypeMetadata{}, `2 days 16:08:00`},
   106  		{`-2 4:08`, types.IntervalTypeMetadata{}, `-2 days +04:08:00`},
   107  		{`2 -4:08`, types.IntervalTypeMetadata{}, `2 days -04:08:00`},
   108  		{`2 -4:08.1234`, types.IntervalTypeMetadata{}, `2 days -00:04:08.1234`},
   109  		{`2 -4:08.1234`, minuteToSecondITM, `2 days -00:04:08.1234`},
   110  		{`2 -4:08`, minuteToSecondITM, `2 days -00:04:08`},
   111  		{`1-2 4:08`, types.IntervalTypeMetadata{}, `1 year 2 mons 04:08:00`},
   112  		{`1-2 3 4:08`, types.IntervalTypeMetadata{}, `1 year 2 mons 3 days 04:08:00`},
   113  		{`1-2 3 4:08:05`, types.IntervalTypeMetadata{}, `1 year 2 mons 3 days 04:08:05`},
   114  		{`1-2 4:08:23`, types.IntervalTypeMetadata{}, `1 year 2 mons 04:08:23`},
   115  		{`1- 4:08:23`, types.IntervalTypeMetadata{}, `1 year 04:08:23`},
   116  		{`0-2 3 4:08`, types.IntervalTypeMetadata{}, `2 mons 3 days 04:08:00`},
   117  		{`1- 3 4:08:`, types.IntervalTypeMetadata{}, `1 year 3 days 04:08:00`},
   118  		{`-1- 3 4:08:`, types.IntervalTypeMetadata{}, `-1 years 3 days +04:08:00`},
   119  		{`0- 3 4:08`, types.IntervalTypeMetadata{}, `3 days 04:08:00`},
   120  		{`-0- 3 4:08`, types.IntervalTypeMetadata{}, `3 days 04:08:00`},
   121  		{`-0- -0 4:08`, types.IntervalTypeMetadata{}, `04:08:00`},
   122  		{`-0- -0 0:0`, types.IntervalTypeMetadata{}, `00:00:00`},
   123  		{`-0- -0 -0:0`, types.IntervalTypeMetadata{}, `00:00:00`},
   124  		{`-0- -3 -4:08`, types.IntervalTypeMetadata{}, `-3 days -04:08:00`},
   125  		{`0- 3 4::08`, types.IntervalTypeMetadata{}, `3 days 04:00:08`},
   126  		{`	0-   3    4::08  `, types.IntervalTypeMetadata{}, `3 days 04:00:08`},
   127  		{`2 4:08:23`, types.IntervalTypeMetadata{}, `2 days 04:08:23`},
   128  		{`1-2 3 4:08:23`, types.IntervalTypeMetadata{}, `1 year 2 mons 3 days 04:08:23`},
   129  		{`1-`, types.IntervalTypeMetadata{}, `1 year`},
   130  		{`1- 2`, types.IntervalTypeMetadata{}, `1 year 00:00:02`},
   131  		{`2 3:`, types.IntervalTypeMetadata{}, `2 days 03:00:00`},
   132  		{`2 3:4:`, types.IntervalTypeMetadata{}, `2 days 03:04:00`},
   133  		{`1- 3:`, types.IntervalTypeMetadata{}, `1 year 03:00:00`},
   134  		{`1- 3:4`, types.IntervalTypeMetadata{}, `1 year 03:04:00`},
   135  
   136  		{`2 3`, dayToHourITM, `2 days 03:00:00`},
   137  		{`-2 -3`, dayToHourITM, `-2 days -03:00:00`},
   138  		{`-2 3`, dayToHourITM, `-2 days +03:00:00`},
   139  		{`2 -3`, dayToHourITM, `2 days -03:00:00`},
   140  		{`1-2 3`, dayToHourITM, `1 year 2 mons 03:00:00`},
   141  		{`-1-2 -3`, dayToHourITM, `-1 years -2 mons -03:00:00`},
   142  		{`-1-2 3`, dayToHourITM, `-1 years -2 mons +03:00:00`},
   143  		{`1-2 -3`, dayToHourITM, `1 year 2 mons -03:00:00`},
   144  	}
   145  	for _, test := range testData {
   146  		t.Run(test.input, func(t *testing.T) {
   147  			dur, err := sqlStdToDuration(test.input, test.itm)
   148  			if err != nil {
   149  				t.Fatalf("%q: %v", test.input, err)
   150  			}
   151  			s := dur.String()
   152  			if s != test.output {
   153  				t.Fatalf(`%q: got "%s", expected "%s"`, test.input, s, test.output)
   154  			}
   155  
   156  			dur2, err := parseDuration(s, test.itm)
   157  			if err != nil {
   158  				t.Fatalf(`%q: repr "%s" is not parsable: %v`, test.input, s, err)
   159  			}
   160  			s2 := dur2.String()
   161  			if s2 != s {
   162  				t.Fatalf(`%q: repr "%s" does not round-trip, got "%s" instead`,
   163  					test.input, s, s2)
   164  			}
   165  
   166  			// Test that a Datum recognizes the format.
   167  			di, err := parseDInterval(test.input, test.itm)
   168  			if err != nil {
   169  				t.Fatalf(`%q: unrecognized as datum: %v`, test.input, err)
   170  			}
   171  			s3 := di.Duration.String()
   172  			if s3 != test.output {
   173  				t.Fatalf(`%q: as datum, got "%s", expected "%s"`, test.input, s3, test.output)
   174  			}
   175  		})
   176  	}
   177  }
   178  
   179  func TestInvalidSQLIntervalSyntax(t *testing.T) {
   180  	defer leaktest.AfterTest(t)()
   181  	testData := []struct {
   182  		input  string
   183  		output string
   184  		error  string
   185  	}{
   186  		{`+`, ``, `invalid input syntax for type interval +`},
   187  		{`++`, ``, `invalid input syntax for type interval ++`},
   188  		{`--`, ``, `invalid input syntax for type interval --`},
   189  		{`{1,2}`, ``, `invalid input syntax for type interval {1,2}`},
   190  		{`0.000,0`, ``, `invalid input syntax for type interval 0.000,0`},
   191  		{`0,0`, ``, `invalid input syntax for type interval 0,0`},
   192  		{`2 3`, ``, `invalid input syntax for type interval 2 3`},
   193  		{`-2 3`, ``, `invalid input syntax for type interval -2 3`},
   194  		{`-2 -3`, ``, `invalid input syntax for type interval -2 -3`},
   195  		{`2 -3`, ``, `invalid input syntax for type interval 2 -3`},
   196  		{`0:-1`, ``, `invalid input syntax for type interval 0:-1`},
   197  		{`0:0:-1`, ``, `invalid input syntax for type interval 0:0:-1`},
   198  		{`1.0:0:-1`, ``, `invalid input syntax for type interval 1.0:0:-1`},
   199  		{`0:-1:0`, ``, `invalid input syntax for type interval 0:-1:0`},
   200  		{`0:-1:-1`, ``, `invalid input syntax for type interval 0:-1:-1`},
   201  		{`-1.0:0:0`, ``, `invalid input syntax for type interval -1.0:0:0`},
   202  		{`-:0:0`, ``, `invalid input syntax for type interval -:0:0`},
   203  	}
   204  	for i, test := range testData {
   205  		dur, err := sqlStdToDuration(test.input, types.IntervalTypeMetadata{})
   206  		if err != nil {
   207  			if test.error != "" {
   208  				if err.Error() != test.error {
   209  					t.Errorf(`%d: %q: got error "%v", expected "%s"`, i, test.input, err, test.error)
   210  				}
   211  			} else {
   212  				t.Errorf("%d: %q: %v", i, test.input, err)
   213  			}
   214  			continue
   215  		} else {
   216  			if test.error != "" {
   217  				t.Errorf(`%d: %q: expected error "%q"`, i, test.input, test.error)
   218  				continue
   219  			}
   220  		}
   221  		s := dur.String()
   222  		if s != test.output {
   223  			t.Errorf(`%d: %q: got "%s", expected "%s"`, i, test.input, s, test.output)
   224  			continue
   225  		}
   226  
   227  		dur2, err := parseDuration(s, types.IntervalTypeMetadata{})
   228  		if err != nil {
   229  			t.Errorf(`%d: %q: repr "%s" is not parsable: %v`, i, test.input, s, err)
   230  			continue
   231  		}
   232  		s2 := dur2.String()
   233  		if s2 != s {
   234  			t.Errorf(`%d: %q: repr "%s" does not round-trip, got "%s" instead`,
   235  				i, test.input, s, s2)
   236  		}
   237  
   238  		// Test that a Datum recognizes the format.
   239  		di, err := parseDInterval(test.input, types.IntervalTypeMetadata{})
   240  		if err != nil {
   241  			t.Errorf(`%d: %q: unrecognized as datum: %v`, i, test.input, err)
   242  			continue
   243  		}
   244  		s3 := di.Duration.String()
   245  		if s3 != test.output {
   246  			t.Errorf(`%d: %q: as datum, got "%s", expected "%s"`, i, test.input, s3, test.output)
   247  		}
   248  	}
   249  }
   250  
   251  func TestPGIntervalSyntax(t *testing.T) {
   252  	defer leaktest.AfterTest(t)()
   253  	testData := []struct {
   254  		input  string
   255  		itm    types.IntervalTypeMetadata
   256  		output string
   257  		error  string
   258  	}{
   259  		{``, types.IntervalTypeMetadata{}, ``, `interval: invalid input syntax: ""`},
   260  		{`-`, types.IntervalTypeMetadata{}, ``, `interval: missing unit at position 1: "-"`},
   261  		{`123`, types.IntervalTypeMetadata{}, ``, `interval: missing unit at position 3: "123"`},
   262  		{`123blah`, types.IntervalTypeMetadata{}, ``, `interval: unknown unit "blah" in duration "123blah"`},
   263  
   264  		{`500nanoseconds`, types.IntervalTypeMetadata{}, ``, `interval: unknown unit "nanoseconds" in duration "500nanoseconds"`},
   265  		{`500ns`, types.IntervalTypeMetadata{}, ``, `interval: unknown unit "ns" in duration "500ns"`},
   266  
   267  		// ns/us boundary
   268  		{`.5us`, types.IntervalTypeMetadata{}, `00:00:00.000001`, ``},
   269  		{`-0.499us`, types.IntervalTypeMetadata{}, `00:00:00`, ``},
   270  		{`-0.5us`, types.IntervalTypeMetadata{}, `-00:00:00.000001`, ``},
   271  		{`0.000000499s`, types.IntervalTypeMetadata{}, `00:00:00`, ``},
   272  		{`0.0000005s`, types.IntervalTypeMetadata{}, `00:00:00.000001`, ``},
   273  		{`-0.000000499s`, types.IntervalTypeMetadata{}, `00:00:00`, ``},
   274  		{`-0.0000005s`, types.IntervalTypeMetadata{}, `-00:00:00.000001`, ``},
   275  
   276  		{`1.2 microsecond`, types.IntervalTypeMetadata{}, `00:00:00.000001`, ``},
   277  		{`1.2microseconds`, types.IntervalTypeMetadata{}, `00:00:00.000001`, ``},
   278  		{`1.2us`, types.IntervalTypeMetadata{}, `00:00:00.000001`, ``},
   279  		// µ = U+00B5 = micro symbol
   280  		// μ = U+03BC = Greek letter mu
   281  		{`1.2µs`, types.IntervalTypeMetadata{}, `00:00:00.000001`, ``},
   282  		{`1.2μs`, types.IntervalTypeMetadata{}, `00:00:00.000001`, ``},
   283  		{`1.2usec`, types.IntervalTypeMetadata{}, `00:00:00.000001`, ``},
   284  		{`1.2usecs`, types.IntervalTypeMetadata{}, `00:00:00.000001`, ``},
   285  		{`1.2usecond`, types.IntervalTypeMetadata{}, `00:00:00.000001`, ``},
   286  		{`1.2useconds`, types.IntervalTypeMetadata{}, `00:00:00.000001`, ``},
   287  		{`0.23us`, types.IntervalTypeMetadata{}, `00:00:00`, ``},
   288  		{`-0.23us`, types.IntervalTypeMetadata{}, `00:00:00`, ``},
   289  		{`0.2346us`, types.IntervalTypeMetadata{}, `00:00:00`, ``},
   290  		{`-1.2us`, types.IntervalTypeMetadata{}, `-00:00:00.000001`, ``},
   291  
   292  		{`1.2millisecond`, types.IntervalTypeMetadata{}, `00:00:00.0012`, ``},
   293  		{`1.2milliseconds`, types.IntervalTypeMetadata{}, `00:00:00.0012`, ``},
   294  		{`1.2ms`, types.IntervalTypeMetadata{}, `00:00:00.0012`, ``},
   295  		{`1.2msec`, types.IntervalTypeMetadata{}, `00:00:00.0012`, ``},
   296  		{`1.2msecs`, types.IntervalTypeMetadata{}, `00:00:00.0012`, ``},
   297  		{`1.2msecond`, types.IntervalTypeMetadata{}, `00:00:00.0012`, ``},
   298  		{`1.2mseconds`, types.IntervalTypeMetadata{}, `00:00:00.0012`, ``},
   299  		{`0.2304506ms`, types.IntervalTypeMetadata{}, `00:00:00.00023`, ``},
   300  		{`0.0002304506ms`, types.IntervalTypeMetadata{}, `00:00:00`, ``},
   301  
   302  		{`1.2second`, types.IntervalTypeMetadata{}, `00:00:01.2`, ``},
   303  		{`1.2seconds`, types.IntervalTypeMetadata{}, `00:00:01.2`, ``},
   304  		{`1.2s`, types.IntervalTypeMetadata{}, `00:00:01.2`, ``},
   305  		{`1.2sec`, types.IntervalTypeMetadata{}, `00:00:01.2`, ``},
   306  		{`1.2secs`, types.IntervalTypeMetadata{}, `00:00:01.2`, ``},
   307  		{`0.2304506708s`, types.IntervalTypeMetadata{}, `00:00:00.230451`, ``},
   308  		{`0.0002304506708s`, types.IntervalTypeMetadata{}, `00:00:00.00023`, ``},
   309  		{`0.0000002304506s`, types.IntervalTypeMetadata{}, `00:00:00`, ``},
   310  		{`75.5s`, types.IntervalTypeMetadata{}, `00:01:15.5`, ``},
   311  		{`3675.5s`, types.IntervalTypeMetadata{}, `01:01:15.5`, ``},
   312  		{`86475.5s`, types.IntervalTypeMetadata{}, `24:01:15.5`, ``},
   313  		{`86400s -60000ms 100us`, types.IntervalTypeMetadata{}, `23:59:00.0001`, ``},
   314  
   315  		{`1.2minute`, types.IntervalTypeMetadata{}, `00:01:12`, ``},
   316  		{`1.2minutes`, types.IntervalTypeMetadata{}, `00:01:12`, ``},
   317  		{`1.2m`, types.IntervalTypeMetadata{}, `00:01:12`, ``},
   318  		{`1.2min`, types.IntervalTypeMetadata{}, `00:01:12`, ``},
   319  		{`1.2mins`, types.IntervalTypeMetadata{}, `00:01:12`, ``},
   320  		{`1.2m 8s`, types.IntervalTypeMetadata{}, `00:01:20`, ``},
   321  		{`0.5m`, types.IntervalTypeMetadata{}, `00:00:30`, ``},
   322  		{`120.5m`, types.IntervalTypeMetadata{}, `02:00:30`, ``},
   323  		{`0.23045067089m`, types.IntervalTypeMetadata{}, `00:00:13.82704`, ``},
   324  		{`-0.23045067089m`, types.IntervalTypeMetadata{}, `-00:00:13.82704`, ``},
   325  
   326  		{`1.2hour`, types.IntervalTypeMetadata{}, `01:12:00`, ``},
   327  		{`1.2hours`, types.IntervalTypeMetadata{}, `01:12:00`, ``},
   328  		{`1.2h`, types.IntervalTypeMetadata{}, `01:12:00`, ``},
   329  		{`1.2hr`, types.IntervalTypeMetadata{}, `01:12:00`, ``},
   330  		{`1.2hrs`, types.IntervalTypeMetadata{}, `01:12:00`, ``},
   331  		{`1.2h 8m`, types.IntervalTypeMetadata{}, `01:20:00`, ``},
   332  		{`0.5h`, types.IntervalTypeMetadata{}, `00:30:00`, ``},
   333  		{`25.5h`, types.IntervalTypeMetadata{}, `25:30:00`, ``},
   334  		{`0.23045067089h`, types.IntervalTypeMetadata{}, `00:13:49.622415`, ``},
   335  		{`-0.23045067089h`, types.IntervalTypeMetadata{}, `-00:13:49.622415`, ``},
   336  
   337  		{`1 day`, types.IntervalTypeMetadata{}, `1 day`, ``},
   338  		{`1 days`, types.IntervalTypeMetadata{}, `1 day`, ``},
   339  		{`1d`, types.IntervalTypeMetadata{}, `1 day`, ``},
   340  		{`1.1d`, types.IntervalTypeMetadata{}, `1 day 02:24:00`, ``},
   341  		{`1.2d`, types.IntervalTypeMetadata{}, `1 day 04:48:00`, ``},
   342  		{`1.11d`, types.IntervalTypeMetadata{}, `1 day 02:38:24`, ``},
   343  		{`1.111d`, types.IntervalTypeMetadata{}, `1 day 02:39:50.4`, ``},
   344  		{`1.1111d`, types.IntervalTypeMetadata{}, `1 day 02:39:59.04`, ``},
   345  		{`60d 25h`, types.IntervalTypeMetadata{}, `60 days 25:00:00`, ``},
   346  		{`-9223372036854775808d`, types.IntervalTypeMetadata{}, `-9223372036854775808 days`, ``},
   347  		{`9223372036854775807d`, types.IntervalTypeMetadata{}, `9223372036854775807 days`, ``},
   348  
   349  		{`1week`, types.IntervalTypeMetadata{}, `7 days`, ``},
   350  		{`1weeks`, types.IntervalTypeMetadata{}, `7 days`, ``},
   351  		{`1w`, types.IntervalTypeMetadata{}, `7 days`, ``},
   352  		{`1.1w`, types.IntervalTypeMetadata{}, `7 days 16:48:00`, ``},
   353  		{`1.5w`, types.IntervalTypeMetadata{}, `10 days 12:00:00`, ``},
   354  		{`1w -1d`, types.IntervalTypeMetadata{}, `6 days`, ``},
   355  
   356  		{`1month`, types.IntervalTypeMetadata{}, `1 mon`, ``},
   357  		{`1months`, types.IntervalTypeMetadata{}, `1 mon`, ``},
   358  		{`1mons`, types.IntervalTypeMetadata{}, `1 mon`, ``},
   359  		{`1.5mon`, types.IntervalTypeMetadata{}, `1 mon 15 days`, ``},
   360  		{`1 mon 2 week`, types.IntervalTypeMetadata{}, `1 mon 14 days`, ``},
   361  		{`1.1mon`, types.IntervalTypeMetadata{}, `1 mon 3 days`, ``},
   362  		{`1.2mon`, types.IntervalTypeMetadata{}, `1 mon 6 days`, ``},
   363  		{`1.11mon`, types.IntervalTypeMetadata{}, `1 mon 3 days 07:12:00`, ``},
   364  		{`-9223372036854775808mon`, types.IntervalTypeMetadata{}, `-768614336404564650 years -8 mons`, ``},
   365  		{`9223372036854775807mon`, types.IntervalTypeMetadata{}, `768614336404564650 years 7 mons`, ``},
   366  
   367  		{`1year`, types.IntervalTypeMetadata{}, `1 year`, ``},
   368  		{`1years`, types.IntervalTypeMetadata{}, `1 year`, ``},
   369  		{`1y`, types.IntervalTypeMetadata{}, `1 year`, ``},
   370  		{`1yr`, types.IntervalTypeMetadata{}, `1 year`, ``},
   371  		{`1yrs`, types.IntervalTypeMetadata{}, `1 year`, ``},
   372  		{`1.5y`, types.IntervalTypeMetadata{}, `1 year 6 mons`, ``},
   373  		{`1.1y`, types.IntervalTypeMetadata{}, `1 year 1 mon 6 days`, ``},
   374  		{`1.11y`, types.IntervalTypeMetadata{}, `1 year 1 mon 9 days 14:24:00`, ``},
   375  
   376  		// Mixed unit/HH:MM:SS formats
   377  		{`1:2:3`, types.IntervalTypeMetadata{}, `01:02:03`, ``},
   378  		{`-1:2:3`, types.IntervalTypeMetadata{}, `-01:02:03`, ``},
   379  		{`-0:2:3`, types.IntervalTypeMetadata{}, `-00:02:03`, ``},
   380  		{`+0:2:3`, types.IntervalTypeMetadata{}, `00:02:03`, ``},
   381  		{`1 day 12:30`, types.IntervalTypeMetadata{}, `1 day 12:30:00`, ``},
   382  		{`12:30 1 day`, types.IntervalTypeMetadata{}, `1 day 12:30:00`, ``},
   383  		{`1 day -12:30`, types.IntervalTypeMetadata{}, `1 day -12:30:00`, ``},
   384  		{`1 day -00:30`, types.IntervalTypeMetadata{}, `1 day -00:30:00`, ``},
   385  		{`1 day -00:00:30`, types.IntervalTypeMetadata{}, `1 day -00:00:30`, ``},
   386  		{`-1 day +00:00:30`, types.IntervalTypeMetadata{}, `-1 days +00:00:30`, ``},
   387  		{`2 days -4:08.1234`, types.IntervalTypeMetadata{}, `2 days -00:04:08.1234`, ``},
   388  		{`2 days -4:08`, minuteToSecondITM, `2 days -00:04:08`, ``},
   389  		{`1 day 12:30.5`, types.IntervalTypeMetadata{}, `1 day 00:12:30.5`, ``},
   390  		{`1 day 12:30:40`, types.IntervalTypeMetadata{}, `1 day 12:30:40`, ``},
   391  		{`1 day 12:30:40.5`, types.IntervalTypeMetadata{}, `1 day 12:30:40.5`, ``},
   392  		{`1 day 12:30:40.500500001`, types.IntervalTypeMetadata{}, `1 day 12:30:40.5005`, ``},
   393  
   394  		// Regressions
   395  
   396  		// This was 1ns off due to float rounding.
   397  		{`50 years 6 mons 75 days 1572897:25:58.535696141`, types.IntervalTypeMetadata{}, `50 years 6 mons 75 days 1572897:25:58.535696`, ``},
   398  	}
   399  	for _, test := range testData {
   400  		t.Run(test.input, func(t *testing.T) {
   401  			dur, err := parseDuration(test.input, test.itm)
   402  			if err != nil {
   403  				if test.error != "" {
   404  					if err.Error() != test.error {
   405  						t.Fatalf(`%q: got error "%v", expected "%s"`, test.input, err, test.error)
   406  					}
   407  				} else {
   408  					t.Fatalf("%q: %v", test.input, err)
   409  				}
   410  				return
   411  			}
   412  			if test.error != "" {
   413  				t.Fatalf(`%q: expected error "%q"`, test.input, test.error)
   414  			}
   415  			s := dur.String()
   416  			if s != test.output {
   417  				t.Fatalf(`%q: got "%s", expected "%s"`, test.input, s, test.output)
   418  			}
   419  
   420  			dur2, err := parseDuration(s, test.itm)
   421  			if err != nil {
   422  				t.Fatalf(`%q: repr "%s" is not parsable: %v`, test.input, s, err)
   423  			}
   424  			s2 := dur2.String()
   425  			if s2 != s {
   426  				t.Fatalf(`%q: repr "%s" does not round-trip, got "%s" instead`, test.input, s, s2)
   427  			}
   428  
   429  			// Test that a Datum recognizes the format.
   430  			di, err := parseDInterval(test.input, test.itm)
   431  			if err != nil {
   432  				t.Fatalf(`%q: unrecognized as datum: %v`, test.input, err)
   433  			}
   434  			s3 := di.Duration.String()
   435  			if s3 != test.output {
   436  				t.Fatalf(`%q: as datum, got "%s", expected "%s"`, test.input, s3, test.output)
   437  			}
   438  		})
   439  	}
   440  }