github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/s3select/sql/timestampfuncs.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package sql 19 20 import ( 21 "time" 22 ) 23 24 const ( 25 layoutYear = "2006T" 26 layoutMonth = "2006-01T" 27 layoutDay = "2006-01-02T" 28 layoutMinute = "2006-01-02T15:04Z07:00" 29 layoutSecond = "2006-01-02T15:04:05Z07:00" 30 layoutNanosecond = "2006-01-02T15:04:05.999999999Z07:00" 31 ) 32 33 var tformats = []string{ 34 layoutYear, 35 layoutMonth, 36 layoutDay, 37 layoutMinute, 38 layoutSecond, 39 layoutNanosecond, 40 } 41 42 func parseSQLTimestamp(s string) (t time.Time, err error) { 43 for _, f := range tformats { 44 t, err = time.Parse(f, s) 45 if err == nil { 46 break 47 } 48 } 49 return 50 } 51 52 // FormatSQLTimestamp - returns the a string representation of the 53 // timestamp as used in S3 Select 54 func FormatSQLTimestamp(t time.Time) string { 55 _, zoneOffset := t.Zone() 56 hasZone := zoneOffset != 0 57 hasFracSecond := t.Nanosecond() != 0 58 hasSecond := t.Second() != 0 59 hasTime := t.Hour() != 0 || t.Minute() != 0 60 hasDay := t.Day() != 1 61 hasMonth := t.Month() != 1 62 63 switch { 64 case hasFracSecond: 65 return t.Format(layoutNanosecond) 66 case hasSecond: 67 return t.Format(layoutSecond) 68 case hasTime || hasZone: 69 return t.Format(layoutMinute) 70 case hasDay: 71 return t.Format(layoutDay) 72 case hasMonth: 73 return t.Format(layoutMonth) 74 default: 75 return t.Format(layoutYear) 76 } 77 } 78 79 const ( 80 timePartYear = "YEAR" 81 timePartMonth = "MONTH" 82 timePartDay = "DAY" 83 timePartHour = "HOUR" 84 timePartMinute = "MINUTE" 85 timePartSecond = "SECOND" 86 timePartTimezoneHour = "TIMEZONE_HOUR" 87 timePartTimezoneMinute = "TIMEZONE_MINUTE" 88 ) 89 90 func extract(what string, t time.Time) (v *Value, err error) { 91 switch what { 92 case timePartYear: 93 return FromInt(int64(t.Year())), nil 94 case timePartMonth: 95 return FromInt(int64(t.Month())), nil 96 case timePartDay: 97 return FromInt(int64(t.Day())), nil 98 case timePartHour: 99 return FromInt(int64(t.Hour())), nil 100 case timePartMinute: 101 return FromInt(int64(t.Minute())), nil 102 case timePartSecond: 103 return FromInt(int64(t.Second())), nil 104 case timePartTimezoneHour: 105 _, zoneOffset := t.Zone() 106 return FromInt(int64(zoneOffset / 3600)), nil 107 case timePartTimezoneMinute: 108 _, zoneOffset := t.Zone() 109 return FromInt(int64((zoneOffset % 3600) / 60)), nil 110 default: 111 // This does not happen 112 return nil, errNotImplemented 113 } 114 } 115 116 func dateAdd(timePart string, qty float64, t time.Time) (*Value, error) { 117 var duration time.Duration 118 switch timePart { 119 case timePartYear: 120 return FromTimestamp(t.AddDate(int(qty), 0, 0)), nil 121 case timePartMonth: 122 return FromTimestamp(t.AddDate(0, int(qty), 0)), nil 123 case timePartDay: 124 return FromTimestamp(t.AddDate(0, 0, int(qty))), nil 125 case timePartHour: 126 duration = time.Duration(qty) * time.Hour 127 case timePartMinute: 128 duration = time.Duration(qty) * time.Minute 129 case timePartSecond: 130 duration = time.Duration(qty) * time.Second 131 default: 132 return nil, errNotImplemented 133 } 134 return FromTimestamp(t.Add(duration)), nil 135 } 136 137 // dateDiff computes the difference between two times in terms of the 138 // `timePart` which can be years, months, days, hours, minutes or 139 // seconds. For difference in years, months or days, the time part, 140 // including timezone is ignored. 141 func dateDiff(timePart string, ts1, ts2 time.Time) (*Value, error) { 142 if ts2.Before(ts1) { 143 v, err := dateDiff(timePart, ts2, ts1) 144 if err == nil { 145 v.negate() 146 } 147 return v, err 148 } 149 150 duration := ts2.Sub(ts1) 151 y1, m1, d1 := ts1.Date() 152 y2, m2, d2 := ts2.Date() 153 154 switch timePart { 155 case timePartYear: 156 dy := int64(y2 - y1) 157 if m2 > m1 || (m2 == m1 && d2 >= d1) { 158 return FromInt(dy), nil 159 } 160 return FromInt(dy - 1), nil 161 case timePartMonth: 162 m1 += time.Month(12 * y1) 163 m2 += time.Month(12 * y2) 164 165 return FromInt(int64(m2 - m1)), nil 166 case timePartDay: 167 return FromInt(int64(duration / (24 * time.Hour))), nil 168 case timePartHour: 169 hours := duration / time.Hour 170 return FromInt(int64(hours)), nil 171 case timePartMinute: 172 minutes := duration / time.Minute 173 return FromInt(int64(minutes)), nil 174 case timePartSecond: 175 seconds := duration / time.Second 176 return FromInt(int64(seconds)), nil 177 default: 178 179 } 180 return nil, errNotImplemented 181 }