gtime_format.go 8.8 KB

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