lightFormat.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import { lightFormatters } from "./_lib/format/lightFormatters.js";
  2. import { isValid } from "./isValid.js";
  3. import { toDate } from "./toDate.js";
  4. // Rexports of internal for libraries to use.
  5. // See: https://github.com/date-fns/date-fns/issues/3638#issuecomment-1877082874
  6. export { lightFormatters };
  7. // This RegExp consists of three parts separated by `|`:
  8. // - (\w)\1* matches any sequences of the same letter
  9. // - '' matches two quote characters in a row
  10. // - '(''|[^'])+('|$) matches anything surrounded by two quote characters ('),
  11. // except a single quote symbol, which ends the sequence.
  12. // Two quote characters do not end the sequence.
  13. // If there is no matching single quote
  14. // then the sequence will continue until the end of the string.
  15. // - . matches any single character unmatched by previous parts of the RegExps
  16. const formattingTokensRegExp = /(\w)\1*|''|'(''|[^'])+('|$)|./g;
  17. const escapedStringRegExp = /^'([^]*?)'?$/;
  18. const doubleQuoteRegExp = /''/g;
  19. const unescapedLatinCharacterRegExp = /[a-zA-Z]/;
  20. /**
  21. * @private
  22. */
  23. /**
  24. * @name lightFormat
  25. * @category Common Helpers
  26. * @summary Format the date.
  27. *
  28. * @description
  29. * Return the formatted date string in the given format. Unlike `format`,
  30. * `lightFormat` doesn't use locales and outputs date using the most popular tokens.
  31. *
  32. * > ⚠️ Please note that the `lightFormat` tokens differ from Moment.js and other libraries.
  33. * > See: https://github.com/date-fns/date-fns/blob/master/docs/unicodeTokens.md
  34. *
  35. * The characters wrapped between two single quotes characters (') are escaped.
  36. * Two single quotes in a row, whether inside or outside a quoted sequence, represent a 'real' single quote.
  37. *
  38. * Format of the string is based on Unicode Technical Standard #35:
  39. * https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
  40. *
  41. * Accepted patterns:
  42. * | Unit | Pattern | Result examples |
  43. * |---------------------------------|---------|-----------------------------------|
  44. * | AM, PM | a..aaa | AM, PM |
  45. * | | aaaa | a.m., p.m. |
  46. * | | aaaaa | a, p |
  47. * | Calendar year | y | 44, 1, 1900, 2017 |
  48. * | | yy | 44, 01, 00, 17 |
  49. * | | yyy | 044, 001, 000, 017 |
  50. * | | yyyy | 0044, 0001, 1900, 2017 |
  51. * | Month (formatting) | M | 1, 2, ..., 12 |
  52. * | | MM | 01, 02, ..., 12 |
  53. * | Day of month | d | 1, 2, ..., 31 |
  54. * | | dd | 01, 02, ..., 31 |
  55. * | Hour [1-12] | h | 1, 2, ..., 11, 12 |
  56. * | | hh | 01, 02, ..., 11, 12 |
  57. * | Hour [0-23] | H | 0, 1, 2, ..., 23 |
  58. * | | HH | 00, 01, 02, ..., 23 |
  59. * | Minute | m | 0, 1, ..., 59 |
  60. * | | mm | 00, 01, ..., 59 |
  61. * | Second | s | 0, 1, ..., 59 |
  62. * | | ss | 00, 01, ..., 59 |
  63. * | Fraction of second | S | 0, 1, ..., 9 |
  64. * | | SS | 00, 01, ..., 99 |
  65. * | | SSS | 000, 001, ..., 999 |
  66. * | | SSSS | ... |
  67. *
  68. * @param date - The original date
  69. * @param format - The string of tokens
  70. *
  71. * @returns The formatted date string
  72. *
  73. * @throws `Invalid time value` if the date is invalid
  74. * @throws format string contains an unescaped latin alphabet character
  75. *
  76. * @example
  77. * const result = lightFormat(new Date(2014, 1, 11), 'yyyy-MM-dd')
  78. * //=> '2014-02-11'
  79. */
  80. export function lightFormat(date, formatStr) {
  81. const date_ = toDate(date);
  82. if (!isValid(date_)) {
  83. throw new RangeError("Invalid time value");
  84. }
  85. const tokens = formatStr.match(formattingTokensRegExp);
  86. // The only case when formattingTokensRegExp doesn't match the string is when it's empty
  87. if (!tokens) return "";
  88. const result = tokens
  89. .map((substring) => {
  90. // Replace two single quote characters with one single quote character
  91. if (substring === "''") {
  92. return "'";
  93. }
  94. const firstCharacter = substring[0];
  95. if (firstCharacter === "'") {
  96. return cleanEscapedString(substring);
  97. }
  98. const formatter = lightFormatters[firstCharacter];
  99. if (formatter) {
  100. return formatter(date_, substring);
  101. }
  102. if (firstCharacter.match(unescapedLatinCharacterRegExp)) {
  103. throw new RangeError(
  104. "Format string contains an unescaped latin alphabet character `" +
  105. firstCharacter +
  106. "`",
  107. );
  108. }
  109. return substring;
  110. })
  111. .join("");
  112. return result;
  113. }
  114. function cleanEscapedString(input) {
  115. const matches = input.match(escapedStringRegExp);
  116. if (!matches) return input;
  117. return matches[1].replace(doubleQuoteRegExp, "'");
  118. }
  119. // Fallback for modularized imports:
  120. export default lightFormat;