summaryrefslogtreecommitdiffstats
path: root/src/modules/dayjs/plugin/timezone/index.js
blob: a7bd673e33f05e40cc3e0f5304bd99eaad3c8685 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/**
 * Copy from node_modules/dayjs/plugin/timezone.js
 * Try to fix https://github.com/louislam/uptime-kuma/issues/2318
 * Source: https://github.com/iamkun/dayjs/tree/dev/src/plugin/utc
 * License: MIT
 */
import { MIN, MS } from "../../constant";
let typeToPos = {
    year: 0,
    month: 1,
    day: 2,
    hour: 3,
    minute: 4,
    second: 5
}; // Cache time-zone lookups from Intl.DateTimeFormat,
// as it is a *very* slow method.

let dtfCache = {};

let getDateTimeFormat = function getDateTimeFormat(timezone, options) {
    if (options === void 0) {
        options = {};
    }

    let timeZoneName = options.timeZoneName || "short";
    let key = timezone + "|" + timeZoneName;
    let dtf = dtfCache[key];

    if (!dtf) {
        dtf = new Intl.DateTimeFormat("en-US", {
            hour12: false,
            timeZone: timezone,
            year: "numeric",
            month: "2-digit",
            day: "2-digit",
            hour: "2-digit",
            minute: "2-digit",
            second: "2-digit",
            timeZoneName: timeZoneName
        });
        dtfCache[key] = dtf;
    }

    return dtf;
};

export default (function (o, c, d) {
    let defaultTimezone;

    let makeFormatParts = function makeFormatParts(timestamp, timezone, options) {
        if (options === void 0) {
            options = {};
        }

        let date = new Date(timestamp);
        let dtf = getDateTimeFormat(timezone, options);
        return dtf.formatToParts(date);
    };

    let tzOffset = function tzOffset(timestamp, timezone) {
        let formatResult = makeFormatParts(timestamp, timezone);
        let filled = [];

        for (let i = 0; i < formatResult.length; i += 1) {
            let _formatResult$i = formatResult[i];
            let type = _formatResult$i.type;
            let value = _formatResult$i.value;
            let pos = typeToPos[type];

            if (pos >= 0) {
                filled[pos] = parseInt(value, 10);
            }
        }

        let hour = filled[3]; // Workaround for the same behavior in different node version
        // https://github.com/nodejs/node/issues/33027

        /* istanbul ignore next */

        let fixedHour = hour === 24 ? 0 : hour;
        let utcString = filled[0] + "-" + filled[1] + "-" + filled[2] + " " + fixedHour + ":" + filled[4] + ":" + filled[5] + ":000";
        let utcTs = d.utc(utcString).valueOf();
        let asTS = +timestamp;
        let over = asTS % 1000;
        asTS -= over;
        return (utcTs - asTS) / (60 * 1000);
    }; // find the right offset a given local time. The o input is our guess, which determines which
    // offset we'll pick in ambiguous cases (e.g. there are two 3 AMs b/c Fallback DST)
    // https://github.com/moment/luxon/blob/master/src/datetime.js#L76

    let fixOffset = function fixOffset(localTS, o0, tz) {
    // Our UTC time is just a guess because our offset is just a guess
        let utcGuess = localTS - o0 * 60 * 1000; // Test whether the zone matches the offset for this ts

        let o2 = tzOffset(utcGuess, tz); // If so, offset didn't change and we're done

        if (o0 === o2) {
            return [ utcGuess, o0 ];
        } // If not, change the ts by the difference in the offset

        utcGuess -= (o2 - o0) * 60 * 1000; // If that gives us the local time we want, we're done

        let o3 = tzOffset(utcGuess, tz);

        if (o2 === o3) {
            return [ utcGuess, o2 ];
        } // If it's different, we're in a hole time.
        // The offset has changed, but the we don't adjust the time

        return [ localTS - Math.min(o2, o3) * 60 * 1000, Math.max(o2, o3) ];
    };

    let proto = c.prototype;

    proto.tz = function (timezone, keepLocalTime) {
        if (timezone === void 0) {
            timezone = defaultTimezone;
        }

        let oldOffset = this.utcOffset();
        let date = this.toDate();
        let target = date.toLocaleString("en-US", {
            timeZone: timezone
        }).replace("\u202f", " ");
        let diff = Math.round((date - new Date(target)) / 1000 / 60);
        let ins = d(target).$set(MS, this.$ms).utcOffset(-Math.round(date.getTimezoneOffset() / 15) * 15 - diff, true);

        if (keepLocalTime) {
            let newOffset = ins.utcOffset();
            ins = ins.add(oldOffset - newOffset, MIN);
        }

        ins.$x.$timezone = timezone;
        return ins;
    };

    proto.offsetName = function (type) {
    // type: short(default) / long
        let zone = this.$x.$timezone || d.tz.guess();
        let result = makeFormatParts(this.valueOf(), zone, {
            timeZoneName: type
        }).find(function (m) {
            return m.type.toLowerCase() === "timezonename";
        });
        return result && result.value;
    };

    let oldStartOf = proto.startOf;

    proto.startOf = function (units, startOf) {
        if (!this.$x || !this.$x.$timezone) {
            return oldStartOf.call(this, units, startOf);
        }

        let withoutTz = d(this.format("YYYY-MM-DD HH:mm:ss:SSS"));
        let startOfWithoutTz = oldStartOf.call(withoutTz, units, startOf);
        return startOfWithoutTz.tz(this.$x.$timezone, true);
    };

    d.tz = function (input, arg1, arg2) {
        let parseFormat = arg2 && arg1;
        let timezone = arg2 || arg1 || defaultTimezone;
        let previousOffset = tzOffset(+d(), timezone);

        if (typeof input !== "string") {
            // timestamp number || js Date || Day.js
            return d(input).tz(timezone);
        }

        let localTs = d.utc(input, parseFormat).valueOf();

        let _fixOffset = fixOffset(localTs, previousOffset, timezone);
        let targetTs = _fixOffset[0];
        let targetOffset = _fixOffset[1];

        let ins = d(targetTs).utcOffset(targetOffset);
        ins.$x.$timezone = timezone;
        return ins;
    };

    d.tz.guess = function () {
        return Intl.DateTimeFormat().resolvedOptions().timeZone;
    };

    d.tz.setDefault = function (timezone) {
        defaultTimezone = timezone;
    };
});