github.com/zhongdalu/gf@v1.0.0/g/os/gtime/gtime_format.go (about)

     1  // Copyright 2018 gf Author(https://github.com/zhongdalu/gf). 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/zhongdalu/gf.
     6  
     7  package gtime
     8  
     9  import (
    10  	"bytes"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/zhongdalu/gf/g/text/gregex"
    15  )
    16  
    17  var (
    18  	// 参考:http://php.net/manual/zh/function.date.php
    19  	formats = map[byte]string{
    20  		// ================== 日 ==================
    21  		'd': "02",     // 月份中的第几天,有前导零的 2 位数字(01 到 31)
    22  		'D': "Mon",    // 星期中的第几天,文本表示,3 个字母(Mon 到 Sun)
    23  		'w': "Monday", // 星期中的第几天,数字型式的文本表示 0为星期天 6为星期六
    24  		'N': "Monday", // ISO-8601 格式数字表示的星期中的第几天 1(表示星期一)到 7(表示星期天)
    25  		'j': "=j=02",  // 月份中的第几天,没有前导零(1 到 31)
    26  		'S': "02",     // 每月天数后面的英文后缀,2 个字符 st,nd,rd 或者 th。可以和 j 一起用
    27  		'l': "Monday", // ("L"的小写字母)星期几,完整的文本格式(Sunday 到 Saturday)
    28  		'z': "",       // 年份中的第几天  0到365
    29  
    30  		// ================== 日 ==================
    31  		'W': "", // ISO-8601 格式年份中的第几周,每周从星期一开始 例如:42(当年的第 42 周)
    32  
    33  		// ================== 月 ==================
    34  		'F': "January", // 月份,完整的文本格式,例如 January 或者 March	January 到 December
    35  		'm': "01",      // 数字表示的月份,有前导零(01 到 12)
    36  		'M': "Jan",     // 三个字母缩写表示的月份(Jan 到 Dec)
    37  		'n': "1",       // 数字表示的月份,没有前导零(1 到 12)
    38  		't': "",        // 指定的月份有几天 28到31
    39  
    40  		// ================== 年 ==================
    41  		'Y': "2006", // 4 位数字完整表示的年份, 例如:1999 或 2003
    42  		'y': "06",   // 2 位数字表示的年份, 例如:99 或 03
    43  
    44  		// ================== 时间 ==================
    45  		'a': "pm",      // 小写的上午和下午值	am 或 pm
    46  		'A': "PM",      // 大写的上午和下午值	AM 或 PM
    47  		'g': "3",       // 小时,12 小时格式,没有前导零,  1 到 12
    48  		'G': "=G=15",   // 小时,24 小时格式,没有前导零,  0 到 23
    49  		'h': "03",      // 小时,12 小时格式,有前导零,   01 到 12
    50  		'H': "15",      // 小时,24 小时格式,有前导零,   00 到 23
    51  		'i': "04",      // 有前导零的分钟数, 00 到 59
    52  		's': "05",      // 秒数,有前导零,   00 到 59
    53  		'u': "=u=.000", // 毫秒(3位)
    54  		'U': "",        // 将时间格式化为Unix时间,即从时间点January 1, 1970 UTC到时间点t所经过的时间(单位秒)
    55  
    56  		// ================== 时区 ==================
    57  		'O': "-0700",  // 与UTC相差的小时数, 例如:+0200
    58  		'P': "-07:00", // 与UTC的差别,小时和分钟之间有冒号分隔, 例如:+02:00
    59  		'T': "MST",    // 时区缩写, 例如:  UTC, EST, MDT
    60  
    61  		// ================== 完整的日期/时间 ==================
    62  		'c': "2006-01-02T15:04:05-07:00", // ISO 8601 格式的日期,例如:2004-02-12T15:19:21+00:00
    63  		'r': "Mon, 02 Jan 06 15:04 MST",  // RFC 822 格式的日期,例如:Thu, 21 Dec 2000 16:01:07 +0200
    64  	}
    65  
    66  	// 星期的英文值和数字值对应map
    67  	weekMap = map[string]string{
    68  		"Sunday":    "0",
    69  		"Monday":    "1",
    70  		"Tuesday":   "2",
    71  		"Wednesday": "3",
    72  		"Thursday":  "4",
    73  		"Friday":    "5",
    74  		"Saturday":  "6",
    75  	}
    76  
    77  	// 每个月累计的天数 不含润年的时候
    78  	dayOfMonth = []int{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}
    79  )
    80  
    81  // 使用自定义日期格式格式化输出日期。
    82  func (t *Time) Format(format string) string {
    83  	runes := []rune(format)
    84  	buffer := bytes.NewBuffer(nil)
    85  	for i := 0; i < len(runes); {
    86  		switch runes[i] {
    87  		case '\\':
    88  			if i < len(runes)-1 {
    89  				buffer.WriteRune(runes[i+1])
    90  				i += 2
    91  				continue
    92  			} else {
    93  				return buffer.String()
    94  			}
    95  		case 'W':
    96  			buffer.WriteString(strconv.Itoa(t.WeeksOfYear()))
    97  		case 'z':
    98  			buffer.WriteString(strconv.Itoa(t.DayOfYear()))
    99  		case 't':
   100  			buffer.WriteString(strconv.Itoa(t.DaysInMonth()))
   101  		case 'U':
   102  			buffer.WriteString(strconv.FormatInt(t.Unix(), 10))
   103  		default:
   104  			if runes[i] > 255 {
   105  				buffer.WriteRune(runes[i])
   106  				break
   107  			}
   108  			if f, ok := formats[byte(runes[i])]; ok {
   109  				result := t.Time.Format(f)
   110  				// 有几个转换的符号需要特殊处理
   111  				switch runes[i] {
   112  				case 'j':
   113  					for _, s := range []string{"=j=0", "=j="} {
   114  						result = strings.Replace(result, s, "", -1)
   115  					}
   116  					buffer.WriteString(result)
   117  				case 'G':
   118  					for _, s := range []string{"=G=0", "=G="} {
   119  						result = strings.Replace(result, s, "", -1)
   120  					}
   121  					buffer.WriteString(result)
   122  				case 'u':
   123  					buffer.WriteString(strings.Replace(result, "=u=.", "", -1))
   124  				case 'w':
   125  					buffer.WriteString(weekMap[result])
   126  				case 'N':
   127  					buffer.WriteString(strings.Replace(weekMap[result], "0", "7", -1))
   128  				case 'S':
   129  					buffer.WriteString(formatMonthDaySuffixMap(result))
   130  				default:
   131  					buffer.WriteString(result)
   132  				}
   133  			} else {
   134  				buffer.WriteRune(runes[i])
   135  			}
   136  		}
   137  		i++
   138  	}
   139  	return buffer.String()
   140  }
   141  
   142  // 通过自定义格式转换当前日期为新的日期。
   143  func (t *Time) FormatTo(format string) *Time {
   144  	t.Time = NewFromStr(t.Format(format)).Time
   145  	return t
   146  }
   147  
   148  // 使用标准库格式格式化输出日期。
   149  func (t *Time) Layout(layout string) string {
   150  	return t.Time.Format(layout)
   151  }
   152  
   153  // 通过标准库格式转换当前日期为新的日期。
   154  func (t *Time) LayoutTo(layout string) *Time {
   155  	t.Time = NewFromStr(t.Layout(layout)).Time
   156  	return t
   157  }
   158  
   159  // 返回是否是润年
   160  func (t *Time) IsLeapYear() bool {
   161  	year := t.Year()
   162  	if (year%4 == 0 && year%100 != 0) || year%400 == 0 {
   163  		return true
   164  	}
   165  	return false
   166  }
   167  
   168  // 返回一个时间点在当年中是第几天 0到365 有润年情况
   169  func (t *Time) DayOfYear() int {
   170  	month := int(t.Month())
   171  	day := t.Day()
   172  
   173  	// 判断是否润年
   174  	if t.IsLeapYear() {
   175  		if month > 2 {
   176  			return dayOfMonth[month-1] + day
   177  		}
   178  		return dayOfMonth[month-1] + day - 1
   179  	}
   180  	return dayOfMonth[month-1] + day - 1
   181  }
   182  
   183  // 一个时间点所在的月最长有多少天 28至31
   184  func (t *Time) DaysInMonth() int {
   185  	switch t.Month() {
   186  	case 1, 3, 5, 7, 8, 10, 12:
   187  		return 31
   188  	case 4, 6, 9, 11:
   189  		return 30
   190  	}
   191  
   192  	// 只剩下第二月份,润年29天
   193  	if t.IsLeapYear() {
   194  		return 29
   195  	}
   196  	return 28
   197  }
   198  
   199  // 获取时间点在本年内是第多少周
   200  func (t *Time) WeeksOfYear() int {
   201  	_, week := t.ISOWeek()
   202  	return week
   203  }
   204  
   205  // 将自定义的格式转换为标准库时间格式
   206  func formatToStdLayout(format string) string {
   207  	b := bytes.NewBuffer(nil)
   208  	for i := 0; i < len(format); {
   209  		switch format[i] {
   210  		case '\\':
   211  			if i < len(format)-1 {
   212  				b.WriteByte(format[i+1])
   213  				i += 2
   214  				continue
   215  			} else {
   216  				return b.String()
   217  			}
   218  
   219  		default:
   220  			if f, ok := formats[format[i]]; ok {
   221  				// 有几个转换的符号需要特殊处理
   222  				switch format[i] {
   223  				case 'j':
   224  					b.WriteString("02")
   225  				case 'G':
   226  					b.WriteString("15")
   227  				case 'u':
   228  					if i > 0 && format[i-1] == '.' {
   229  						b.WriteString("000")
   230  					} else {
   231  						b.WriteString(".000")
   232  					}
   233  
   234  				default:
   235  					b.WriteString(f)
   236  				}
   237  			} else {
   238  				b.WriteByte(format[i])
   239  			}
   240  			i++
   241  		}
   242  	}
   243  	return b.String()
   244  }
   245  
   246  // 将format格式转换为正则表达式规则
   247  func formatToRegexPattern(format string) string {
   248  	s := gregex.Quote(formatToStdLayout(format))
   249  	s, _ = gregex.ReplaceString(`[0-9]`, `[0-9]`, s)
   250  	s, _ = gregex.ReplaceString(`[A-Za-z]`, `[A-Za-z]`, s)
   251  	return s
   252  }
   253  
   254  // 每月天数后面的英文后缀,2 个字符st nd,rd 或者 th
   255  func formatMonthDaySuffixMap(day string) string {
   256  	switch day {
   257  	case "01":
   258  		return "st"
   259  	case "02":
   260  		return "nd"
   261  	case "03":
   262  		return "rd"
   263  	default:
   264  		return "th"
   265  	}
   266  }