github.com/mithrandie/csvq@v1.18.1/lib/value/conv.go (about) 1 package value 2 3 import ( 4 "bytes" 5 "math" 6 "strconv" 7 "strings" 8 "sync" 9 "time" 10 11 "github.com/mithrandie/csvq/lib/option" 12 13 "github.com/mithrandie/ternary" 14 ) 15 16 var DatetimeFormats = NewDatetimeFormatMap() 17 18 type DatetimeFormatMap struct { 19 m *sync.Map 20 mtx *sync.Mutex 21 } 22 23 func NewDatetimeFormatMap() DatetimeFormatMap { 24 return DatetimeFormatMap{ 25 m: &sync.Map{}, 26 mtx: &sync.Mutex{}, 27 } 28 } 29 30 func (dfmap DatetimeFormatMap) store(key string, value string) { 31 dfmap.m.Store(key, value) 32 } 33 34 func (dfmap DatetimeFormatMap) load(key string) (string, bool) { 35 v, ok := dfmap.m.Load(key) 36 if ok { 37 return v.(string), ok 38 } 39 return "", ok 40 } 41 42 func (dfmap DatetimeFormatMap) Get(s string) string { 43 if f, ok := dfmap.load(s); ok { 44 return f 45 } 46 47 dfmap.mtx.Lock() 48 defer dfmap.mtx.Unlock() 49 50 if f, ok := dfmap.load(s); ok { 51 return f 52 } 53 54 f := ConvertDatetimeFormat(s) 55 dfmap.store(s, f) 56 return f 57 } 58 59 func StrToTime(s string, formats []string, location *time.Location) (time.Time, bool) { 60 s = option.TrimSpace(s) 61 62 for _, format := range formats { 63 if t, e := time.ParseInLocation(DatetimeFormats.Get(format), s, location); e == nil { 64 return t, true 65 } 66 } 67 68 if 8 <= len(s) && '0' <= s[0] && s[0] <= '9' { 69 switch { 70 case s[4] == '-': 71 if len(s) < 10 { 72 if t, e := time.ParseInLocation("2006-1-2", s, location); e == nil { 73 return t, true 74 } 75 } else if len(s) == 10 { 76 if t, e := time.ParseInLocation("2006-01-02", s, location); e == nil { 77 return t, true 78 } 79 } else if s[10] == 'T' { 80 if s[len(s)-6] == '+' || s[len(s)-6] == '-' || s[len(s)-1] == 'Z' { 81 if t, e := time.Parse(time.RFC3339Nano, s); e == nil { 82 return t, true 83 } 84 } else { 85 if t, e := time.ParseInLocation("2006-01-02T15:04:05.999999999", s, location); e == nil { 86 return t, true 87 } 88 } 89 } else if s[10] == ' ' { 90 if t, e := time.ParseInLocation("2006-01-02 15:04:05.999999999", s, location); e == nil { 91 return t, true 92 } else if t, e := time.Parse("2006-01-02 15:04:05.999999999 Z07:00", s); e == nil { 93 return t, true 94 } else if t, e := time.Parse("2006-01-02 15:04:05.999999999 -0700", s); e == nil { 95 return t, true 96 } else if t, e := time.Parse("2006-01-02 15:04:05.999999999 MST", s); e == nil { 97 return t, true 98 } 99 } else { 100 if t, e := time.ParseInLocation("2006-1-2 15:04:05.999999999", s, location); e == nil { 101 return t, true 102 } else if t, e := time.Parse("2006-1-2 15:04:05.999999999 Z07:00", s); e == nil { 103 return t, true 104 } else if t, e := time.Parse("2006-1-2 15:04:05.999999999 -0700", s); e == nil { 105 return t, true 106 } else if t, e := time.Parse("2006-1-2 15:04:05.999999999 MST", s); e == nil { 107 return t, true 108 } 109 } 110 case s[4] == '/': 111 if len(s) < 10 { 112 if t, e := time.ParseInLocation("2006/1/2", s, location); e == nil { 113 return t, true 114 } 115 } else if len(s) == 10 { 116 if t, e := time.ParseInLocation("2006/01/02", s, location); e == nil { 117 return t, true 118 } 119 } else if s[10] == ' ' { 120 if t, e := time.ParseInLocation("2006/01/02 15:04:05.999999999", s, location); e == nil { 121 return t, true 122 } else if t, e := time.Parse("2006/01/02 15:04:05.999999999 Z07:00", s); e == nil { 123 return t, true 124 } else if t, e := time.Parse("2006/01/02 15:04:05.999999999 -0700", s); e == nil { 125 return t, true 126 } else if t, e := time.Parse("2006/01/02 15:04:05.999999999 MST", s); e == nil { 127 return t, true 128 } 129 } else { 130 if t, e := time.ParseInLocation("2006/1/2 15:04:05.999999999", s, location); e == nil { 131 return t, true 132 } else if t, e := time.Parse("2006/1/2 15:04:05.999999999 Z07:00", s); e == nil { 133 return t, true 134 } else if t, e := time.Parse("2006/1/2 15:04:05.999999999 -0700", s); e == nil { 135 return t, true 136 } else if t, e := time.Parse("2006/1/2 15:04:05.999999999 MST", s); e == nil { 137 return t, true 138 } 139 } 140 default: 141 if t, e := time.Parse(time.RFC822, s); e == nil { 142 return t, true 143 } else if t, e := time.Parse(time.RFC822Z, s); e == nil { 144 return t, true 145 } 146 } 147 } 148 return time.Time{}, false 149 } 150 151 func ConvertDatetimeFormat(format string) string { 152 runes := []rune(format) 153 var buf bytes.Buffer 154 155 escaped := false 156 for _, r := range runes { 157 if !escaped { 158 switch r { 159 case '%': 160 escaped = true 161 default: 162 buf.WriteRune(r) 163 } 164 continue 165 } 166 167 switch r { 168 case 'a': 169 buf.WriteString("Mon") 170 case 'b': 171 buf.WriteString("Jan") 172 case 'c': 173 buf.WriteString("1") 174 case 'd': 175 buf.WriteString("02") 176 case 'E': 177 buf.WriteString("_2") 178 case 'e': 179 buf.WriteString("2") 180 case 'F': 181 buf.WriteString(".999999") 182 case 'f': 183 buf.WriteString(".000000") 184 case 'H': 185 buf.WriteString("15") 186 case 'h': 187 buf.WriteString("03") 188 case 'i': 189 buf.WriteString("04") 190 case 'l': 191 buf.WriteString("3") 192 case 'M': 193 buf.WriteString("January") 194 case 'm': 195 buf.WriteString("01") 196 case 'N': 197 buf.WriteString(".999999999") 198 case 'n': 199 buf.WriteString(".000000000") 200 case 'p': 201 buf.WriteString("PM") 202 case 'r': 203 buf.WriteString("03:04:05 PM") 204 case 's': 205 buf.WriteString("05") 206 case 'T': 207 buf.WriteString("15:04:05") 208 case 'W': 209 buf.WriteString("Monday") 210 case 'Y': 211 buf.WriteString("2006") 212 case 'y': 213 buf.WriteString("06") 214 case 'Z': 215 buf.WriteString("Z07:00") 216 case 'z': 217 buf.WriteString("MST") 218 default: 219 buf.WriteRune(r) 220 } 221 escaped = false 222 } 223 224 return buf.String() 225 } 226 227 func Float64ToTime(f float64, location *time.Location) time.Time { 228 s := Float64ToStr(f, false) 229 ar := strings.Split(s, ".") 230 231 sec, _ := strconv.ParseInt(ar[0], 10, 64) 232 nsec, _ := (func() (int64, error) { 233 if len(ar) < 2 { 234 return 0, nil 235 } 236 237 if 9 < len(ar[1]) { 238 return strconv.ParseInt(ar[1][:9], 10, 64) 239 } 240 241 dec := ar[1] + strings.Repeat("0", 9-len(ar[1])) 242 return strconv.ParseInt(dec, 10, 64) 243 })() 244 245 return TimeFromUnixTime(sec, nsec, location) 246 } 247 248 func Int64ToStr(i int64) string { 249 return strconv.FormatInt(i, 10) 250 } 251 252 func Float64ToStr(f float64, useScientificNotation bool) string { 253 if useScientificNotation { 254 return strconv.FormatFloat(f, 'g', -1, 64) 255 } 256 return strconv.FormatFloat(f, 'f', -1, 64) 257 } 258 259 func ToInteger(p Primary) Primary { 260 switch val := p.(type) { 261 case *Integer: 262 return NewInteger(val.Raw()) 263 case *Float: 264 if math.IsNaN(val.Raw()) || math.IsInf(val.Raw(), 0) { 265 return NewNull() 266 } 267 return NewInteger(int64(val.Raw())) 268 case *String: 269 s := option.TrimSpace(val.Raw()) 270 if i, e := strconv.ParseInt(s, 10, 64); e == nil { 271 return NewInteger(i) 272 } 273 if f, e := strconv.ParseFloat(s, 64); e == nil { 274 return NewInteger(int64(f)) 275 } 276 } 277 278 return NewNull() 279 } 280 281 func ToIntegerStrictly(p Primary) Primary { 282 switch p.(type) { 283 case *Integer: 284 return NewInteger(p.(*Integer).Raw()) 285 case *String: 286 s := option.TrimSpace(p.(*String).Raw()) 287 if i, e := strconv.ParseInt(s, 10, 64); e == nil { 288 return NewInteger(i) 289 } 290 } 291 292 return NewNull() 293 } 294 295 func ToFloat(p Primary) Primary { 296 switch p.(type) { 297 case *Integer: 298 return NewFloat(float64(p.(*Integer).Raw())) 299 case *Float: 300 return NewFloat(p.(*Float).Raw()) 301 case *String: 302 s := option.TrimSpace(p.(*String).Raw()) 303 if f, e := strconv.ParseFloat(s, 64); e == nil { 304 return NewFloat(f) 305 } 306 } 307 308 return NewNull() 309 } 310 311 func ToDatetime(p Primary, formats []string, location *time.Location) Primary { 312 switch p.(type) { 313 case *Datetime: 314 return NewDatetime(p.(*Datetime).Raw()) 315 case *String: 316 if dt, ok := StrToTime(p.(*String).Raw(), formats, location); ok { 317 return NewDatetime(dt) 318 } 319 } 320 321 return NewNull() 322 } 323 324 func TimeFromUnixTime(sec int64, nano int64, location *time.Location) time.Time { 325 return time.Unix(sec, nano).In(location) 326 } 327 328 func ToBoolean(p Primary) Primary { 329 switch p.(type) { 330 case *Boolean: 331 return NewBoolean(p.(*Boolean).Raw()) 332 case *String, *Integer, *Float, *Ternary: 333 if p.Ternary() != ternary.UNKNOWN { 334 return NewBoolean(p.Ternary().ParseBool()) 335 } 336 } 337 return NewNull() 338 } 339 340 func ToString(p Primary) Primary { 341 switch p.(type) { 342 case *String: 343 return NewString(p.(*String).Raw()) 344 case *Integer: 345 return NewString(Int64ToStr(p.(*Integer).Raw())) 346 case *Float: 347 return NewString(Float64ToStr(p.(*Float).Raw(), false)) 348 } 349 return NewNull() 350 }