date-fns-extra.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. /* eslint-disable max-len */
  2. /* eslint-disable eqeqeq */
  3. import {
  4. toDate,
  5. format as dateFnsFormat,
  6. utcToZonedTime as dateFnsUtcToZonedTime,
  7. zonedTimeToUtc as dateFnsZonedTimeToUtc,
  8. OptionsWithTZ
  9. } from 'date-fns-tz';
  10. import { parse as dateFnsParse } from 'date-fns';
  11. /**
  12. * Need to be IANA logo without daylight saving time
  13. */
  14. export const IANAOffsetMap = [
  15. [-11, ['Pacific/Midway']],
  16. [-10, ['Pacific/Honolulu']],
  17. [-9.5, ['Pacific/Marquesas']],
  18. [-9, ['Pacific/Gambier']],
  19. [-8, ['Pacific/Pitcairn']],
  20. [-7, ['America/Phoenix']],
  21. [-6, ['America/Tegucigalpa']],
  22. [-5, ['America/Bogota']],
  23. [-4, ['America/Puerto_Rico']],
  24. [-3.5, ['America/St_Johns']], // No alternative daylight saving time zone
  25. [-3, ['America/Montevideo']],
  26. [-2, ['Atlantic/South_Georgia']],
  27. [-1, ['Atlantic/Cape_Verde']],
  28. [0, ['Africa/Accra']],
  29. [1, ['Africa/Bangui']],
  30. [2, ['Africa/Cairo']],
  31. [3, ['Asia/Bahrain', 'Indian/Antananarivo']],
  32. [3.5, ['Asia/Tehran']], // No alternative daylight saving time zone
  33. [4, ['Asia/Dubai', 'Asia/Muscat']],
  34. [4.5, ['Asia/Kabul']],
  35. [5, ['Asia/Samarkand', 'Asia/Karachi']],
  36. [5.5, ['Asia/Kolkata']],
  37. [5.75, ['Asia/Kathmandu']],
  38. [6, ['Asia/Dhaka']],
  39. [6.5, ['Asia/Rangoon', 'Asia/Rangoon']],
  40. [7, ['Asia/Jakarta', 'Asia/Phnom_Penh', 'Asia/Bangkok']],
  41. [8, ['Asia/Shanghai', 'Asia/Singapore']],
  42. [8.75, ['Australia/Eucla']],
  43. [9, ['Asia/Tokyo', 'Asia/Seoul', 'Asia/Pyongyang']],
  44. [9.5, ['Australia/Darwin']],
  45. [10, ['Pacific/Guam']],
  46. [10.5, ['Australia/Adelaide']], // No alternative daylight saving time zone
  47. [11, ['Pacific/Guadalcanal']],
  48. [12, ['Pacific/Funafuti']],
  49. [13, ['Pacific/Enderbury']],
  50. [13.75, ['Pacific/Chatham']], // No alternative daylight saving time zone
  51. [14, ['Pacific/Kiritimati']],
  52. ];
  53. const GMTStringReg = /([\-\+]{1})(\d{2})\:(\d{2})/;
  54. /**
  55. *
  56. * @param {string|number} tz
  57. * @returns {number|undefined}
  58. */
  59. export const toIANA = (tz: string | number) => {
  60. let matches = null;
  61. if (typeof tz === 'string') {
  62. matches = tz.match(GMTStringReg);
  63. if (!matches) {
  64. return tz;
  65. }
  66. const symbol = parseInt(matches[1] + 1, 10); // => -1 or 1
  67. const hourOffset = parseInt(matches[2], 10);
  68. const minuteOffset = parseInt(matches[3], 10);
  69. tz = symbol * (hourOffset + minuteOffset / 60);
  70. }
  71. if (typeof tz === 'number') {
  72. const found = IANAOffsetMap.find(item => item[0] === tz);
  73. return found && found[1][0];
  74. }
  75. };
  76. /**
  77. *
  78. * @param {string | number | Date} date
  79. * @param {string} formatToken
  80. * @param {object} [options]
  81. * @param {string} [options.timeZone]
  82. * @returns {Date}
  83. */
  84. /* istanbul ignore next */
  85. const parse = (date: string | number | Date, formatToken: string, options?: any) => {
  86. if (typeof date === 'string') {
  87. date = dateFnsParse(date, formatToken, new Date(), options);
  88. }
  89. if (options && options.timeZone != null && options.timeZone !== '') {
  90. const timeZone = toIANA(options.timeZone);
  91. options = { ...options, timeZone };
  92. }
  93. return toDate(date, options);
  94. };
  95. /**
  96. *
  97. * @param {string | number | Date} date
  98. * @param {string} formatToken
  99. * @param {object} [options]
  100. * @param {string} [options.timeZone]
  101. */
  102. /* istanbul ignore next */
  103. const format = (date: string | number | Date, formatToken: string, options?: any) => {
  104. if (options && options.timeZone != null && options.timeZone !== '') {
  105. const timeZone = toIANA(options.timeZone);
  106. options = { ...options, timeZone };
  107. date = dateFnsUtcToZonedTime(date, timeZone, options);
  108. }
  109. return dateFnsFormat(date, formatToken, options);
  110. };
  111. /**
  112. *
  113. * @param {string | number | Date} date
  114. * @param {string} timeZone
  115. * @param {object} options
  116. * @returns {Date}
  117. */
  118. const utcToZonedTime = (date: string | number | Date, timeZone: string, options?: OptionsWithTZ) => dateFnsUtcToZonedTime(date, toIANA(timeZone), options);
  119. /**
  120. *
  121. * @param {string | number | Date} date
  122. * @param {string} timeZone
  123. * @param {object} options
  124. * @returns {Date}
  125. */
  126. const zonedTimeToUtc = (date: string | number | Date, timeZone: string, options?: OptionsWithTZ) => dateFnsZonedTimeToUtc(date, toIANA(timeZone), options);
  127. /**
  128. * return current system hour offset based on utc:
  129. *
  130. * ```
  131. * 8 => "GMT+08:00"
  132. * -9.5 => "GMT-09:30"
  133. * -8 => "GMT-08:00"
  134. * ```
  135. */
  136. const getCurrentTimeZone = () => new Date().getTimezoneOffset() / 60;
  137. export { format, parse, utcToZonedTime, zonedTimeToUtc, getCurrentTimeZone };