9cf59d6daf91401f43b45b925a9ff66442e152eff2bde567918f3660b98b62525bda6ee805e585c22eef8b4a66b3baf1c6fa3c44e42ea097348b7c6538f372 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. import zeroFill from '../utils/zero-fill';
  2. import { createDuration } from '../duration/create';
  3. import { addSubtract } from '../moment/add-subtract';
  4. import { isMoment, copyConfig } from '../moment/constructor';
  5. import { addFormatToken } from '../format/format';
  6. import { addRegexToken, matchOffset, matchShortOffset } from '../parse/regex';
  7. import { addParseToken } from '../parse/token';
  8. import { createLocal } from '../create/local';
  9. import { prepareConfig } from '../create/from-anything';
  10. import { createUTC } from '../create/utc';
  11. import isDate from '../utils/is-date';
  12. import toInt from '../utils/to-int';
  13. import isUndefined from '../utils/is-undefined';
  14. import compareArrays from '../utils/compare-arrays';
  15. import { hooks } from '../utils/hooks';
  16. // FORMATTING
  17. function offset (token, separator) {
  18. addFormatToken(token, 0, 0, function () {
  19. var offset = this.utcOffset();
  20. var sign = '+';
  21. if (offset < 0) {
  22. offset = -offset;
  23. sign = '-';
  24. }
  25. return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2);
  26. });
  27. }
  28. offset('Z', ':');
  29. offset('ZZ', '');
  30. // PARSING
  31. addRegexToken('Z', matchShortOffset);
  32. addRegexToken('ZZ', matchShortOffset);
  33. addParseToken(['Z', 'ZZ'], function (input, array, config) {
  34. config._useUTC = true;
  35. config._tzm = offsetFromString(matchShortOffset, input);
  36. });
  37. // HELPERS
  38. // timezone chunker
  39. // '+10:00' > ['10', '00']
  40. // '-1530' > ['-15', '30']
  41. var chunkOffset = /([\+\-]|\d\d)/gi;
  42. function offsetFromString(matcher, string) {
  43. var matches = ((string || '').match(matcher) || []);
  44. var chunk = matches[matches.length - 1] || [];
  45. var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0];
  46. var minutes = +(parts[1] * 60) + toInt(parts[2]);
  47. return parts[0] === '+' ? minutes : -minutes;
  48. }
  49. // Return a moment from input, that is local/utc/zone equivalent to model.
  50. export function cloneWithOffset(input, model) {
  51. var res, diff;
  52. if (model._isUTC) {
  53. res = model.clone();
  54. diff = (isMoment(input) || isDate(input) ? input.valueOf() : createLocal(input).valueOf()) - res.valueOf();
  55. // Use low-level api, because this fn is low-level api.
  56. res._d.setTime(res._d.valueOf() + diff);
  57. hooks.updateOffset(res, false);
  58. return res;
  59. } else {
  60. return createLocal(input).local();
  61. }
  62. }
  63. function getDateOffset (m) {
  64. // On Firefox.24 Date#getTimezoneOffset returns a floating point.
  65. // https://github.com/moment/moment/pull/1871
  66. return -Math.round(m._d.getTimezoneOffset() / 15) * 15;
  67. }
  68. // HOOKS
  69. // This function will be called whenever a moment is mutated.
  70. // It is intended to keep the offset in sync with the timezone.
  71. hooks.updateOffset = function () {};
  72. // MOMENTS
  73. // keepLocalTime = true means only change the timezone, without
  74. // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]-->
  75. // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset
  76. // +0200, so we adjust the time as needed, to be valid.
  77. //
  78. // Keeping the time actually adds/subtracts (one hour)
  79. // from the actual represented time. That is why we call updateOffset
  80. // a second time. In case it wants us to change the offset again
  81. // _changeInProgress == true case, then we have to adjust, because
  82. // there is no such time in the given timezone.
  83. export function getSetOffset (input, keepLocalTime) {
  84. var offset = this._offset || 0,
  85. localAdjust;
  86. if (!this.isValid()) {
  87. return input != null ? this : NaN;
  88. }
  89. if (input != null) {
  90. if (typeof input === 'string') {
  91. input = offsetFromString(matchShortOffset, input);
  92. } else if (Math.abs(input) < 16) {
  93. input = input * 60;
  94. }
  95. if (!this._isUTC && keepLocalTime) {
  96. localAdjust = getDateOffset(this);
  97. }
  98. this._offset = input;
  99. this._isUTC = true;
  100. if (localAdjust != null) {
  101. this.add(localAdjust, 'm');
  102. }
  103. if (offset !== input) {
  104. if (!keepLocalTime || this._changeInProgress) {
  105. addSubtract(this, createDuration(input - offset, 'm'), 1, false);
  106. } else if (!this._changeInProgress) {
  107. this._changeInProgress = true;
  108. hooks.updateOffset(this, true);
  109. this._changeInProgress = null;
  110. }
  111. }
  112. return this;
  113. } else {
  114. return this._isUTC ? offset : getDateOffset(this);
  115. }
  116. }
  117. export function getSetZone (input, keepLocalTime) {
  118. if (input != null) {
  119. if (typeof input !== 'string') {
  120. input = -input;
  121. }
  122. this.utcOffset(input, keepLocalTime);
  123. return this;
  124. } else {
  125. return -this.utcOffset();
  126. }
  127. }
  128. export function setOffsetToUTC (keepLocalTime) {
  129. return this.utcOffset(0, keepLocalTime);
  130. }
  131. export function setOffsetToLocal (keepLocalTime) {
  132. if (this._isUTC) {
  133. this.utcOffset(0, keepLocalTime);
  134. this._isUTC = false;
  135. if (keepLocalTime) {
  136. this.subtract(getDateOffset(this), 'm');
  137. }
  138. }
  139. return this;
  140. }
  141. export function setOffsetToParsedOffset () {
  142. if (this._tzm) {
  143. this.utcOffset(this._tzm);
  144. } else if (typeof this._i === 'string') {
  145. var tZone = offsetFromString(matchOffset, this._i);
  146. if (tZone === 0) {
  147. this.utcOffset(0, true);
  148. } else {
  149. this.utcOffset(offsetFromString(matchOffset, this._i));
  150. }
  151. }
  152. return this;
  153. }
  154. export function hasAlignedHourOffset (input) {
  155. if (!this.isValid()) {
  156. return false;
  157. }
  158. input = input ? createLocal(input).utcOffset() : 0;
  159. return (this.utcOffset() - input) % 60 === 0;
  160. }
  161. export function isDaylightSavingTime () {
  162. return (
  163. this.utcOffset() > this.clone().month(0).utcOffset() ||
  164. this.utcOffset() > this.clone().month(5).utcOffset()
  165. );
  166. }
  167. export function isDaylightSavingTimeShifted () {
  168. if (!isUndefined(this._isDSTShifted)) {
  169. return this._isDSTShifted;
  170. }
  171. var c = {};
  172. copyConfig(c, this);
  173. c = prepareConfig(c);
  174. if (c._a) {
  175. var other = c._isUTC ? createUTC(c._a) : createLocal(c._a);
  176. this._isDSTShifted = this.isValid() &&
  177. compareArrays(c._a, other.toArray()) > 0;
  178. } else {
  179. this._isDSTShifted = false;
  180. }
  181. return this._isDSTShifted;
  182. }
  183. export function isLocal () {
  184. return this.isValid() ? !this._isUTC : false;
  185. }
  186. export function isUtcOffset () {
  187. return this.isValid() ? this._isUTC : false;
  188. }
  189. export function isUtc () {
  190. return this.isValid() ? this._isUTC && this._offset === 0 : false;
  191. }