packages/core/src/zone/SystemDefaultZoneRules.js
/*
* @copyright (c) 2016, Philipp Thürwächter & Pattrick Hüper
* @license BSD-3-Clause (see LICENSE in the root directory of this source tree)
*/
import { ZoneRules } from './ZoneRules';
import { ZoneOffset } from '../ZoneOffset';
import { DateTimeException } from '../errors';
// the range of a Date in epoch millis (ECMA-262), a Date outside of it is invalid
const MAX_DATE_EPOCH_MILLI = 8640000000000000;
// the timezone offset of a Date in minutes, as Date.getTimezoneOffset(); an epoch milli outside
// the range of a Date is clamped to it, so that far instants get the offset at the end of the range
function timezoneOffsetInMinutes(epochMilli) {
const clamped = Math.max(-MAX_DATE_EPOCH_MILLI, Math.min(MAX_DATE_EPOCH_MILLI, epochMilli));
return new Date(clamped).getTimezoneOffset();
}
// the epoch milli of the instant, clamped to the range of a Date; Instant.toEpochMilli() would
// overflow for instants like Instant.MAX
function clampedEpochMilli(instant) {
const epochSecond = instant.epochSecond();
if (epochSecond > MAX_DATE_EPOCH_MILLI / 1000) {
return MAX_DATE_EPOCH_MILLI;
}
if (epochSecond < -MAX_DATE_EPOCH_MILLI / 1000) {
return -MAX_DATE_EPOCH_MILLI;
}
return instant.toEpochMilli();
}
export class SystemDefaultZoneRules extends ZoneRules {
isFixedOffset(){
return false;
}
/**
*
* @param {Instant} instant
* @returns {ZoneOffset}
*/
offsetOfInstant(instant){
const offsetInMinutes = timezoneOffsetInMinutes(clampedEpochMilli(instant));
return ZoneOffset.ofTotalMinutes(offsetInMinutes * -1);
}
/**
*
* @param {number} epochMilli
* @returns {ZoneOffset}
*/
offsetOfEpochMilli(epochMilli){
const offsetInMinutes = timezoneOffsetInMinutes(epochMilli);
return ZoneOffset.ofTotalMinutes(offsetInMinutes * -1);
}
/**
* This implementation is NOT returning the best value in a gap or overlap situation
* as specified at {@link ZoneRules.offsetOfLocalDateTime}.
*
* The calculated offset depends Date.prototype.getTimezoneOffset and its not specified
* at the ECMA-262 specification how to handle daylight savings gaps/ overlaps.
*
* The Chrome Browser version 49 is returning the next transition offset in a gap/overlap situation,
* other browsers/ engines might do it in the same way.
*
* @param {LocalDateTime} localDateTime
* @returns {ZoneOffset}
*/
offsetOfLocalDateTime(localDateTime){
const epochMilli = localDateTime.toEpochSecond(ZoneOffset.UTC) * 1000;
const offsetInMinutesBeforePossibleTransition = timezoneOffsetInMinutes(epochMilli);
const epochMilliSystemZone = epochMilli + offsetInMinutesBeforePossibleTransition * 60000;
const offsetInMinutesAfterPossibleTransition = timezoneOffsetInMinutes(epochMilliSystemZone);
return ZoneOffset.ofTotalMinutes(offsetInMinutesAfterPossibleTransition * -1);
}
/**
*
* @param localDateTime
* @return {ZoneOffset[]}
*/
validOffsets(localDateTime){
return [this.offsetOfLocalDateTime(localDateTime)];
}
/**
* @return null, not supported
*/
transition(){
return null;
}
/**
*
* @param instant
* @return {ZoneOffset}
*/
standardOffset(instant){
return this.offsetOfInstant(instant);
}
/**
* @throws DateTimeException not supported
*/
daylightSavings(){
this._throwNotSupported();
}
/**
* @throws DateTimeException not supported
*/
isDaylightSavings(){
this._throwNotSupported();
}
/**
*
* @param {LocalDateTime} dateTime
* @param {ZoneOffset} offset
* @return {boolean}
*/
isValidOffset(dateTime, offset) {
return this.offsetOfLocalDateTime(dateTime).equals(offset);
}
/**
* @throws DateTimeException not supported
*/
nextTransition(){
this._throwNotSupported();
}
/**
* @throws DateTimeException not supported
*/
previousTransition(){
this._throwNotSupported();
}
/**
* @throws DateTimeException not supported
*/
transitions(){
this._throwNotSupported();
}
/**
* @throws DateTimeException not supported
*/
transitionRules(){
this._throwNotSupported();
}
/**
* @throws DateTimeException not supported
*/
_throwNotSupported(){
throw new DateTimeException('not supported operation');
}
//-----------------------------------------------------------------------
/**
*
* @param {*} other
* @returns {boolean}
*/
equals(other) {
if (this === other || other instanceof SystemDefaultZoneRules) {
return true;
} else {
return false;
}
}
/**
*
* @returns {string}
*/
toString() {
return 'SYSTEM';
}
}