calendar.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. package ut
  2. import (
  3. "errors"
  4. "fmt"
  5. "strings"
  6. "time"
  7. )
  8. const (
  9. am = "am"
  10. pm = "pm"
  11. )
  12. // Standard Formats for Dates, Times & DateTimes
  13. // These are the options to pass to the Format method.
  14. const (
  15. DateFormatFull = iota
  16. DateFormatLong
  17. DateFormatMedium
  18. DateFormatShort
  19. TimeFormatFull
  20. TimeFormatLong
  21. TimeFormatMedium
  22. TimeFormatShort
  23. DateTimeFormatFull
  24. DateTimeFormatLong
  25. DateTimeFormatMedium
  26. DateTimeFormatShort
  27. )
  28. // Characters with special meaning in a datetime string:
  29. // Technically, all a-z,A-Z characters should be treated as if they represent a
  30. // datetime unit - but not all actually do. Any a-z,A-Z character that is
  31. // intended to be rendered as a literal a-z,A-Z character should be surrounded
  32. // by single quotes. There is currently no support for rendering a single quote
  33. // literal.
  34. const (
  35. datetimeFormatUnitEra = 'G'
  36. datetimeFormatUnitYear = 'y'
  37. datetimeFormatUnitMonth = 'M'
  38. datetimeFormatUnitDayOfWeek = 'E'
  39. datetimeFormatUnitDay = 'd'
  40. datetimeFormatUnitHour12 = 'h'
  41. datetimeFormatUnitHour24 = 'H'
  42. datetimeFormatUnitMinute = 'm'
  43. datetimeFormatUnitSecond = 's'
  44. datetimeFormatUnitPeriod = 'a'
  45. datetimeForamtUnitQuarter = 'Q'
  46. datetimeFormatUnitTimeZone1 = 'z'
  47. datetimeFormatUnitTimeZone2 = 'v'
  48. datetimeFormatTimeSeparator = ':'
  49. datetimeFormatLiteral = '\''
  50. )
  51. // The sequence length of datetime unit characters indicates how they should be
  52. // rendered.
  53. const (
  54. datetimeFormatLength1Plus = 1
  55. datetimeFormatLength2Plus = 2
  56. datetimeFormatLengthAbbreviated = 3
  57. datetimeFormatLengthWide = 4
  58. datetimeFormatLengthNarrow = 5
  59. )
  60. // datetime formats are a sequences off datetime components and string literals
  61. const (
  62. datetimePatternComponentUnit = iota
  63. datetimePatternComponentLiteral
  64. )
  65. // A list of currently unsupported units:
  66. // These still need to be implemented. For now they are ignored.
  67. var (
  68. datetimeFormatUnitCutset = []rune{
  69. datetimeFormatUnitEra,
  70. datetimeForamtUnitQuarter,
  71. datetimeFormatUnitTimeZone1,
  72. datetimeFormatUnitTimeZone2,
  73. }
  74. )
  75. type datetimePatternComponent struct {
  76. pattern string
  77. componentType int
  78. }
  79. // FmtDateFull formats the time with the current locales full date format
  80. func (c Calendar) FmtDateFull(t time.Time) (string, error) {
  81. return c.Format(t, c.Formats.Date.Full)
  82. }
  83. // FmtDateLong formats the time with the current locales long date format
  84. func (c Calendar) FmtDateLong(t time.Time) (string, error) {
  85. return c.Format(t, c.Formats.Date.Long)
  86. }
  87. // FmtDateMedium formats the time with the current locales medium date format
  88. func (c Calendar) FmtDateMedium(t time.Time) (string, error) {
  89. return c.Format(t, c.Formats.Date.Medium)
  90. }
  91. // FmtDateShort formats the time with the current locales short date format
  92. func (c Calendar) FmtDateShort(t time.Time) (string, error) {
  93. return c.Format(t, c.Formats.Date.Short)
  94. }
  95. // FmtTimeFull formats the time with the current locales full time format
  96. func (c Calendar) FmtTimeFull(t time.Time) (string, error) {
  97. return c.Format(t, c.Formats.Time.Full)
  98. }
  99. // FmtTimeLong formats the time with the current locales long time format
  100. func (c Calendar) FmtTimeLong(t time.Time) (string, error) {
  101. return c.Format(t, c.Formats.Time.Long)
  102. }
  103. // FmtTimeMedium formats the time with the current locales medium time format
  104. func (c Calendar) FmtTimeMedium(t time.Time) (string, error) {
  105. return c.Format(t, c.Formats.Time.Medium)
  106. }
  107. // FmtTimeShort formats the time with the current locales short time format
  108. func (c Calendar) FmtTimeShort(t time.Time) (string, error) {
  109. return c.Format(t, c.Formats.Time.Short)
  110. }
  111. // FmtDateTimeFull formats the time with the current locales full data & time format
  112. func (c Calendar) FmtDateTimeFull(t time.Time) (string, error) {
  113. pattern := getDateTimePattern(c.Formats.DateTime.Full, c.Formats.Date.Full, c.Formats.Time.Full)
  114. return c.Format(t, pattern)
  115. }
  116. // FmtDateTimeLong formats the time with the current locales long data & time format
  117. func (c Calendar) FmtDateTimeLong(t time.Time) (string, error) {
  118. pattern := getDateTimePattern(c.Formats.DateTime.Long, c.Formats.Date.Long, c.Formats.Time.Long)
  119. return c.Format(t, pattern)
  120. }
  121. // FmtDateTimeMedium formats the time with the current locales medium data & time format
  122. func (c Calendar) FmtDateTimeMedium(t time.Time) (string, error) {
  123. pattern := getDateTimePattern(c.Formats.DateTime.Medium, c.Formats.Date.Medium, c.Formats.Time.Medium)
  124. return c.Format(t, pattern)
  125. }
  126. // FmtDateTimeShort formats the time with the current locales short data & time format
  127. func (c Calendar) FmtDateTimeShort(t time.Time) (string, error) {
  128. pattern := getDateTimePattern(c.Formats.DateTime.Short, c.Formats.Date.Short, c.Formats.Time.Short)
  129. return c.Format(t, pattern)
  130. }
  131. // Format takes a time struct and a format and returns a formatted
  132. // string. Callers should use a DateFormat, TimeFormat, or DateTimeFormat
  133. // constant.
  134. func (c Calendar) Format(datetime time.Time, pattern string) (string, error) {
  135. parsed, err := c.parseDateTimeFormat(pattern)
  136. if err != nil {
  137. return "", err
  138. }
  139. return c.formatDateTime(datetime, parsed)
  140. }
  141. // formatDateTime takes a time.Time and a sequence of parsed pattern components
  142. // and returns an internationalized string representation.
  143. func (c Calendar) formatDateTime(datetime time.Time, pattern []*datetimePatternComponent) (string, error) {
  144. formatted := ""
  145. for _, component := range pattern {
  146. if component.componentType == datetimePatternComponentLiteral {
  147. formatted += component.pattern
  148. } else {
  149. f, err := c.formatDateTimeComponent(datetime, component.pattern)
  150. if err != nil {
  151. return "", err
  152. }
  153. formatted += f
  154. }
  155. }
  156. return strings.Trim(formatted, " ,"), nil
  157. }
  158. // formatDateTimeComponent renders a single component of a datetime format
  159. // pattern.
  160. func (c Calendar) formatDateTimeComponent(datetime time.Time, pattern string) (string, error) {
  161. switch pattern[0:1] {
  162. case string(datetimeFormatUnitEra):
  163. return c.formatDateTimeComponentEra(datetime, len(pattern))
  164. case string(datetimeFormatUnitYear):
  165. return c.formatDateTimeComponentYear(datetime, len(pattern))
  166. case string(datetimeFormatUnitMonth):
  167. return c.formatDateTimeComponentMonth(datetime, len(pattern))
  168. case string(datetimeFormatUnitDayOfWeek):
  169. return c.formatDateTimeComponentDayOfWeek(datetime, len(pattern))
  170. case string(datetimeFormatUnitDay):
  171. return c.formatDateTimeComponentDay(datetime, len(pattern))
  172. case string(datetimeFormatUnitHour12):
  173. return c.formatDateTimeComponentHour12(datetime, len(pattern))
  174. case string(datetimeFormatUnitHour24):
  175. return c.formatDateTimeComponentHour24(datetime, len(pattern))
  176. case string(datetimeFormatUnitMinute):
  177. return c.formatDateTimeComponentMinute(datetime, len(pattern))
  178. case string(datetimeFormatUnitSecond):
  179. return c.formatDateTimeComponentSecond(datetime, len(pattern))
  180. case string(datetimeFormatUnitPeriod):
  181. return c.formatDateTimeComponentPeriod(datetime, len(pattern))
  182. case string(datetimeForamtUnitQuarter):
  183. return c.formatDateTimeComponentQuarter(datetime, len(pattern))
  184. case string(datetimeFormatUnitTimeZone1):
  185. fallthrough
  186. case string(datetimeFormatUnitTimeZone2):
  187. return c.formatDateTimeComponentTimeZone(datetime, len(pattern))
  188. }
  189. return "", errors.New("unknown datetime format unit: " + pattern[0:1])
  190. }
  191. // formatDateTimeComponentEra renders an era component.
  192. // TODO: not yet implemented
  193. func (c Calendar) formatDateTimeComponentEra(datetime time.Time, length int) (string, error) {
  194. return "", nil
  195. }
  196. // formatDateTimeComponentYear renders a year component.
  197. func (c Calendar) formatDateTimeComponentYear(datetime time.Time, length int) (string, error) {
  198. year := datetime.Year()
  199. switch length {
  200. case datetimeFormatLength1Plus:
  201. return c.formatDateTimeComponentYearLengthWide(year), nil
  202. case datetimeFormatLength2Plus:
  203. return c.formatDateTimeComponentYearLength2Plus(year), nil
  204. case datetimeFormatLengthWide:
  205. return c.formatDateTimeComponentYearLengthWide(year), nil
  206. }
  207. return "", fmt.Errorf("unsupported year length: %d", length)
  208. }
  209. // formatDateTimeComponentYearLength2Plus renders a 2-digit year component.
  210. func (c Calendar) formatDateTimeComponentYearLength2Plus(year int) string {
  211. yearShort := year % 100
  212. if yearShort < 10 {
  213. return fmt.Sprintf("0%d", yearShort)
  214. }
  215. return fmt.Sprintf("%d", yearShort)
  216. }
  217. // formatDateTimeComponentYearLength2Plus renders a full-year component - for
  218. // all modern dates, that's four digits.
  219. func (c Calendar) formatDateTimeComponentYearLengthWide(year int) string {
  220. return fmt.Sprintf("%d", year)
  221. }
  222. // formatDateTimeComponentMonth renders a month component.
  223. func (c Calendar) formatDateTimeComponentMonth(datetime time.Time, length int) (string, error) {
  224. month := datetime.Month()
  225. switch length {
  226. case datetimeFormatLength1Plus:
  227. return c.formatDateTimeComponentMonth1Plus(month), nil
  228. case datetimeFormatLength2Plus:
  229. return c.formatDateTimeComponentMonth2Plus(month), nil
  230. case datetimeFormatLengthAbbreviated:
  231. return c.formatDateTimeComponentMonthAbbreviated(month), nil
  232. case datetimeFormatLengthWide:
  233. return c.formatDateTimeComponentMonthWide(month), nil
  234. case datetimeFormatLengthNarrow:
  235. return c.formatDateTimeComponentMonthNarrow(month), nil
  236. }
  237. return "", fmt.Errorf("unsupported month length: %d", length)
  238. }
  239. // formatDateTimeComponentMonth1Plus renders a numeric month component with 1 or
  240. // 2 digits depending on value.
  241. func (c Calendar) formatDateTimeComponentMonth1Plus(month time.Month) string {
  242. return fmt.Sprintf("%d", month)
  243. }
  244. // formatDateTimeComponentMonth2Plus renders a numeric month component always
  245. // with 2 digits.
  246. func (c Calendar) formatDateTimeComponentMonth2Plus(month time.Month) string {
  247. if month < 10 {
  248. return fmt.Sprintf("0%d", month)
  249. }
  250. return fmt.Sprintf("%d", month)
  251. }
  252. // formatDateTimeComponentMonthAbbreviated renders an abbreviated text month
  253. // component.
  254. func (c Calendar) formatDateTimeComponentMonthAbbreviated(month time.Month) string {
  255. return c.FormatNames.Months.Abbreviated[month]
  256. }
  257. // formatDateTimeComponentMonthWide renders a full text month component.
  258. func (c Calendar) formatDateTimeComponentMonthWide(month time.Month) string {
  259. return c.FormatNames.Months.Wide[month]
  260. }
  261. // formatDateTimeComponentMonthNarrow renders a super-short month compontent -
  262. // not guaranteed to be unique for different months.
  263. func (c Calendar) formatDateTimeComponentMonthNarrow(month time.Month) string {
  264. return c.FormatNames.Months.Narrow[month]
  265. }
  266. // formatDateTimeComponentDayOfWeek renders a day-of-week component.
  267. func (c Calendar) formatDateTimeComponentDayOfWeek(datetime time.Time, length int) (string, error) {
  268. switch length {
  269. case datetimeFormatLength1Plus:
  270. return c.formatDateTimeComponentDayOfWeekWide(datetime.Weekday()), nil
  271. case datetimeFormatLength2Plus:
  272. return c.formatDateTimeComponentDayOfWeekShort(datetime.Weekday()), nil
  273. case datetimeFormatLengthAbbreviated:
  274. return c.formatDateTimeComponentDayOfWeekAbbreviated(datetime.Weekday()), nil
  275. case datetimeFormatLengthWide:
  276. return c.formatDateTimeComponentDayOfWeekWide(datetime.Weekday()), nil
  277. case datetimeFormatLengthNarrow:
  278. return c.formatDateTimeComponentDayOfWeekNarrow(datetime.Weekday()), nil
  279. }
  280. return "", fmt.Errorf("unsupported year day-of-week: %d", length)
  281. }
  282. // formatDateTimeComponentDayOfWeekAbbreviated renders an abbreviated text
  283. // day-of-week component.
  284. func (c Calendar) formatDateTimeComponentDayOfWeekAbbreviated(weekday time.Weekday) string {
  285. return c.FormatNames.Days.Abbreviated[weekday]
  286. }
  287. // formatDateTimeComponentDayOfWeekAbbreviated renders a
  288. // shorter-then-abbreviated but still unique text day-of-week component.
  289. func (c Calendar) formatDateTimeComponentDayOfWeekShort(weekday time.Weekday) string {
  290. return c.FormatNames.Days.Short[weekday]
  291. }
  292. // formatDateTimeComponentDayOfWeekWide renders a full text day-of-week
  293. // component.
  294. func (c Calendar) formatDateTimeComponentDayOfWeekWide(weekday time.Weekday) string {
  295. return c.FormatNames.Days.Wide[weekday]
  296. }
  297. // formatDateTimeComponentDayOfWeekNarrow renders a super-short day-of-week
  298. // compontent - not guaranteed to be unique for different days.
  299. func (c Calendar) formatDateTimeComponentDayOfWeekNarrow(weekday time.Weekday) string {
  300. return c.FormatNames.Days.Narrow[weekday]
  301. }
  302. // formatDateTimeComponentDay renders a day-of-year component.
  303. func (c Calendar) formatDateTimeComponentDay(datetime time.Time, length int) (string, error) {
  304. day := datetime.Day()
  305. switch length {
  306. case datetimeFormatLength1Plus:
  307. return fmt.Sprintf("%d", day), nil
  308. case datetimeFormatLength2Plus:
  309. if day < 10 {
  310. return fmt.Sprintf("0%d", day), nil
  311. }
  312. return fmt.Sprintf("%d", day), nil
  313. }
  314. return "", fmt.Errorf("unsupported day-of-year: %d", length)
  315. }
  316. // formatDateTimeComponentHour12 renders an hour-component using a 12-hour
  317. // clock.
  318. func (c Calendar) formatDateTimeComponentHour12(datetime time.Time, length int) (string, error) {
  319. hour := datetime.Hour()
  320. if hour > 12 {
  321. hour = hour - 12
  322. }
  323. switch length {
  324. case datetimeFormatLength1Plus:
  325. return fmt.Sprintf("%d", hour), nil
  326. case datetimeFormatLength2Plus:
  327. if hour < 10 {
  328. return fmt.Sprintf("0%d", hour), nil
  329. }
  330. return fmt.Sprintf("%d", hour), nil
  331. }
  332. return "", fmt.Errorf("unsupported hour-12: %d", length)
  333. }
  334. // formatDateTimeComponentHour24 renders an hour-component using a 24-hour
  335. // clock.
  336. func (c Calendar) formatDateTimeComponentHour24(datetime time.Time, length int) (string, error) {
  337. hour := datetime.Hour()
  338. switch length {
  339. case datetimeFormatLength1Plus:
  340. return fmt.Sprintf("%d", hour), nil
  341. case datetimeFormatLength2Plus:
  342. if hour < 10 {
  343. return fmt.Sprintf("0%d", hour), nil
  344. }
  345. return fmt.Sprintf("%d", hour), nil
  346. }
  347. return "", fmt.Errorf("unsupported hour-24: %d", length)
  348. }
  349. // formatDateTimeComponentMinute renders a minute component.
  350. func (c Calendar) formatDateTimeComponentMinute(datetime time.Time, length int) (string, error) {
  351. minute := datetime.Minute()
  352. switch length {
  353. case datetimeFormatLength1Plus:
  354. return fmt.Sprintf("%d", minute), nil
  355. case datetimeFormatLength2Plus:
  356. if minute < 10 {
  357. return fmt.Sprintf("0%d", minute), nil
  358. }
  359. return fmt.Sprintf("%d", minute), nil
  360. }
  361. return "", fmt.Errorf("unsupported minute: %d", length)
  362. }
  363. // formatDateTimeComponentSecond renders a second component
  364. func (c Calendar) formatDateTimeComponentSecond(datetime time.Time, length int) (string, error) {
  365. second := datetime.Second()
  366. switch length {
  367. case datetimeFormatLength1Plus:
  368. return fmt.Sprintf("%d", second), nil
  369. case datetimeFormatLength2Plus:
  370. if second < 10 {
  371. return fmt.Sprintf("0%d", second), nil
  372. }
  373. return fmt.Sprintf("%d", second), nil
  374. }
  375. return "", fmt.Errorf("unsupported second: %d", length)
  376. }
  377. // formatDateTimeComponentPeriod renders a period component (AM/PM).
  378. func (c Calendar) formatDateTimeComponentPeriod(datetime time.Time, length int) (string, error) {
  379. hour := datetime.Hour()
  380. switch length {
  381. case datetimeFormatLength1Plus:
  382. return c.formatDateTimeComponentPeriodWide(hour), nil
  383. case datetimeFormatLengthAbbreviated:
  384. return c.formatDateTimeComponentPeriodAbbreviated(hour), nil
  385. case datetimeFormatLengthWide:
  386. return c.formatDateTimeComponentPeriodWide(hour), nil
  387. case datetimeFormatLengthNarrow:
  388. return c.formatDateTimeComponentPeriodNarrow(hour), nil
  389. }
  390. return "", fmt.Errorf("unsupported day-period: %d", length)
  391. }
  392. // formatDateTimeComponentPeriodAbbreviated renders an abbreviated period
  393. // component.
  394. func (c Calendar) formatDateTimeComponentPeriodAbbreviated(hour int) string {
  395. if hour < 12 {
  396. return c.FormatNames.Periods.Abbreviated[am]
  397. }
  398. return c.FormatNames.Periods.Abbreviated[pm]
  399. }
  400. // formatDateTimeComponentPeriodWide renders a full period component.
  401. func (c Calendar) formatDateTimeComponentPeriodWide(hour int) string {
  402. if hour < 12 {
  403. return c.FormatNames.Periods.Wide[am]
  404. }
  405. return c.FormatNames.Periods.Wide[pm]
  406. }
  407. // formatDateTimeComponentPeriodNarrow renders a super-short period component.
  408. func (c Calendar) formatDateTimeComponentPeriodNarrow(hour int) string {
  409. if hour < 12 {
  410. return c.FormatNames.Periods.Narrow[am]
  411. }
  412. return c.FormatNames.Periods.Narrow[pm]
  413. }
  414. // formatDateTimeComponentQuarter renders a calendar quarter component - this
  415. // is calendar quarters and not fiscal quarters.
  416. // - Q1: Jan-Mar
  417. // - Q2: Apr-Jun
  418. // - Q3: Jul-Sep
  419. // - Q4: Oct-Dec
  420. // TODO: not yet implemented
  421. func (c Calendar) formatDateTimeComponentQuarter(datetime time.Time, length int) (string, error) {
  422. return "", nil
  423. }
  424. // formatDateTimeComponentTimeZone renders a time zone component.
  425. // TODO: this has not yet been implemented
  426. func (c Calendar) formatDateTimeComponentTimeZone(datetime time.Time, length int) (string, error) {
  427. return "", nil
  428. }
  429. // parseDateTimeFormat takes a format pattern string and returns a sequence of
  430. // components.
  431. func (c Calendar) parseDateTimeFormat(pattern string) ([]*datetimePatternComponent, error) {
  432. // every thing between single quotes should become a literal
  433. // all non a-z, A-Z characters become a literal
  434. // everything else, repeat character sequences become a component
  435. format := []*datetimePatternComponent{}
  436. for i := 0; i < len(pattern); {
  437. char := pattern[i : i+1]
  438. skip := false
  439. // for units we don't support yet, just skip over them
  440. for _, r := range datetimeFormatUnitCutset {
  441. if char == string(r) {
  442. skip = true
  443. break
  444. }
  445. }
  446. if skip {
  447. i++
  448. continue
  449. }
  450. if char == string(datetimeFormatLiteral) {
  451. // find the next single quote
  452. // create a literal out of everything between the quotes
  453. // and set i to the position after the second quote
  454. if i == len(pattern)-1 {
  455. return []*datetimePatternComponent{}, errors.New("malformed datetime format")
  456. }
  457. nextQuote := strings.Index(pattern[i+1:], string(datetimeFormatLiteral))
  458. if nextQuote == -1 {
  459. return []*datetimePatternComponent{}, errors.New("malformed datetime format")
  460. }
  461. component := &datetimePatternComponent{
  462. pattern: pattern[i+1 : nextQuote+i+1],
  463. componentType: datetimePatternComponentLiteral,
  464. }
  465. format = append(format, component)
  466. i = nextQuote + i + 2
  467. continue
  468. }
  469. if (char >= "a" && char <= "z") || (char >= "A" && char <= "Z") {
  470. // this represents a format unit
  471. // find the entire sequence of the same character
  472. endChar := lastSequenceIndex(pattern[i:]) + i
  473. component := &datetimePatternComponent{
  474. pattern: pattern[i : endChar+1],
  475. componentType: datetimePatternComponentUnit,
  476. }
  477. format = append(format, component)
  478. i = endChar + 1
  479. continue
  480. }
  481. if char == string(datetimeFormatTimeSeparator) {
  482. component := &datetimePatternComponent{
  483. // pattern: c.TimeSeparator,
  484. pattern: string(datetimeFormatTimeSeparator),
  485. componentType: datetimePatternComponentLiteral,
  486. }
  487. format = append(format, component)
  488. i++
  489. continue
  490. }
  491. component := &datetimePatternComponent{
  492. pattern: char,
  493. componentType: datetimePatternComponentLiteral,
  494. }
  495. format = append(format, component)
  496. i++
  497. continue
  498. }
  499. return format, nil
  500. }
  501. // getDateTimePattern combines a date pattern and a time pattern into a datetime
  502. // pattern. The datetimePattern argument includes a {0} placeholder for the time
  503. // pattern, and a {1} placeholder for the date component.
  504. func getDateTimePattern(datetimePattern, datePattern, timePattern string) string {
  505. return strings.Replace(strings.Replace(datetimePattern, "{1}", datePattern, 1), "{0}", timePattern, 1)
  506. }
  507. // lastSequenceIndex looks at the first character in a string and returns the
  508. // last digits of the first sequence of that character. For example:
  509. // - ABC: 0
  510. // - AAB: 1
  511. // - ABA: 0
  512. // - AAA: 2
  513. func lastSequenceIndex(str string) int {
  514. if len(str) == 0 {
  515. return -1
  516. }
  517. if len(str) == 1 {
  518. return 0
  519. }
  520. sequenceChar := str[0:1]
  521. lastPos := 0
  522. for i := 1; i < len(str); i++ {
  523. if str[i:i+1] != sequenceChar {
  524. break
  525. }
  526. lastPos = i
  527. }
  528. return lastPos
  529. }