github.com/gogf/gf/v2@v2.7.4/os/gtime/gtime_format.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/gogf/gf.
     6  
     7  package gtime
     8  
     9  import (
    10  	"bytes"
    11  	"regexp"
    12  	"strconv"
    13  	"strings"
    14  
    15  	"github.com/gogf/gf/v2/text/gregex"
    16  )
    17  
    18  var (
    19  	// Refer: http://php.net/manual/en/function.date.php
    20  	formats = map[byte]string{
    21  		'd': "02",                        // Day: Day of the month, 2 digits with leading zeros. Eg: 01 to 31.
    22  		'D': "Mon",                       // Day: A textual representation of a day, three letters. Eg: Mon through Sun.
    23  		'w': "Monday",                    // Day: Numeric representation of the day of the week. Eg: 0 (for Sunday) through 6 (for Saturday).
    24  		'N': "Monday",                    // Day: ISO-8601 numeric representation of the day of the week. Eg: 1 (for Monday) through 7 (for Sunday).
    25  		'j': "=j=02",                     // Day: Day of the month without leading zeros. Eg: 1 to 31.
    26  		'S': "02",                        // Day: English ordinal suffix for the day of the month, 2 characters. Eg: st, nd, rd or th. Works well with j.
    27  		'l': "Monday",                    // Day: A full textual representation of the day of the week. Eg: Sunday through Saturday.
    28  		'z': "",                          // Day: The day of the year (starting from 0). Eg: 0 through 365.
    29  		'W': "",                          // Week: ISO-8601 week number of year, weeks starting on Monday. Eg: 42 (the 42nd week in the year).
    30  		'F': "January",                   // Month: A full textual representation of a month, such as January or March. Eg: January through December.
    31  		'm': "01",                        // Month: Numeric representation of a month, with leading zeros. Eg: 01 through 12.
    32  		'M': "Jan",                       // Month: A short textual representation of a month, three letters. Eg: Jan through Dec.
    33  		'n': "1",                         // Month: Numeric representation of a month, without leading zeros. Eg: 1 through 12.
    34  		't': "",                          // Month: Number of days in the given month. Eg: 28 through 31.
    35  		'Y': "2006",                      // Year: A full numeric representation of a year, 4 digits. Eg: 1999 or 2003.
    36  		'y': "06",                        // Year: A two-digit representation of a year. Eg: 99 or 03.
    37  		'a': "pm",                        // Time: Lowercase Ante meridiem and Post meridiem. Eg: am or pm.
    38  		'A': "PM",                        // Time: Uppercase Ante meridiem and Post meridiem. Eg: AM or PM.
    39  		'g': "3",                         // Time: 12-hour format of an hour without leading zeros. Eg: 1 through 12.
    40  		'G': "=G=15",                     // Time: 24-hour format of an hour without leading zeros. Eg: 0 through 23.
    41  		'h': "03",                        // Time: 12-hour format of an hour with leading zeros. Eg: 01 through 12.
    42  		'H': "15",                        // Time: 24-hour format of an hour with leading zeros. Eg: 00 through 23.
    43  		'i': "04",                        // Time: Minutes with leading zeros. Eg: 00 to 59.
    44  		's': "05",                        // Time: Seconds with leading zeros. Eg: 00 through 59.
    45  		'u': "=u=.000",                   // Time: Milliseconds. Eg: 234, 678.
    46  		'U': "",                          // Time: Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT).
    47  		'O': "-0700",                     // Zone: Difference to Greenwich time (GMT) in hours. Eg: +0200.
    48  		'P': "-07:00",                    // Zone: Difference to Greenwich time (GMT) with colon between hours and minutes. Eg: +02:00.
    49  		'T': "MST",                       // Zone: Timezone abbreviation. Eg: UTC, EST, MDT ...
    50  		'c': "2006-01-02T15:04:05-07:00", // Format: ISO 8601 date. Eg: 2004-02-12T15:19:21+00:00.
    51  		'r': "Mon, 02 Jan 06 15:04 MST",  // Format: RFC 2822 formatted date. Eg: Thu, 21 Dec 2000 16:01:07 +0200.
    52  	}
    53  
    54  	// Week to number mapping.
    55  	weekMap = map[string]string{
    56  		"Sunday":    "0",
    57  		"Monday":    "1",
    58  		"Tuesday":   "2",
    59  		"Wednesday": "3",
    60  		"Thursday":  "4",
    61  		"Friday":    "5",
    62  		"Saturday":  "6",
    63  	}
    64  
    65  	// Day count of each month which is not in leap year.
    66  	dayOfMonth = []int{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}
    67  )
    68  
    69  // Format formats and returns the formatted result with custom `format`.
    70  // Refer method Layout, if you want to follow stdlib layout.
    71  func (t *Time) Format(format string) string {
    72  	if t == nil {
    73  		return ""
    74  	}
    75  	runes := []rune(format)
    76  	buffer := bytes.NewBuffer(nil)
    77  	for i := 0; i < len(runes); {
    78  		switch runes[i] {
    79  		case '\\':
    80  			if i < len(runes)-1 {
    81  				buffer.WriteRune(runes[i+1])
    82  				i += 2
    83  				continue
    84  			} else {
    85  				return buffer.String()
    86  			}
    87  		case 'W':
    88  			buffer.WriteString(strconv.Itoa(t.WeeksOfYear()))
    89  		case 'z':
    90  			buffer.WriteString(strconv.Itoa(t.DayOfYear()))
    91  		case 't':
    92  			buffer.WriteString(strconv.Itoa(t.DaysInMonth()))
    93  		case 'U':
    94  			buffer.WriteString(strconv.FormatInt(t.Unix(), 10))
    95  		default:
    96  			if runes[i] > 255 {
    97  				buffer.WriteRune(runes[i])
    98  				break
    99  			}
   100  			if f, ok := formats[byte(runes[i])]; ok {
   101  				result := t.Time.Format(f)
   102  				// Particular chars should be handled here.
   103  				switch runes[i] {
   104  				case 'j':
   105  					for _, s := range []string{"=j=0", "=j="} {
   106  						result = strings.ReplaceAll(result, s, "")
   107  					}
   108  					buffer.WriteString(result)
   109  				case 'G':
   110  					for _, s := range []string{"=G=0", "=G="} {
   111  						result = strings.ReplaceAll(result, s, "")
   112  					}
   113  					buffer.WriteString(result)
   114  				case 'u':
   115  					buffer.WriteString(strings.ReplaceAll(result, "=u=.", ""))
   116  				case 'w':
   117  					buffer.WriteString(weekMap[result])
   118  				case 'N':
   119  					buffer.WriteString(strings.ReplaceAll(weekMap[result], "0", "7"))
   120  				case 'S':
   121  					buffer.WriteString(formatMonthDaySuffixMap(result))
   122  				default:
   123  					buffer.WriteString(result)
   124  				}
   125  			} else {
   126  				buffer.WriteRune(runes[i])
   127  			}
   128  		}
   129  		i++
   130  	}
   131  	return buffer.String()
   132  }
   133  
   134  // FormatNew formats and returns a new Time object with given custom `format`.
   135  func (t *Time) FormatNew(format string) *Time {
   136  	if t == nil {
   137  		return nil
   138  	}
   139  	return NewFromStr(t.Format(format))
   140  }
   141  
   142  // FormatTo formats `t` with given custom `format`.
   143  func (t *Time) FormatTo(format string) *Time {
   144  	if t == nil {
   145  		return nil
   146  	}
   147  	t.Time = NewFromStr(t.Format(format)).Time
   148  	return t
   149  }
   150  
   151  // Layout formats the time with stdlib layout and returns the formatted result.
   152  func (t *Time) Layout(layout string) string {
   153  	if t == nil {
   154  		return ""
   155  	}
   156  	return t.Time.Format(layout)
   157  }
   158  
   159  // LayoutNew formats the time with stdlib layout and returns the new Time object.
   160  func (t *Time) LayoutNew(layout string) *Time {
   161  	if t == nil {
   162  		return nil
   163  	}
   164  	newTime, err := StrToTimeLayout(t.Layout(layout), layout)
   165  	if err != nil {
   166  		panic(err)
   167  	}
   168  	return newTime
   169  }
   170  
   171  // LayoutTo formats `t` with stdlib layout.
   172  func (t *Time) LayoutTo(layout string) *Time {
   173  	if t == nil {
   174  		return nil
   175  	}
   176  	newTime, err := StrToTimeLayout(t.Layout(layout), layout)
   177  	if err != nil {
   178  		panic(err)
   179  	}
   180  	t.Time = newTime.Time
   181  	return t
   182  }
   183  
   184  // IsLeapYear checks whether the time is leap year.
   185  func (t *Time) IsLeapYear() bool {
   186  	year := t.Year()
   187  	if (year%4 == 0 && year%100 != 0) || year%400 == 0 {
   188  		return true
   189  	}
   190  	return false
   191  }
   192  
   193  // DayOfYear checks and returns the position of the day for the year.
   194  func (t *Time) DayOfYear() int {
   195  	var (
   196  		day   = t.Day()
   197  		month = t.Month()
   198  	)
   199  	if t.IsLeapYear() {
   200  		if month > 2 {
   201  			return dayOfMonth[month-1] + day
   202  		}
   203  		return dayOfMonth[month-1] + day - 1
   204  	}
   205  	return dayOfMonth[month-1] + day - 1
   206  }
   207  
   208  // DaysInMonth returns the day count of current month.
   209  func (t *Time) DaysInMonth() int {
   210  	switch t.Month() {
   211  	case 1, 3, 5, 7, 8, 10, 12:
   212  		return 31
   213  	case 4, 6, 9, 11:
   214  		return 30
   215  	}
   216  	if t.IsLeapYear() {
   217  		return 29
   218  	}
   219  	return 28
   220  }
   221  
   222  // WeeksOfYear returns the point of current week for the year.
   223  func (t *Time) WeeksOfYear() int {
   224  	_, week := t.ISOWeek()
   225  	return week
   226  }
   227  
   228  // formatToStdLayout converts custom format to stdlib layout.
   229  func formatToStdLayout(format string) string {
   230  	b := bytes.NewBuffer(nil)
   231  	for i := 0; i < len(format); {
   232  		switch format[i] {
   233  		case '\\':
   234  			if i < len(format)-1 {
   235  				b.WriteByte(format[i+1])
   236  				i += 2
   237  				continue
   238  			} else {
   239  				return b.String()
   240  			}
   241  
   242  		default:
   243  			if f, ok := formats[format[i]]; ok {
   244  				// Handle particular chars.
   245  				switch format[i] {
   246  				case 'j':
   247  					b.WriteString("2")
   248  				case 'G':
   249  					b.WriteString("15")
   250  				case 'u':
   251  					if i > 0 && format[i-1] == '.' {
   252  						b.WriteString("000")
   253  					} else {
   254  						b.WriteString(".000")
   255  					}
   256  
   257  				default:
   258  					b.WriteString(f)
   259  				}
   260  			} else {
   261  				b.WriteByte(format[i])
   262  			}
   263  			i++
   264  		}
   265  	}
   266  	return b.String()
   267  }
   268  
   269  // formatToRegexPattern converts the custom format to its corresponding regular expression.
   270  func formatToRegexPattern(format string) string {
   271  	s := regexp.QuoteMeta(formatToStdLayout(format))
   272  	s, _ = gregex.ReplaceString(`[0-9]`, `[0-9]`, s)
   273  	s, _ = gregex.ReplaceString(`[A-Za-z]`, `[A-Za-z]`, s)
   274  	s, _ = gregex.ReplaceString(`\s+`, `\s+`, s)
   275  	return s
   276  }
   277  
   278  // formatMonthDaySuffixMap returns the short english word for current day.
   279  func formatMonthDaySuffixMap(day string) string {
   280  	switch day {
   281  	case "01", "21", "31":
   282  		return "st"
   283  	case "02", "22":
   284  		return "nd"
   285  	case "03", "23":
   286  		return "rd"
   287  	default:
   288  		return "th"
   289  	}
   290  }