github.com/yaoapp/kun@v0.9.0/day/datetime.go (about)

     1  package day
     2  
     3  import (
     4  	"database/sql/driver"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"strings"
     9  	"time"
    10  	"unicode"
    11  )
    12  
    13  // Datetime type of day
    14  type Datetime struct {
    15  	time.Time
    16  }
    17  
    18  var defaultLocation *time.Location = nil
    19  
    20  var defaultFormats = []string{
    21  	"2006-01-02T15:04:05-0700",
    22  	"2006-01-02T15:04:05.000Z",
    23  	"2006-01-02T15:04:05Z",
    24  	"2006-01-02T15:04:05+08:00",
    25  	"2006-01-02T15:04:05",
    26  	"2006-01-02 15:04:05",
    27  	"2006-01-02",
    28  	"15:04:05",
    29  }
    30  
    31  // Now is a alias of Make
    32  func Now() *Datetime {
    33  	return Make()
    34  }
    35  
    36  // Make make a new Datetime
    37  func Make() *Datetime {
    38  	now := &Datetime{Time: time.Now()}
    39  	if defaultLocation != nil {
    40  		now.Time = now.In(defaultLocation)
    41  	}
    42  	return now
    43  }
    44  
    45  // Of make a new datetime with the given value
    46  func Of(value interface{}, formats ...string) *Datetime {
    47  
    48  	switch value.(type) {
    49  	case time.Time:
    50  		return &Datetime{Time: value.(time.Time)}
    51  	case *Datetime:
    52  		return value.(*Datetime)
    53  	case Datetime:
    54  		d := value.(Datetime)
    55  		return &d
    56  	}
    57  
    58  	if len(formats) == 0 {
    59  		formats = defaultFormats
    60  	}
    61  	valueStr := fmt.Sprintf("%v", value)
    62  	for _, format := range formats {
    63  		valueTime, err := time.Parse(format, valueStr)
    64  		if err == nil {
    65  			if defaultLocation != nil {
    66  				valueTime = valueTime.In(defaultLocation)
    67  				return &Datetime{Time: valueTime}
    68  			}
    69  			return &Datetime{Time: valueTime}
    70  		}
    71  	}
    72  	panic("the given value is not time format")
    73  }
    74  
    75  // Load load time with given format
    76  func (d *Datetime) Load(value interface{}, formats ...string) *Datetime {
    77  	*d = *Of(value, formats...)
    78  	return d
    79  }
    80  
    81  // Timezone set the timezone of datetime
    82  func (d *Datetime) Timezone(name string, offset ...int) *Datetime {
    83  	if len(offset) > 0 {
    84  		loc := time.FixedZone(name, offset[0])
    85  		defaultLocation = loc
    86  		d.Time = d.In(loc)
    87  		return d
    88  	}
    89  	loc, err := time.LoadLocation(name)
    90  	if err != nil {
    91  		panic(err.Error())
    92  	}
    93  	d.Time = d.In(loc)
    94  	return d
    95  }
    96  
    97  // GetTimezone get the timezone of current
    98  func GetTimezone() (name string, offset int) {
    99  	if defaultLocation == nil {
   100  		return time.Now().Zone()
   101  	}
   102  	return time.Now().In(defaultLocation).Zone()
   103  }
   104  
   105  // TimezoneSystem using the system default zone
   106  func TimezoneSystem() {
   107  	defaultLocation = nil
   108  }
   109  
   110  // TimezoneUTC using the UTC zone
   111  func TimezoneUTC() {
   112  	defaultLocation = time.UTC
   113  }
   114  
   115  // Timezone set the default location
   116  func Timezone(name string, offset ...int) {
   117  	if len(offset) > 0 {
   118  		loc := time.FixedZone(name, offset[0])
   119  		defaultLocation = loc
   120  		return
   121  	}
   122  
   123  	loc, err := time.LoadLocation(name)
   124  	if err != nil {
   125  		panic(err.Error())
   126  	}
   127  	defaultLocation = loc
   128  }
   129  
   130  // TimeZones Get a list of valid time zones
   131  func TimeZones() []string {
   132  	var zones []string
   133  	var zoneDirs = []string{
   134  		// Update path according to your OS
   135  		"/usr/share/zoneinfo/",
   136  		"/usr/share/lib/zoneinfo/",
   137  		"/usr/lib/locale/TZ/",
   138  	}
   139  
   140  	for _, zd := range zoneDirs {
   141  		zones = walkTzDir(zd, zones)
   142  		for idx, zone := range zones {
   143  			zones[idx] = strings.ReplaceAll(zone, zd+"/", "")
   144  		}
   145  	}
   146  
   147  	return zones
   148  }
   149  
   150  func walkTzDir(path string, zones []string) []string {
   151  	fileInfos, err := ioutil.ReadDir(path)
   152  	if err != nil {
   153  		return zones
   154  	}
   155  
   156  	isAlpha := func(s string) bool {
   157  		for _, r := range s {
   158  			if !unicode.IsLetter(r) {
   159  				return false
   160  			}
   161  		}
   162  		return true
   163  	}
   164  
   165  	for _, info := range fileInfos {
   166  		if info.Name() != strings.ToUpper(info.Name()[:1])+info.Name()[1:] {
   167  			continue
   168  		}
   169  
   170  		if !isAlpha(info.Name()[:1]) {
   171  			continue
   172  		}
   173  
   174  		newPath := path + "/" + info.Name()
   175  
   176  		if info.IsDir() {
   177  			zones = walkTzDir(newPath, zones)
   178  		} else {
   179  			zones = append(zones, newPath)
   180  		}
   181  	}
   182  
   183  	return zones
   184  }
   185  
   186  // Scan for db scan
   187  func (d *Datetime) Scan(src interface{}) error {
   188  	*d = *Of(src)
   189  	return nil
   190  }
   191  
   192  // Value for db driver value
   193  func (d *Datetime) Value() (driver.Value, error) {
   194  	return d.Time, nil
   195  }
   196  
   197  // MarshalJSON for json marshalJSON
   198  func (d *Datetime) MarshalJSON() ([]byte, error) {
   199  	return json.Marshal(d.Time)
   200  }
   201  
   202  // UnmarshalJSON for json marshalJSON
   203  func (d *Datetime) UnmarshalJSON(data []byte) error {
   204  	*d = *Of(data)
   205  	return nil
   206  }