github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/integration/write_read_timezone_test.go (about) 1 // +build integration 2 3 // Copyright (c) 2017 Uber Technologies, Inc. 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 // 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 23 package integration 24 25 import ( 26 "os" 27 "testing" 28 "time" 29 30 "github.com/m3db/m3/src/x/ident" 31 xtime "github.com/m3db/m3/src/x/time" 32 33 "github.com/stretchr/testify/require" 34 ) 35 36 type readWriteTZCase struct { 37 namespace string 38 id string 39 datapoints []readWriteTZDP 40 } 41 42 type readWriteTZDP struct { 43 value float64 44 timestamp xtime.UnixNano 45 } 46 47 // Make sure that everything works properly end-to-end even if the client issues 48 // a write in a timezone other than that of the server. 49 func TestWriteReadTimezone(t *testing.T) { 50 if testing.Short() { 51 t.SkipNow() 52 } 53 54 // Ensure that the test is running with the local timezone set to US/Pacific. 55 // Note that we do this instead of just manipulating the NowFn on the test 56 // setup because there are ways to end up with a time object with a non-UTC 57 // timezone besides calling time.Now(). For example, converting a unix 58 // timestamp into a time.Time object will automatically associate the machine's 59 // local timezone. 60 os.Setenv("TZ", "US/Pacific") 61 name, offset := time.Now().Zone() 62 // The zone name will be PST or PDT depending on whether daylight savings 63 // is currently in effect 64 if name == "PDT" { 65 require.Equal(t, offset, -25200) 66 } else if name == "PST" { 67 require.Equal(t, offset, -28800) 68 } else { 69 t.Fatalf("Zone should be PDT or PST, but was: %s", name) 70 } 71 72 // Load locations that we'll need later in the tests 73 pacificLocation, err := time.LoadLocation("US/Pacific") 74 require.NoError(t, err) 75 nyLocation, err := time.LoadLocation("America/New_York") 76 require.NoError(t, err) 77 78 // Setup / start server 79 opts := NewTestOptions(t) 80 setup, err := NewTestSetup(t, opts, nil) 81 require.NoError(t, err) 82 defer setup.Close() 83 require.NoError(t, setup.StartServer()) 84 require.NoError(t, setup.WaitUntilServerIsBootstrapped()) 85 86 // Make sure that the server's internal clock function returns pacific timezone 87 start := setup.NowFn()().ToTime() 88 setup.SetNowFn(xtime.ToUnixNano(start.In(pacificLocation))) 89 90 // Instantiate a client 91 client := setup.M3DBClient() 92 session, err := client.DefaultSession() 93 require.NoError(t, err) 94 defer session.Close() 95 96 // Generate test datapoints (all with NY timezone) 97 namespace := opts.Namespaces()[0].ID().String() 98 startNy := xtime.ToUnixNano(start.In(nyLocation)) 99 writeSeries := []readWriteTZCase{ 100 { 101 namespace: namespace, 102 id: "some-id-1", 103 datapoints: []readWriteTZDP{ 104 { 105 value: 20.0, 106 timestamp: startNy, 107 }, 108 { 109 value: 20.0, 110 timestamp: startNy.Add(1 * time.Second), 111 }, 112 { 113 value: 20.0, 114 timestamp: startNy.Add(2 * time.Second), 115 }, 116 }, 117 }, 118 { 119 namespace: namespace, 120 id: "some-id-2", 121 datapoints: []readWriteTZDP{ 122 { 123 value: 30.0, 124 timestamp: startNy, 125 }, 126 { 127 value: 30.0, 128 timestamp: startNy.Add(1 * time.Second), 129 }, 130 { 131 value: 30.0, 132 timestamp: startNy.Add(2 * time.Second), 133 }, 134 }, 135 }, 136 } 137 138 // Write datapoints 139 for _, series := range writeSeries { 140 for _, write := range series.datapoints { 141 err = session.Write(ident.StringID(series.namespace), ident.StringID(series.id), write.timestamp, write.value, xtime.Second, nil) 142 require.NoError(t, err) 143 } 144 } 145 146 // Read datapoints back 147 iters, err := session.FetchIDs(ident.StringID(namespace), 148 ident.NewIDsIterator(ident.StringID("some-id-1"), ident.StringID("some-id-2")), 149 startNy, startNy.Add(1*time.Hour)) 150 require.NoError(t, err) 151 152 // Assert datapoints match what we wrote 153 for i, iter := range iters.Iters() { 154 for j := 0; iter.Next(); j++ { 155 dp, _, _ := iter.Current() 156 expectedDatapoint := writeSeries[i].datapoints[j] 157 // Datapoints will comeback with the timezone set to the local timezone 158 // of the machine that the client is running on. The Equal() method ensures 159 // that the two time.Time struct's refer to the same instant in time 160 require.True(t, expectedDatapoint.timestamp.Equal(dp.TimestampNanos)) 161 require.Equal(t, expectedDatapoint.value, dp.Value) 162 } 163 } 164 }