Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/src/resources/library/dayjs/plugins/timezone.js
12923 views
1
import { MIN, MS } from "./constant.js";
2
3
const typeToPos = {
4
year: 0,
5
month: 1,
6
day: 2,
7
hour: 3,
8
minute: 4,
9
second: 5,
10
};
11
12
// Cache time-zone lookups from Intl.DateTimeFormat,
13
// as it is a *very* slow method.
14
const dtfCache = {};
15
const getDateTimeFormat = (timezone, options = {}) => {
16
const timeZoneName = options.timeZoneName || "short";
17
const key = `${timezone}|${timeZoneName}`;
18
let dtf = dtfCache[key];
19
if (!dtf) {
20
dtf = new Intl.DateTimeFormat("en-US", {
21
hour12: false,
22
timeZone: timezone,
23
year: "numeric",
24
month: "2-digit",
25
day: "2-digit",
26
hour: "2-digit",
27
minute: "2-digit",
28
second: "2-digit",
29
timeZoneName,
30
});
31
dtfCache[key] = dtf;
32
}
33
return dtf;
34
};
35
36
export default (o, c, d) => {
37
let defaultTimezone;
38
39
const makeFormatParts = (timestamp, timezone, options = {}) => {
40
const date = new Date(timestamp);
41
const dtf = getDateTimeFormat(timezone, options);
42
return dtf.formatToParts(date);
43
};
44
45
const tzOffset = (timestamp, timezone) => {
46
const formatResult = makeFormatParts(timestamp, timezone);
47
const filled = [];
48
for (let i = 0; i < formatResult.length; i += 1) {
49
const { type, value } = formatResult[i];
50
const pos = typeToPos[type];
51
52
if (pos >= 0) {
53
filled[pos] = parseInt(value, 10);
54
}
55
}
56
const hour = filled[3];
57
// Workaround for the same behavior in different node version
58
// https://github.com/nodejs/node/issues/33027
59
/* istanbul ignore next */
60
const fixedHour = hour === 24 ? 0 : hour;
61
const utcString = `${filled[0]}-${filled[1]}-${filled[2]} ${fixedHour}:${filled[4]}:${filled[5]}:000`;
62
const utcTs = d.utc(utcString).valueOf();
63
let asTS = +timestamp;
64
const over = asTS % 1000;
65
asTS -= over;
66
return (utcTs - asTS) / (60 * 1000);
67
};
68
69
// find the right offset a given local time. The o input is our guess, which determines which
70
// offset we'll pick in ambiguous cases (e.g. there are two 3 AMs b/c Fallback DST)
71
// https://github.com/moment/luxon/blob/master/src/datetime.js#L76
72
const fixOffset = (localTS, o0, tz) => {
73
// Our UTC time is just a guess because our offset is just a guess
74
let utcGuess = localTS - o0 * 60 * 1000;
75
// Test whether the zone matches the offset for this ts
76
const o2 = tzOffset(utcGuess, tz);
77
// If so, offset didn't change and we're done
78
if (o0 === o2) {
79
return [utcGuess, o0];
80
}
81
// If not, change the ts by the difference in the offset
82
utcGuess -= (o2 - o0) * 60 * 1000;
83
// If that gives us the local time we want, we're done
84
const o3 = tzOffset(utcGuess, tz);
85
if (o2 === o3) {
86
return [utcGuess, o2];
87
}
88
// If it's different, we're in a hole time.
89
// The offset has changed, but the we don't adjust the time
90
return [localTS - Math.min(o2, o3) * 60 * 1000, Math.max(o2, o3)];
91
};
92
93
const proto = c.prototype;
94
95
proto.tz = function (timezone = defaultTimezone, keepLocalTime) {
96
const oldOffset = this.utcOffset();
97
const date = this.toDate();
98
const target = date.toLocaleString("en-US", { timeZone: timezone });
99
const diff = Math.round((date - new Date(target)) / 1000 / 60);
100
let ins = d(target)
101
.$set(MS, this.$ms)
102
.utcOffset(-Math.round(date.getTimezoneOffset() / 15) * 15 - diff, true);
103
if (keepLocalTime) {
104
const newOffset = ins.utcOffset();
105
ins = ins.add(oldOffset - newOffset, MIN);
106
}
107
ins.$x.$timezone = timezone;
108
return ins;
109
};
110
111
proto.offsetName = function (type) {
112
// type: short(default) / long
113
const zone = this.$x?.$timezone || d.tz.guess();
114
const result = makeFormatParts(this.valueOf(), zone, {
115
timeZoneName: type,
116
}).find((m) => m.type.toLowerCase() === "timezonename");
117
return result && result.value;
118
};
119
120
const oldStartOf = proto.startOf;
121
proto.startOf = function (units, startOf) {
122
if (!this.$x || !this.$x.$timezone) {
123
return oldStartOf.call(this, units, startOf);
124
}
125
126
const withoutTz = d(this.format("YYYY-MM-DD HH:mm:ss:SSS"));
127
const startOfWithoutTz = oldStartOf.call(withoutTz, units, startOf);
128
return startOfWithoutTz.tz(this.$x.$timezone, true);
129
};
130
131
d.tz = function (input, arg1, arg2) {
132
const parseFormat = arg2 && arg1;
133
const timezone = arg2 || arg1 || defaultTimezone;
134
const previousOffset = tzOffset(+d(), timezone);
135
if (typeof input !== "string") {
136
// timestamp number || js Date || Day.js
137
return d(input).tz(timezone);
138
}
139
const localTs = d.utc(input, parseFormat).valueOf();
140
const [targetTs, targetOffset] = fixOffset(
141
localTs,
142
previousOffset,
143
timezone
144
);
145
const ins = d(targetTs).utcOffset(targetOffset);
146
ins.$x.$timezone = timezone;
147
return ins;
148
};
149
150
d.tz.guess = function () {
151
return Intl.DateTimeFormat().resolvedOptions().timeZone;
152
};
153
154
d.tz.setDefault = function (timezone) {
155
defaultTimezone = timezone;
156
};
157
};
158
159