github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/timetz/timetz_test.go (about)

     1  // Copyright 2019 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 timetz
    12  
    13  import (
    14  	"fmt"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/cockroachdb/cockroach/pkg/util/timeofday"
    19  	"github.com/cockroachdb/cockroach/pkg/util/timeutil"
    20  	"github.com/stretchr/testify/assert"
    21  	"github.com/stretchr/testify/require"
    22  )
    23  
    24  func TestParseTimeTZToStringRoundTrip(t *testing.T) {
    25  	testCases := []string{
    26  		"24:00:00-1559",
    27  		"11:12:13+05:06:07",
    28  		"10:11:12+0",
    29  		"10:11:12.05+0",
    30  	}
    31  	for _, tc := range testCases {
    32  		t.Run(tc, func(t *testing.T) {
    33  			exampleTime, err := ParseTimeTZ(timeutil.Now(), tc, time.Microsecond)
    34  			assert.NoError(t, err)
    35  
    36  			exampleTimeFromString, err := ParseTimeTZ(timeutil.Now(), exampleTime.String(), time.Microsecond)
    37  			assert.NoError(t, err)
    38  
    39  			assert.True(t, exampleTime.Equal(exampleTimeFromString))
    40  		})
    41  	}
    42  }
    43  
    44  func TestTimeTZString(t *testing.T) {
    45  	testCases := []struct {
    46  		input    TimeTZ
    47  		expected string
    48  	}{
    49  		{MakeTimeTZ(timeofday.New(0, 0, 0, 0), 0), "00:00:00+00:00:00"},
    50  		{MakeTimeTZ(timeofday.New(10, 11, 12, 0), 0), "10:11:12+00:00:00"},
    51  		{MakeTimeTZ(timeofday.New(10, 11, 12, 0), -30), "10:11:12+00:00:30"},
    52  		{MakeTimeTZ(timeofday.New(10, 11, 12, 0), 30), "10:11:12-00:00:30"},
    53  		{MakeTimeTZ(timeofday.New(10, 11, 12, 0), 120), "10:11:12-00:02:00"},
    54  		{MakeTimeTZ(timeofday.New(10, 11, 12, 0), 3), "10:11:12-00:00:03"},
    55  		{MakeTimeTZ(timeofday.Max-1, -10*60*60), "23:59:59.999999+10:00:00"},
    56  		{MakeTimeTZ(timeofday.Time2400, 10*60*60), "24:00:00-10:00:00"},
    57  	}
    58  	for i, tc := range testCases {
    59  		t.Run(fmt.Sprintf("%d:%s", i, tc.expected), func(t *testing.T) {
    60  			assert.Equal(t, tc.expected, tc.input.String())
    61  		})
    62  	}
    63  }
    64  
    65  func TestTimeTZ(t *testing.T) {
    66  	maxTime, err := ParseTimeTZ(timeutil.Now(), "24:00:00-1559", time.Microsecond)
    67  	require.NoError(t, err)
    68  	minTime, err := ParseTimeTZ(timeutil.Now(), "00:00:00+1559", time.Microsecond)
    69  	require.NoError(t, err)
    70  
    71  	// These are all the same UTC time equivalents.
    72  	utcTime, err := ParseTimeTZ(timeutil.Now(), "11:14:15+0", time.Microsecond)
    73  	require.NoError(t, err)
    74  	sydneyTime, err := ParseTimeTZ(timeutil.Now(), "21:14:15+10", time.Microsecond)
    75  	require.NoError(t, err)
    76  
    77  	sydneyTimeWithMillisecond, err := ParseTimeTZ(timeutil.Now(), "21:14:15.001+10", time.Microsecond)
    78  	require.NoError(t, err)
    79  
    80  	// No daylight savings in Hawaii!
    81  	hawaiiZone, err := time.LoadLocation("Pacific/Honolulu")
    82  	require.NoError(t, err)
    83  	hawaiiTime := MakeTimeTZFromLocation(timeofday.New(1, 14, 15, 0), hawaiiZone)
    84  
    85  	weirdTimeZone := MakeTimeTZ(timeofday.New(10, 0, 0, 0), -((5 * 60 * 60) + 30*60 + 15))
    86  
    87  	testCases := []struct {
    88  		t               TimeTZ
    89  		toTime          time.Time
    90  		toDuration      time.Duration
    91  		largerThan      []TimeTZ
    92  		smallerThan     []TimeTZ
    93  		equalTo         []TimeTZ
    94  		roundedToSecond TimeTZ
    95  	}{
    96  		{
    97  			t:               weirdTimeZone,
    98  			toTime:          time.Date(1970, 1, 1, 10, 0, 0, 0, timeutil.FixedOffsetTimeZoneToLocation((5*60*60)+(30*60)+15, "TimeTZ")),
    99  			toDuration:      time.Duration((10*60*60 - ((5 * 60 * 60) + 30*60 + 15))) * time.Second,
   100  			largerThan:      []TimeTZ{minTime},
   101  			smallerThan:     []TimeTZ{maxTime},
   102  			equalTo:         []TimeTZ{weirdTimeZone},
   103  			roundedToSecond: weirdTimeZone,
   104  		},
   105  		{
   106  			t:               utcTime,
   107  			toTime:          time.Date(1970, 1, 1, 11, 14, 15, 0, timeutil.FixedOffsetTimeZoneToLocation(0, "TimeTZ")),
   108  			toDuration:      time.Duration(11*60*60+14*60+15) * time.Second,
   109  			largerThan:      []TimeTZ{minTime, sydneyTime},
   110  			smallerThan:     []TimeTZ{maxTime, hawaiiTime},
   111  			equalTo:         []TimeTZ{utcTime},
   112  			roundedToSecond: utcTime,
   113  		},
   114  		{
   115  			t:               sydneyTime,
   116  			toTime:          time.Date(1970, 1, 1, 21, 14, 15, 0, timeutil.FixedOffsetTimeZoneToLocation(10*60*60, "TimeTZ")),
   117  			toDuration:      time.Duration(11*60*60+14*60+15) * time.Second,
   118  			largerThan:      []TimeTZ{minTime},
   119  			smallerThan:     []TimeTZ{maxTime, utcTime, hawaiiTime},
   120  			equalTo:         []TimeTZ{sydneyTime},
   121  			roundedToSecond: sydneyTime,
   122  		},
   123  		{
   124  			t:               sydneyTimeWithMillisecond,
   125  			toTime:          time.Date(1970, 1, 1, 21, 14, 15, 1000000, timeutil.FixedOffsetTimeZoneToLocation(10*60*60, "TimeTZ")),
   126  			toDuration:      time.Duration(11*60*60+14*60+15)*time.Second + 1*time.Millisecond,
   127  			largerThan:      []TimeTZ{minTime, utcTime, hawaiiTime, sydneyTime},
   128  			smallerThan:     []TimeTZ{maxTime},
   129  			equalTo:         []TimeTZ{sydneyTimeWithMillisecond},
   130  			roundedToSecond: sydneyTime,
   131  		},
   132  		{
   133  			t:               hawaiiTime,
   134  			toTime:          time.Date(1970, 1, 1, 1, 14, 15, 0, timeutil.FixedOffsetTimeZoneToLocation(-10*60*60, "TimeTZ")),
   135  			toDuration:      time.Duration(11*60*60+14*60+15) * time.Second,
   136  			largerThan:      []TimeTZ{minTime, utcTime, sydneyTime},
   137  			smallerThan:     []TimeTZ{maxTime},
   138  			equalTo:         []TimeTZ{hawaiiTime},
   139  			roundedToSecond: hawaiiTime,
   140  		},
   141  		{
   142  			t:               minTime,
   143  			toTime:          time.Date(1970, 1, 1, 0, 0, 0, 0, timeutil.FixedOffsetTimeZoneToLocation(15*60*60+59*60, "TimeTZ")),
   144  			toDuration:      time.Duration(-(15*60*60 + 59*60)) * time.Second,
   145  			largerThan:      []TimeTZ{},
   146  			smallerThan:     []TimeTZ{maxTime, utcTime, sydneyTime, hawaiiTime},
   147  			equalTo:         []TimeTZ{minTime},
   148  			roundedToSecond: minTime,
   149  		},
   150  		{
   151  			t:               maxTime,
   152  			toTime:          time.Date(1970, 1, 2, 0, 0, 0, 0, timeutil.FixedOffsetTimeZoneToLocation(-(15*60*60+59*60), "TimeTZ")),
   153  			toDuration:      time.Duration(24*60*60+15*60*60+59*60) * time.Second,
   154  			largerThan:      []TimeTZ{minTime, utcTime, sydneyTime, hawaiiTime},
   155  			smallerThan:     []TimeTZ{},
   156  			equalTo:         []TimeTZ{maxTime},
   157  			roundedToSecond: maxTime,
   158  		},
   159  	}
   160  	for i, tc := range testCases {
   161  		t.Run(fmt.Sprintf("#%d %s", i, tc.t.String()), func(t *testing.T) {
   162  			assert.Equal(t, tc.toTime, tc.t.ToTime())
   163  			assert.Equal(t, tc.roundedToSecond, tc.t.Round(time.Second))
   164  			assert.Equal(t, tc.toDuration, tc.t.ToDuration())
   165  
   166  			for _, largerThan := range tc.largerThan {
   167  				assert.True(t, tc.t.After(largerThan), "%s > %s", tc.t.String(), largerThan)
   168  			}
   169  
   170  			for _, smallerThan := range tc.smallerThan {
   171  				assert.True(t, tc.t.Before(smallerThan), "%s < %s", tc.t.String(), smallerThan)
   172  			}
   173  
   174  			for _, equalTo := range tc.equalTo {
   175  				assert.True(t, tc.t.Equal(equalTo), "%s = %s", tc.t.String(), equalTo)
   176  			}
   177  		})
   178  	}
   179  }
   180  
   181  func TestParseTimeTZ(t *testing.T) {
   182  	testCases := []struct {
   183  		str       string
   184  		precision time.Duration
   185  
   186  		expected      TimeTZ
   187  		expectedError bool
   188  	}{
   189  		{str: "01:02:03", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.New(1, 2, 3, 0), 0)},
   190  		{str: "01:02:03.000123", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.New(1, 2, 3, 123), 0)},
   191  		{str: "01:24:00", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.New(1, 24, 0, 0), 0)},
   192  		{str: "01:03:24", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.New(1, 3, 24, 0), 0)},
   193  		{str: "1970-01-01 01:02:03", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.New(1, 2, 3, 0), 0)},
   194  		{str: "1970-01-01T01:02:03", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.New(1, 2, 3, 0), 0)},
   195  		{str: "1970-01-01T01:02:03", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.New(1, 2, 3, 0), 0)},
   196  		{str: "0000-01-01  01:02:03", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.New(1, 2, 3, 0), 0)},
   197  		{str: "01:02:03.000123", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.New(1, 2, 3, 123), 0)},
   198  		{str: "4:5:6", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.New(4, 5, 6, 0), 0)},
   199  		{str: "24:00", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.Time2400, 0)},
   200  		{str: "24:00:00", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.Time2400, 0)},
   201  		{str: "24:00:00.000", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.Time2400, 0)},
   202  		{str: "24:00:00.000000", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.Time2400, 0)},
   203  		{str: "01:02:03+13", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.New(1, 2, 3, 0), -13*60*60)},
   204  		{str: "01:02:03-13", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.New(1, 2, 3, 0), 13*60*60)},
   205  		{str: "01:02:03+7", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.New(1, 2, 3, 0), -7*60*60)},
   206  		{str: "01:02:03-0730", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.New(1, 2, 3, 0), 7*60*60+30*60)},
   207  		{str: "24:00+3", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.Time2400, -3*60*60)},
   208  		{str: "24:00:00+4", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.Time2400, -4*60*60)},
   209  		{str: "24:00:00.000-5", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.Time2400, 5*60*60)},
   210  		{str: "24:00:00.000000+6", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.Time2400, -6*60*60)},
   211  		{str: "24:00:00.000000+6", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.Time2400, -6*60*60)},
   212  		{str: "1970-01-01T24:00:00.000000+6", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.Time2400, -6*60*60)},
   213  		{str: "00:00-1559", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.New(0, 0, 0, 0), MaxTimeTZOffsetSecs)},
   214  		{str: "00:00+1559", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.New(0, 0, 0, 0), MinTimeTZOffsetSecs)},
   215  		{str: " 01:03:24", precision: time.Microsecond, expected: MakeTimeTZ(timeofday.New(1, 3, 24, 0), 0)},
   216  
   217  		{str: "01:02:03.000123", precision: time.Millisecond, expected: MakeTimeTZ(timeofday.New(1, 2, 3, 0), 0)},
   218  		{str: "01:02:03.000123", precision: time.Millisecond / 10, expected: MakeTimeTZ(timeofday.New(1, 2, 3, 100), 0)},
   219  		{str: "01:02:03.500123", precision: time.Second, expected: MakeTimeTZ(timeofday.New(1, 2, 4, 0), 0)},
   220  
   221  		{str: "", expectedError: true},
   222  		{str: "foo", expectedError: true},
   223  		{str: "01", expectedError: true},
   224  		{str: "01:00=wat", expectedError: true},
   225  		{str: "00:00-1600", expectedError: true},
   226  		{str: "00:00+1600", expectedError: true},
   227  		{str: "00:00+24:00", expectedError: true},
   228  		{str: "1970-01-01 00:00+24:00", expectedError: true},
   229  		{str: "2010-09-28", expectedError: true},
   230  	}
   231  	for i, tc := range testCases {
   232  		t.Run(fmt.Sprintf("#%d: %s", i, tc.str), func(t *testing.T) {
   233  			actual, err := ParseTimeTZ(timeutil.Now(), tc.str, tc.precision)
   234  			if tc.expectedError {
   235  				assert.Error(t, err)
   236  			} else {
   237  				assert.NoError(t, err)
   238  				assert.Equal(t, tc.expected, actual)
   239  			}
   240  		})
   241  	}
   242  }