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  }