calendar.go 24 KB

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