formatRFC3339.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import { addLeadingZeros } from "./_lib/addLeadingZeros.js";
  2. import { isValid } from "./isValid.js";
  3. import { toDate } from "./toDate.js";
  4. /**
  5. * The {@link formatRFC3339} function options.
  6. */
  7. /**
  8. * @name formatRFC3339
  9. * @category Common Helpers
  10. * @summary Format the date according to the RFC 3339 standard (https://tools.ietf.org/html/rfc3339#section-5.6).
  11. *
  12. * @description
  13. * Return the formatted date string in RFC 3339 format. Options may be passed to control the parts and notations of the date.
  14. *
  15. * @param date - The original date
  16. * @param options - An object with options.
  17. *
  18. * @returns The formatted date string
  19. *
  20. * @throws `date` must not be Invalid Date
  21. *
  22. * @example
  23. * // Represent 18 September 2019 in RFC 3339 format:
  24. * formatRFC3339(new Date(2019, 8, 18, 19, 0, 52))
  25. * //=> '2019-09-18T19:00:52Z'
  26. *
  27. * @example
  28. * // Represent 18 September 2019 in RFC 3339 format, 3 digits of second fraction
  29. * formatRFC3339(new Date(2019, 8, 18, 19, 0, 52, 234), {
  30. * fractionDigits: 3
  31. * })
  32. * //=> '2019-09-18T19:00:52.234Z'
  33. */
  34. export function formatRFC3339(date, options) {
  35. const date_ = toDate(date, options?.in);
  36. if (!isValid(date_)) {
  37. throw new RangeError("Invalid time value");
  38. }
  39. const fractionDigits = options?.fractionDigits ?? 0;
  40. const day = addLeadingZeros(date_.getDate(), 2);
  41. const month = addLeadingZeros(date_.getMonth() + 1, 2);
  42. const year = date_.getFullYear();
  43. const hour = addLeadingZeros(date_.getHours(), 2);
  44. const minute = addLeadingZeros(date_.getMinutes(), 2);
  45. const second = addLeadingZeros(date_.getSeconds(), 2);
  46. let fractionalSecond = "";
  47. if (fractionDigits > 0) {
  48. const milliseconds = date_.getMilliseconds();
  49. const fractionalSeconds = Math.trunc(
  50. milliseconds * Math.pow(10, fractionDigits - 3),
  51. );
  52. fractionalSecond = "." + addLeadingZeros(fractionalSeconds, fractionDigits);
  53. }
  54. let offset = "";
  55. const tzOffset = date_.getTimezoneOffset();
  56. if (tzOffset !== 0) {
  57. const absoluteOffset = Math.abs(tzOffset);
  58. const hourOffset = addLeadingZeros(Math.trunc(absoluteOffset / 60), 2);
  59. const minuteOffset = addLeadingZeros(absoluteOffset % 60, 2);
  60. // If less than 0, the sign is +, because it is ahead of time.
  61. const sign = tzOffset < 0 ? "+" : "-";
  62. offset = `${sign}${hourOffset}:${minuteOffset}`;
  63. } else {
  64. offset = "Z";
  65. }
  66. return `${year}-${month}-${day}T${hour}:${minute}:${second}${fractionalSecond}${offset}`;
  67. }
  68. // Fallback for modularized imports:
  69. export default formatRFC3339;