import zeroFill from '../utils/zero-fill'; import { createDuration } from '../duration/create'; import { addSubtract } from '../moment/add-subtract'; import { isMoment, copyConfig } from '../moment/constructor'; import { addFormatToken } from '../format/format'; import { addRegexToken, matchOffset } from '../parse/regex'; import { addParseToken } from '../parse/token'; import { createLocal } from '../create/local'; import { prepareConfig } from '../create/from-anything'; import { createUTC } from '../create/utc'; import isDate from '../utils/is-date'; import toInt from '../utils/to-int'; import compareArrays from '../utils/compare-arrays'; import { hooks } from '../utils/hooks'; // FORMATTING function offset (token, separator) { addFormatToken(token, 0, 0, function () { var offset = this.utcOffset(); var sign = '+'; if (offset < 0) { offset = -offset; sign = '-'; } return sign + zeroFill(~~(offset / 60), 2) + separator + zeroFill(~~(offset) % 60, 2); }); } offset('Z', ':'); offset('ZZ', ''); // PARSING addRegexToken('Z', matchOffset); addRegexToken('ZZ', matchOffset); addParseToken(['Z', 'ZZ'], function (input, array, config) { config._useUTC = true; config._tzm = offsetFromString(input); }); // HELPERS // timezone chunker // '+10:00' > ['10', '00'] // '-1530' > ['-15', '30'] var chunkOffset = /([\+\-]|\d\d)/gi; function offsetFromString(string) { var matches = ((string || '').match(matchOffset) || []); var chunk = matches[matches.length - 1] || []; var parts = (chunk + '').match(chunkOffset) || ['-', 0, 0]; var minutes = +(parts[1] * 60) + toInt(parts[2]); return parts[0] === '+' ? minutes : -minutes; } // Return a moment from input, that is local/utc/zone equivalent to model. export function cloneWithOffset(input, model) { var res, diff; if (model._isUTC) { res = model.clone(); diff = (isMoment(input) || isDate(input) ? +input : +createLocal(input)) - (+res); // Use low-level api, because this fn is low-level api. res._d.setTime(+res._d + diff); hooks.updateOffset(res, false); return res; } else { return createLocal(input).local(); } } function getDateOffset (m) { // On Firefox.24 Date#getTimezoneOffset returns a floating point. // https://github.com/moment/moment/pull/1871 return -Math.round(m._d.getTimezoneOffset() / 15) * 15; } // HOOKS // This function will be called whenever a moment is mutated. // It is intended to keep the offset in sync with the timezone. hooks.updateOffset = function () {}; // MOMENTS // keepLocalTime = true means only change the timezone, without // affecting the local hour. So 5:31:26 +0300 --[utcOffset(2, true)]--> // 5:31:26 +0200 It is possible that 5:31:26 doesn't exist with offset // +0200, so we adjust the time as needed, to be valid. // // Keeping the time actually adds/subtracts (one hour) // from the actual represented time. That is why we call updateOffset // a second time. In case it wants us to change the offset again // _changeInProgress == true case, then we have to adjust, because // there is no such time in the given timezone. export function getSetOffset (input, keepLocalTime) { var offset = this._offset || 0, localAdjust; if (input != null) { if (typeof input === 'string') { input = offsetFromString(input); } if (Math.abs(input) < 16) { input = input * 60; } if (!this._isUTC && keepLocalTime) { localAdjust = getDateOffset(this); } this._offset = input; this._isUTC = true; if (localAdjust != null) { this.add(localAdjust, 'm'); } if (offset !== input) { if (!keepLocalTime || this._changeInProgress) { addSubtract(this, createDuration(input - offset, 'm'), 1, false); } else if (!this._changeInProgress) { this._changeInProgress = true; hooks.updateOffset(this, true); this._changeInProgress = null; } } return this; } else { return this._isUTC ? offset : getDateOffset(this); } } export function getSetZone (input, keepLocalTime) { if (input != null) { if (typeof input !== 'string') { input = -input; } this.utcOffset(input, keepLocalTime); return this; } else { return -this.utcOffset(); } } export function setOffsetToUTC (keepLocalTime) { return this.utcOffset(0, keepLocalTime); } export function setOffsetToLocal (keepLocalTime) { if (this._isUTC) { this.utcOffset(0, keepLocalTime); this._isUTC = false; if (keepLocalTime) { this.subtract(getDateOffset(this), 'm'); } } return this; } export function setOffsetToParsedOffset () { if (this._tzm) { this.utcOffset(this._tzm); } else if (typeof this._i === 'string') { this.utcOffset(offsetFromString(this._i)); } return this; } export function hasAlignedHourOffset (input) { input = input ? createLocal(input).utcOffset() : 0; return (this.utcOffset() - input) % 60 === 0; } export function isDaylightSavingTime () { return ( this.utcOffset() > this.clone().month(0).utcOffset() || this.utcOffset() > this.clone().month(5).utcOffset() ); } export function isDaylightSavingTimeShifted () { if (typeof this._isDSTShifted !== 'undefined') { return this._isDSTShifted; } var c = {}; copyConfig(c, this); c = prepareConfig(c); if (c._a) { var other = c._isUTC ? createUTC(c._a) : createLocal(c._a); this._isDSTShifted = this.isValid() && compareArrays(c._a, other.toArray()) > 0; } else { this._isDSTShifted = false; } return this._isDSTShifted; } export function isLocal () { return !this._isUTC; } export function isUtcOffset () { return this._isUTC; } export function isUtc () { return this._isUTC && this._offset === 0; }