From 39bb139279af1e82c0677c13c097462d78da1583 Mon Sep 17 00:00:00 2001 From: martynasma Date: Fri, 22 Sep 2023 17:00:12 +0300 Subject: [PATCH] Version 5.4.8 --- package.json | 2 +- packages/shared/CHANGELOG.md | 14 +++ packages/shared/LICENSE | 4 +- .../charts/stock/StockChartDefaultTheme.ts | 15 ++- .../charts/stock/indicators/Momentum.ts | 98 +++++++++++++++++++ .../stock/indicators/RelativeStrengthIndex.ts | 51 +++++----- .../charts/stock/toolbar/IndicatorControl.ts | 9 +- src/.internal/charts/xy/XYChart.ts | 5 +- src/.internal/charts/xy/axes/DateAxis.ts | 1 + src/.internal/charts/xy/series/XYSeries.ts | 22 ++++- src/.internal/core/Classes.ts | 2 + src/.internal/core/Registry.ts | 2 +- src/.internal/core/Root.ts | 18 ++++ src/.internal/core/util/DateFormatter.ts | 9 +- src/.internal/core/util/Entity.ts | 15 +-- src/.internal/core/util/Utils.ts | 11 ++- 16 files changed, 226 insertions(+), 52 deletions(-) create mode 100644 src/.internal/charts/stock/indicators/Momentum.ts diff --git a/package.json b/package.json index 6c5c64c7..5c081c17 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": true, "name": "@amcharts/amcharts5", - "version": "5.4.7", + "version": "5.4.8", "author": "amCharts (https://www.amcharts.com/)", "description": "amCharts 5", "homepage": "https://www.amcharts.com/", diff --git a/packages/shared/CHANGELOG.md b/packages/shared/CHANGELOG.md index 95411eec..9f219b91 100644 --- a/packages/shared/CHANGELOG.md +++ b/packages/shared/CHANGELOG.md @@ -5,6 +5,20 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/). Please note, that this project, while following numbering syntax, it DOES NOT adhere to [Semantic Versioning](http://semver.org/spec/v2.0.0.html) rules. +## [5.4.8] - 2023-09-22 + +### Added +- Momentum indicator added to `StockChart`. + +### Fixed +- `"z"` date formatting codes were not respecting the `Root.timezone` setting. +- If a new data was loaded and `groupData` of `DateAxis` was changed from `true` to `false`, the chart was not displaying the newly-loaded data properly. +- Accessibility: togglable elements like legend items were not being toggled by ENTER and SPACE keys with some screen reaaders (JAWS, Narrator) active. +- RST indicator of `StockChart` was always using "close" value even if a different value was selected in the settings. +- In some case chart could freeze when selecting with an `XYCursor` and releasing outside plot area. +- If a new animation of the same setting was created right after previous animation finished (on `Animation`'s `"stopped"` event) the animation would not play. + + ## [5.4.7] - 2023-09-18 ### Added diff --git a/packages/shared/LICENSE b/packages/shared/LICENSE index 128c6c46..b55d5e8d 100644 --- a/packages/shared/LICENSE +++ b/packages/shared/LICENSE @@ -12,7 +12,7 @@ This amCharts software is provided under linkware license, conditions of which a ### If the following conditions are met -* You do not disable, hide or alter the branding link which is displayed on all the content generated by amCharts software unless you provide some other adequately prominent attribution to amCharts. +* You do not disable, hide or alter the branding link which is displayed on all the content generated by amCharts software. * You include this original LICENSE file together with original (or modified) files from amCharts software. * Your own personal license does not supersede or in any way negate the effect of this LICENSE, or make the impression of doing so. @@ -20,7 +20,7 @@ This amCharts software is provided under linkware license, conditions of which a * Remove or alter this LICENSE file. * Remove any of the amCharts copyright notices from any of the files of amCharts software. -* Use amCharts software without any kind of prominent attribution (bundled or custom). Please see note about commercial amCharts licenses below. +* Use amCharts software without built-in attribution (logo). Please see note about commercial amCharts licenses below. * Sell or receive any compensation for amCharts software. * Distribute amCharts software on its own, not as part of other application. diff --git a/src/.internal/charts/stock/StockChartDefaultTheme.ts b/src/.internal/charts/stock/StockChartDefaultTheme.ts index 6331be2b..cec367a3 100644 --- a/src/.internal/charts/stock/StockChartDefaultTheme.ts +++ b/src/.internal/charts/stock/StockChartDefaultTheme.ts @@ -732,6 +732,11 @@ export class StockChartDefaultTheme extends Theme { legendLabelText: "{shortName} ({period.formatNumber('#.')},{field})" }) + r("LineSeries", ["momentum"]).setAll({ + legendValueText: "[{seriesColor} bold]{valueY.formatNumber('#.000a')}[/]", + legendLabelText: "{shortName} ({period.formatNumber('#.')},{field})" + }) + r("LineSeries", ["williamsr"]).setAll({ legendValueText: "[{seriesColor} bold]{valueY.formatNumber('#.000a')}[/]", legendLabelText: "{shortName} ({period.formatNumber('#.')})" @@ -1059,6 +1064,14 @@ export class StockChartDefaultTheme extends Theme { seriesColor: color(0xab82da) }) + r("Momentum").setAll({ + name: "Momentum", + shortName: "Mom", + period: 14, + field: "close", + seriesColor: color(0xab82da) + }) + r("WilliamsR").setAll({ name: "Williams %R", shortName: "Williams %R", @@ -1154,7 +1167,7 @@ export class StockChartDefaultTheme extends Theme { r("IndicatorControl").setAll({ name: l.translateAny("Indicators"), - indicators: ["Aroon", "Accumulation Distribution", "Accumulative Swing Index", "Awesome Oscillator", "Bollinger Bands", "Chaikin Money Flow", "Chaikin Oscillator", "Commodity Channel Index", "Disparity Index", "MACD", "Median Price", "Moving Average", "Moving Average Deviation", "Moving Average Envelope", "On Balance Volume", "Relative Strength Index", "Standard Deviation", "Stochastic Oscillator", "Trix", "Typical Price", "Volume", "VWAP", "Williams R", "ZigZag"] + indicators: ["Aroon", "Accumulation Distribution", "Accumulative Swing Index", "Awesome Oscillator", "Bollinger Bands", "Chaikin Money Flow", "Chaikin Oscillator", "Commodity Channel Index", "Disparity Index", "MACD", "Median Price", "Momentum", "Moving Average", "Moving Average Deviation", "Moving Average Envelope", "On Balance Volume", "Relative Strength Index", "Standard Deviation", "Stochastic Oscillator", "Trix", "Typical Price", "Volume", "VWAP", "Williams R", "ZigZag"] }); r("ComparisonControl").setAll({ diff --git a/src/.internal/charts/stock/indicators/Momentum.ts b/src/.internal/charts/stock/indicators/Momentum.ts new file mode 100644 index 00000000..159370fd --- /dev/null +++ b/src/.internal/charts/stock/indicators/Momentum.ts @@ -0,0 +1,98 @@ +import type { IIndicatorEditableSetting } from "./Indicator"; + +import { ChartIndicator, IChartIndicatorSettings, IChartIndicatorPrivate, IChartIndicatorEvents } from "./ChartIndicator"; +import { LineSeries } from "../../xy/series/LineSeries"; + +import * as $array from "../../../core/util/Array"; + +export interface IMomentumSettings extends IChartIndicatorSettings { +} + +export interface IMomentumPrivate extends IChartIndicatorPrivate { +} + +export interface IMomentumEvents extends IChartIndicatorEvents { +} + + +/** + * An implementation of a [[StockChart]] indicator. + * + * @see {@link https://www.amcharts.com/docs/v5/charts/stock/indicators/} for more info + * @since 5.4.8 + */ +export class Momentum extends ChartIndicator { + public static className: string = "Momentum"; + public static classNames: Array = ChartIndicator.classNames.concat([Momentum.className]); + + declare public _settings: IMomentumSettings; + declare public _privateSettings: IMomentumPrivate; + declare public _events: IMomentumEvents; + + /** + * Indicator series. + */ + declare public series: LineSeries; + + public _editableSettings: IIndicatorEditableSetting[] = [ + { + key: "period", + name: this.root.language.translateAny("Period"), + type: "number" + }, { + key: "field", + name: this.root.language.translateAny("Field"), + type: "dropdown", + options: ["open", "close", "low", "high", "hl/2", "hlc/3", "hlcc/4", "ohlc/4"] + }, { + key: "seriesColor", + name: this.root.language.translateAny("Color"), + type: "color" + } + ]; + + protected _themeTag: string = "momentum"; + + public _createSeries(): LineSeries { + return this.panel.series.push(LineSeries.new(this._root, { + themeTags: ["indicator"], + xAxis: this.xAxis, + yAxis: this.yAxis, + valueXField: "valueX", + valueYField: "valueY", + stroke: this.get("seriesColor"), + fill: undefined + })) + } + + /** + * @ignore + */ + public prepareData() { + if (this.series) { + const dataItems = this.get("stockSeries").dataItems; + const period = this.get("period", 14); + const data: Array = []; + + let i = 0; + + $array.each(dataItems, (dataItem) => { + if (i > period) { + let value = this._getValue(dataItem); + let prevValue = this._getValue(dataItems[i - period]); + + if (value != undefined && prevValue != undefined) { + data.push({ valueX: dataItem.get("valueX"), valueY: value - prevValue }); + } + } + else { + data.push({ valueX: dataItem.get("valueX") }); + } + i++; + }) + + + this.series.data.setAll(data); + } + } +} \ No newline at end of file diff --git a/src/.internal/charts/stock/indicators/RelativeStrengthIndex.ts b/src/.internal/charts/stock/indicators/RelativeStrengthIndex.ts index c0d059c9..727f3824 100644 --- a/src/.internal/charts/stock/indicators/RelativeStrengthIndex.ts +++ b/src/.internal/charts/stock/indicators/RelativeStrengthIndex.ts @@ -246,40 +246,43 @@ export class RelativeStrengthIndex extends ChartIndicator { if (i == period + 1) { for (let j = 1; j <= period; j++) { - let value = dataItems[j].get("valueY", 0) - let prevValue = dataItems[j - 1].get("valueY", 0); - let change = value - prevValue; - - if (change > 0) { - averageGain += change / period; - } - else { - averageLoss += Math.abs(change) / period; + let value = this._getValue(dataItems[j]); + let prevValue = this._getValue(dataItems[j - 1]); + if (value != undefined && prevValue != undefined) { + let change = value - prevValue; + + if (change > 0) { + averageGain += change / period; + } + else { + averageLoss += Math.abs(change) / period; + } } } rsi = 100 - (100 / (1 + averageGain / averageLoss)); } else if (i > period) { - let value = dataItem.get("valueY", 0); - let prevValue = dataItems[i - 2].get("valueY", 0); - - let change = value - prevValue; + let value = this._getValue(dataItem); + let prevValue = this._getValue(dataItems[i - 2]); + if (value != null && prevValue != null) { + let change = value - prevValue; - let gain = 0; - let loss = 0; + let gain = 0; + let loss = 0; - if (change > 0) { - gain = change; - } - else { - loss = -change; - } + if (change > 0) { + gain = change; + } + else { + loss = -change; + } - averageGain = (prevAverageGain * (period - 1) + gain) / period; - averageLoss = (prevAverageLoss * (period - 1) + loss) / period; + averageGain = (prevAverageGain * (period - 1) + gain) / period; + averageLoss = (prevAverageLoss * (period - 1) + loss) / period; - rsi = 100 - (100 / (1 + averageGain / averageLoss)); + rsi = 100 - (100 / (1 + averageGain / averageLoss)); + } } data.push({ valueX: dataItem.get("valueX"), valueY: rsi }); diff --git a/src/.internal/charts/stock/toolbar/IndicatorControl.ts b/src/.internal/charts/stock/toolbar/IndicatorControl.ts index 38b008b5..0f54c6e2 100644 --- a/src/.internal/charts/stock/toolbar/IndicatorControl.ts +++ b/src/.internal/charts/stock/toolbar/IndicatorControl.ts @@ -19,6 +19,7 @@ import { StandardDeviation } from "../indicators/StandardDeviation"; import { TypicalPrice } from "../indicators/TypicalPrice"; import { MedianPrice } from "../indicators/MedianPrice"; import { OnBalanceVolume } from "../indicators/OnBalanceVolume"; +import { Momentum } from "../indicators/Momentum"; import { RelativeStrengthIndex } from "../indicators/RelativeStrengthIndex"; import { StochasticOscillator } from "../indicators/StochasticOscillator"; import { WilliamsR } from "../indicators/WilliamsR"; @@ -38,7 +39,7 @@ import { StockIcons } from "./StockIcons"; import * as $array from "../../../core/util/Array"; import * as $type from "../../../core/util/Type"; -export type Indicators = "Accumulation Distribution" | "Accumulative Swing Index" | "Aroon" | "Awesome Oscillator" | "Bollinger Bands" | "Chaikin Money Flow" | "Chaikin Oscillator" | "Commodity Channel Index" | "Disparity Index" | "MACD" | "Moving Average" | "Moving Average Deviation" | "Moving Average Envelope" | "On Balance Volume" | "Relative Strength Index" | "Standard Deviation" | "Stochastic Oscillator" | "Trix" | "Typical Price" | "Volume" | "VWAP" | "Williams R" | "Median Price" | "ZigZag"; +export type Indicators = "Accumulation Distribution" | "Accumulative Swing Index" | "Aroon" | "Awesome Oscillator" | "Bollinger Bands" | "Chaikin Money Flow" | "Chaikin Oscillator" | "Commodity Channel Index" | "Disparity Index" | "MACD" | "Momentum" | "Moving Average" | "Moving Average Deviation" | "Moving Average Envelope" | "On Balance Volume" | "Relative Strength Index" | "Standard Deviation" | "Stochastic Oscillator" | "Trix" | "Typical Price" | "Volume" | "VWAP" | "Williams R" | "Median Price" | "ZigZag"; export interface IIndicator { id: string; @@ -262,6 +263,12 @@ export class IndicatorControl extends StockControl { legend: legend }); break; + case "Momentum": + indicator = Momentum.new(this.root, { + stockChart: stockChart, + stockSeries: stockSeries + }); + break; case "Median Price": indicator = MedianPrice.new(this.root, { stockChart: stockChart, diff --git a/src/.internal/charts/xy/XYChart.ts b/src/.internal/charts/xy/XYChart.ts index bb72fd9a..d6d050f7 100644 --- a/src/.internal/charts/xy/XYChart.ts +++ b/src/.internal/charts/xy/XYChart.ts @@ -1291,9 +1291,8 @@ export class XYChart extends SerialChart { const downPositionX = cursor.getPrivate("downPositionX", 0); const downPositionY = cursor.getPrivate("downPositionY", 0); - const positionX = cursor.getPrivate("positionX", 0.5); - const positionY = cursor.getPrivate("positionY", 0.5); - + const positionX = Math.min(1, Math.max(0, cursor.getPrivate("positionX", 0.5))); + const positionY = Math.min(1, Math.max(0, cursor.getPrivate("positionY", 0.5))); this.xAxes.each((axis) => { if (behavior === "zoomX" || behavior === "zoomXY") { diff --git a/src/.internal/charts/xy/axes/DateAxis.ts b/src/.internal/charts/xy/axes/DateAxis.ts index 692a7fd0..da7f5c84 100644 --- a/src/.internal/charts/xy/axes/DateAxis.ts +++ b/src/.internal/charts/xy/axes/DateAxis.ts @@ -247,6 +247,7 @@ export class DateAxis extends ValueAxis { let mainDataSetId: string = baseInterval.timeUnit + baseInterval.count; $array.each(this.series, (series) => { series.setDataSet(mainDataSetId); + series.resetGrouping(); }) this._setBaseInterval(baseInterval); diff --git a/src/.internal/charts/xy/series/XYSeries.ts b/src/.internal/charts/xy/series/XYSeries.ts index 8167beef..0e808c3a 100644 --- a/src/.internal/charts/xy/series/XYSeries.ts +++ b/src/.internal/charts/xy/series/XYSeries.ts @@ -915,8 +915,8 @@ export abstract class XYSeries extends Series { protected _tooltipFieldX?: string; protected _tooltipFieldY?: string; - public _posXDp?:IDisposer; - public _posYDp?:IDisposer; + public _posXDp?: IDisposer; + public _posYDp?: IDisposer; protected _afterNew() { this.fields.push("categoryX", "categoryY", "openCategoryX", "openCategoryY"); @@ -1221,7 +1221,7 @@ export abstract class XYSeries extends Series { this._valuesDirty = true; } - if(this.isDirty("xAxis") || this.isDirty("yAxis")){ + if (this.isDirty("xAxis") || this.isDirty("yAxis")) { this._valuesDirty = true; } @@ -1359,7 +1359,6 @@ export abstract class XYSeries extends Series { } if (this._valuesDirty || this.isPrivateDirty("startIndex") || this.isPrivateDirty("endIndex") || this.isDirty("vcx") || this.isDirty("vcy") || this._stackDirty) { - let startIndex = this.startIndex(); let endIndex = this.endIndex(); let minBulletDistance = this.get("minBulletDistance", 0); @@ -1786,6 +1785,21 @@ export abstract class XYSeries extends Series { } } + /** + * @ignore + */ + public resetGrouping() { + $object.each(this._dataSets, (_key, dataSet) => { + if (dataSet != this._mainDataItems) { + $array.each(dataSet, (dataItem) => { + this.disposeDataItem(dataItem); + }) + } + }) + this._dataSets = {}; + this._dataItems = this.mainDataItems; + } + protected _handleDataSetChange() { if (this.bullets.length > 0) { $array.each(this._dataItems, (dataItem) => { diff --git a/src/.internal/core/Classes.ts b/src/.internal/core/Classes.ts index 604a1a4d..50807843 100644 --- a/src/.internal/core/Classes.ts +++ b/src/.internal/core/Classes.ts @@ -117,6 +117,7 @@ import type { MapPolygonSeries } from "./../charts/map/MapPolygonSeries.js"; import type { MapSeries } from "./../charts/map/MapSeries.js"; import type { MedianPrice } from "./../charts/stock/indicators/MedianPrice.js"; import type { Modal } from "./util/Modal.js"; +import type { Momentum } from "./../charts/stock/indicators/Momentum.js"; import type { MovingAverage } from "./../charts/stock/indicators/MovingAverage.js"; import type { MovingAverageDeviation } from "./../charts/stock/indicators/MovingAverageDeviation.js"; import type { MovingAverageEnvelope } from "./../charts/stock/indicators/MovingAverageEnvelope.js"; @@ -326,6 +327,7 @@ export interface IClasses { "MapSeries": MapSeries; "MedianPrice": MedianPrice; "Modal": Modal; + "Momentum": Momentum; "MovingAverage": MovingAverage; "MovingAverageDeviation": MovingAverageDeviation; "MovingAverageEnvelope": MovingAverageEnvelope; diff --git a/src/.internal/core/Registry.ts b/src/.internal/core/Registry.ts index 9d3e57a0..f958c3f1 100644 --- a/src/.internal/core/Registry.ts +++ b/src/.internal/core/Registry.ts @@ -6,7 +6,7 @@ export class Registry { /** * Currently running version of amCharts. */ - readonly version: string = "5.4.7"; + readonly version: string = "5.4.8"; /** * List of applied licenses. diff --git a/src/.internal/core/Root.ts b/src/.internal/core/Root.ts index 0351c436..631c561a 100644 --- a/src/.internal/core/Root.ts +++ b/src/.internal/core/Root.ts @@ -666,6 +666,24 @@ export class Root implements IDisposer { } })); + this._disposers.push($utils.addEventListener(focusElementContainer, "click", () => { + // Some screen readers convert ENTER (and some SPACE) press whil on + // focused element to a "click" event, preventing actual "keydown" + // event from firing. We're using this "click" event to still + // generate internal click events. + const focusedSprite = this._focusedSprite; + if (focusedSprite) { + const downEvent = renderer.getEvent(new MouseEvent("click")); + focusedSprite.events.dispatch("click", { + type: "click", + originalEvent: downEvent.event, + point: downEvent.point, + simulated: true, + target: focusedSprite + }); + } + })); + this._disposers.push($utils.addEventListener(focusElementContainer, "keydown", (ev: KeyboardEvent) => { const focusedSprite = this._focusedSprite; if (focusedSprite) { diff --git a/src/.internal/core/util/DateFormatter.ts b/src/.internal/core/util/DateFormatter.ts index 34038851..95c68dec 100644 --- a/src/.internal/core/util/DateFormatter.ts +++ b/src/.internal/core/util/DateFormatter.ts @@ -116,7 +116,6 @@ export class DateFormatter extends Entity { date = timezone.convertLocal(date); } - // Check if it's a valid date if (!$type.isNumber(date.getTime())) { // TODO translation @@ -418,19 +417,19 @@ export class DateFormatter extends Entity { break; case "z": - value = $utils.getTimeZone(date, false, false, this._root.utc); + value = $utils.getTimeZone(date, false, false, this._root.utc, this._root.timezone ? this._root.timezone.name : undefined); break; case "zz": - value = $utils.getTimeZone(date, true, false, this._root.utc); + value = $utils.getTimeZone(date, true, false, this._root.utc, this._root.timezone ? this._root.timezone.name : undefined); break; case "zzz": - value = $utils.getTimeZone(date, false, true, this._root.utc); + value = $utils.getTimeZone(date, false, true, this._root.utc, this._root.timezone ? this._root.timezone.name : undefined); break; case "zzzz": - value = $utils.getTimeZone(date, true, true, this._root.utc); + value = $utils.getTimeZone(date, true, true, this._root.utc, this._root.timezone ? this._root.timezone.name : undefined); break; case "Z": diff --git a/src/.internal/core/util/Entity.ts b/src/.internal/core/util/Entity.ts index 4f355e90..ee71b151 100644 --- a/src/.internal/core/util/Entity.ts +++ b/src/.internal/core/util/Entity.ts @@ -388,6 +388,7 @@ export abstract class Settings implements IDisposer, IAnimation, IStartAnimation protected _animatingSettings: Animated = {}; protected _animatingPrivateSettings: Animated = {}; + private _playingAnimations: number = 0; private _disposed: boolean = false; // TODO move this into Entity @@ -418,8 +419,6 @@ export abstract class Settings implements IDisposer, IAnimation, IStartAnimation public _runAnimation(currentTime: number): boolean { if (!this.isDisposed()) { - let playing = false; - $object.each(this._animatingSettings, (key, animation) => { if (animation._stopped) { this._stopAnimation(key); @@ -433,13 +432,11 @@ export abstract class Settings implements IDisposer, IAnimation, IStartAnimation if (animation._checkEnded()) { this.set(key, animation._value(1)); } else { - playing = true; animation._reset(currentTime); this._set(key, animation._value(1)); } } else { - playing = true; this._set(key, animation._value(diff)); } } @@ -459,19 +456,17 @@ export abstract class Settings implements IDisposer, IAnimation, IStartAnimation this.setPrivate(key, animation._value(1)); } else { - playing = true; animation._reset(currentTime); this._setPrivate(key, animation._value(1)); } } else { - playing = true; this._setPrivate(key, animation._value(diff)); } } }); - return playing; + return this._playingAnimations !== 0; } else { return false; @@ -644,6 +639,7 @@ export abstract class Settings implements IDisposer, IAnimation, IStartAnimation const animation = this._animatingSettings[key]; if (animation) { + --this._playingAnimations; delete this._animatingSettings[key]; animation.stop(); } @@ -758,6 +754,7 @@ export abstract class Settings implements IDisposer, IAnimation, IStartAnimation const animation = this._animatingPrivateSettings[key]; if (animation) { + --this._playingAnimations; animation.stop(); delete this._animatingPrivateSettings[key]; } @@ -829,6 +826,8 @@ export abstract class Settings implements IDisposer, IAnimation, IStartAnimation const animation = this._animatingSettings[key] = new Animation(this, from, to, duration, easing, loops, this._animationTime()); + ++this._playingAnimations; + this._startAnimation(); return animation; @@ -864,6 +863,8 @@ export abstract class Settings implements IDisposer, IAnimation, IStartAnimation const animation = this._animatingPrivateSettings[key] = new Animation(this, from, to, duration, easing, loops, this._animationTime()); + ++this._playingAnimations; + this._startAnimation(); return animation; diff --git a/src/.internal/core/util/Utils.ts b/src/.internal/core/util/Utils.ts index df4f52ba..bcb4c7e1 100644 --- a/src/.internal/core/util/Utils.ts +++ b/src/.internal/core/util/Utils.ts @@ -946,10 +946,15 @@ export function get12Hours(hours: number, base?: number): number { * @param utc Assume UTC dates * @return Time zone name */ -export function getTimeZone(date: Date, long: boolean = false, savings: boolean = false, utc: boolean = false): string { +export function getTimeZone(date: Date, long: boolean = false, savings: boolean = false, utc: boolean = false, timezone?: string): string { if (utc) { return long ? "Coordinated Universal Time" : "UTC"; } + else if (timezone) { + const d1 = date.toLocaleString("en-US", { timeZone: timezone }); + const d2 = date.toLocaleString("en-US", { timeZone: timezone, timeZoneName: long ? "long" : "short" }); + return trim(d2.substr(d1.length)); + } let wotz = date.toLocaleString("UTC"); let wtz = date.toLocaleString("UTC", { timeZoneName: long ? "long" : "short" }).substr(wotz.length); //wtz = wtz.replace(/[+-]+[0-9]+$/, ""); @@ -961,8 +966,8 @@ export function getTimeZone(date: Date, long: boolean = false, savings: boolean export function getTimezoneOffset(timezone: string): number { const date = new Date(Date.UTC(2012, 0, 1, 0, 0, 0, 0)); - const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' })); - const tzDate = new Date(date.toLocaleString('en-US', { timeZone: timezone })); + const utcDate = new Date(date.toLocaleString("en-US", { timeZone: "UTC" })); + const tzDate = new Date(date.toLocaleString("en-US", { timeZone: timezone })); return (tzDate.getTime() - utcDate.getTime()) / 6e4 * -1; }