简体   繁体   English

仅计算一个特定时区的时区偏移量

[英]Calculate Timezone offset only for one particular timezone

I am trying to build an app for a person who is teaching online and is based in Germany.我正在尝试为一个在德国在线教学的人构建一个应用程序。 I want to store the person's schedule in an array of appointment starting times.我想将该人的日程表存储在一系列约会开始时间中。 For example:例如:

let schedule = [
  new Date(`${currentDate}T07:00:00Z`),
  new Date(`${currentDate}T08:00:00Z`),
  new Date(`${currentDate}T09:00:00Z`)
  ...
]

The problem is that in some countries, like Germany, there are Standart and Summer times.问题是在一些国家,比如德国,有标准时间和夏季时间。 So, while in summer the starting times in Germany would be 9:00, 10:00 and 11:00, in winter they would shift one hour earlier.因此,虽然在夏季,德国的开始时间是 9:00、10:00 和 11:00,但在冬季,他们会提前一小时。 At the same time in countries, like Singapore, they are gonna stay the same: 15:00, 16:00, 17:00... What do I have to do make the starting times in Germany stable throughout the year and change elsewhere respectively:同时在新加坡等国家/地区,它们将保持不变:15:00、16:00、17:00... 我必须做些什么才能使德国的开始时间全年稳定并在其他地方改变分别:

if (summerTimeInGermany) {
  schedule = [
    new Date(`${currentDate}T07:00:00Z`), // 9:00 in Germany
    new Date(`${currentDate}T08:00:00Z`), // 10:00
    new Date(`${currentDate}T09:00:00Z`) // 11:00
    ...
  ]
} else {
  schedule = [
    new Date(`${currentDate}T08:00:00Z`), // 9:00
    new Date(`${currentDate}T09:00:00Z`), // 10:00
    new Date(`${currentDate}T10:00:00Z`) // 11:00
    ...
  ]
}

I need this schedule list so that I could compare it with the list of appointments per day, which I fetch from Google Calender, and exclude the ones that are already taken.我需要这个日程表,以便我可以将它与我从谷歌日历中获取的每天约会列表进行比较,并排除已经被占用的约会列表。

What I think you're trying to do is to generate a UTC timestamp based on a timestamp in another timezone and given the IANA representative location.我认为您要做的是根据另一个时区的时间戳并给定 IANA 代表位置来生成 UTC 时间戳。

One way of doing that is to generate a Date for the location using the timestamp as UTC and see what the difference in hours and minutes are.一种方法是使用时间戳作为 UTC 生成该位置的日期,并查看小时和分钟的差异。 That should be the offset.那应该是偏移量。 Then apply the offset to the original timestamp.然后将偏移量应用于原始时间戳。 That should then generate a timestamp with the required date and time for the target location when the timezone offset is applied.然后,当应用时区偏移时,这应该会生成具有目标位置所需日期和时间的时间戳。

The following function implements this algorithm using Intl.DateTimeFormat and IANA representative location.以下 function 使用 Intl.DateTimeFormat 和 IANA 代表位置来实现此算法。 It's only been lightly tested so please test further.它只是经过轻微测试,所以请进一步测试。 It does a loop for the adjustments to check that applying the offset doesn't move the date over a DST boundary and hence needs to readjust the offset.它为调整执行一个循环,以检查应用偏移量是否不会将日期移动到 DST 边界上,因此需要重新调整偏移量。

By default it returns an ISO 8601 UTC timestamp for the equivalent time, if the optional returnOffset parameter is set to true , it returns the offset instead.默认情况下,它返回等效时间的 ISO 8601 UTC 时间戳,如果可选的returnOffset参数设置为true ,它会返回偏移量。

I've provided examples of standard and daylight saving timestamps both east and west of GMT (but not many).我已经提供了 GMT 东部和西部的标准和夏令时时间戳的示例(但不多)。 Please test thoroughly and modify as required.请彻底测试并根据需要进行修改。

 /* @param {string} isoString - ISO 8601 timestamp without timezone ** eg YYYY-MM-DDTHH:mm:ss or YYYY-MM-DD HH:mm:ss ** @param {string} loc - IANA representateive location ** eg Europe/Berlin ** @param {boolean} returnOffset - if true, return the offset instead of timestamp ** @returns {string} if returnOffset is true, offset is ±HH:mm[:ss] (seconds only if not zero) ** if returnOffset is false, equivalent ISO 8601 UTC timestamp */ let getUTCTime = (function() { let n = 'numeric'; let formatterOpts = {year:n, month:n, day:n, hour:n, minute:n, second:n, hour12: false}; // Parse YYYY-MM-DDTHH:mm:ss as UTC (T can be space) function parse (isoString) { let [Y,M,D,H,m,s] = isoString.split(/[\DT\s]/); return new Date(Date.UTC(Y,M-1,D,H,m,s)); } // Get date parts, use supplied formatter function toParts(date, formatter) { return formatter.formatToParts(date).reduce((acc, part) => { acc[part.type] = part.value; return acc; }, Object.create(null)); } return function (isoString, loc, returnOffset = false) { // Update formatter options with loc so get target loc values formatterOpts.timeZone = loc; // Formatter let formatter = new Intl.DateTimeFormat('en', formatterOpts); // Parse input string as UTC let oDate = parse(isoString); // Date to adjust to wanted values let utcDate = new Date(oDate); // maxLoops limits do..while in dilemma zone, ensures sensible value let maxLoops = 3, p, diff; // Adjust utcDate so it generates required local date values // Adjustment may shift over DST boundary so may need 2nd adjustment // Limit number of loops (shouldn't be required but just in case...) do { // Get date parts in target timezone p = toParts(utcDate, formatter); // Get difference between local and adjusted values diff = new Date(Date.UTC(p.year, p.month-1, p.day, p.hour, p.minute, p.second)) - oDate; // If necessary, adjust utcDate so it generates required values when shifted if (diff) { utcDate.setTime(utcDate.getTime() - diff); } // Loop until generated values match original or maxLoops } while (diff && maxLoops--) // If maxLoops is -1, hit DST dilemma zone: time doesn't exist on that date // Eg going into daylight saving at 02:00, then 02:30 doesn't exist // and loop will flip in/out of DST until stopped by maxLoops // So generate valid date and offset in DST period let dDiff = null; if (maxLoops < 0) { p = toParts(utcDate, formatter); dDiff = Date.UTC(p.year, p.month - 1, p.day, p.hour, p.minute, p.second) - utcDate; let msg = isoString + ' does not exist at ' + loc + ' due to ' + 'daylight saving change-over, shifting into DST'; // console.log(msg); // throw new RangeError(msg); } // Convert diff between local and adjusted to get ±HH:mm offset // Use dilemma diff (dDiff) if has been set let oDiff = dDiff || oDate - utcDate; let sign = oDiff > 0? '+': '-'; oDiff = Math.abs(oDiff); // console.log(sign + new Date(oDiff).toISOString().substring(11,19).replace(/:00$/,'')); let offH = oDiff / 3.6e6 | 0; let offM = (oDiff % 3.6e6) / 6e4 | 0; let offS = (oDiff % 6e4) / 1e3 | 0; let z = n=>(n<10?'0':'')+n; // Return offset (with offset seconds if not zero) or ISO 8601 UTC string return returnOffset? `${sign}${z(offH)}:${z(offM)}${offS? ':' + z(offS): ''}`: utcDate.toISOString(); } })(); // Given a local timestmap in format YYYY-MM-DDTHH:mm:ss and // loc as IANA representative location // Return equivalent ISO 8061 UTC timestmap function getUTCString(timestamp, loc) { return getUTCTime(timestamp, loc); } // Given a local timestmap in format YYYY-MM-DDTHH:mm:ss and // loc as IANA representative location // Return offset at loc as ±HH:mm[:ss] // - seconds only included if not zero (typically pre-1900) function getUTCOffset(timestamp, loc) { return getUTCTime(timestamp, loc, true); } // Examples window.onload = function() { let t = document.getElementById('t0'); let params = ['Local time', 'UTC time', false]; let showData = (localTime, loc, offset, timeUTC) => { let row = t.insertRow(); [localTime, loc, timeUTC, null, null].forEach((s, i) => { cell = row.insertCell(); cell.textContent = i == 0? localTime.replace('T',' '): i == 1? loc: i == 2? offset: i == 3? timeUTC: new Date(timeUTC).toLocaleString('en-CA', {timeZone: loc, hour12: false}).replace(',',''); }); return new Date(timeUTC).toLocaleString('en-CA', {timeZone: loc, hour12: false}).replace(',',''); }; // Local time required Location [['2020-04-22T09:00:00','Europe/Berlin'], // DST offset +2 ['2020-01-22T09:00:00','Europe/Berlin'], // Std offset +1 ['2020-04-22T09:00:00','America/Denver'], // DST offset -6 ['2020-01-22T09:00:00','America/Denver'], // Std offset -7 ['2020-04-22T09:00:00','US/Aleutian'], // DST offset -9 ['2020-01-22T09:00:00','US/Aleutian'], // Std offset -10 ['2020-01-22T09:00:00','Pacific/Honolulu'],// Std offset -11 ['2020-01-22T19:00:00','Pacific/Honolulu'],// Std offset -11 ['2020-04-22T09:00:00','Asia/Singapore'], // Std offset +8 ['2020-04-22T09:00:00','Pacific/Apia'], // Std offset +13 ['2020-01-22T09:00:00','Pacific/Apia'], // DST offset +14 ['2020-01-22T09:00:00','Asia/Yangon'], // Std offset +6:30 ['2020-04-22T09:00:00','Pacific/Chatham'], // Std offset +12:45 ['2020-01-22T09:00:00','Pacific/Chatham'], // DST offset +13:45 // Historic offsets pre 1900 ['1857-01-01T00:00:00','Europe/Berlin'], // Std offset +00:53:28 ['1857-01-01T00:00:00','Australia/Sydney'],// Std offset +10:04:52 ['1857-01-01T00:00:00','America/New_York'],// Std offset -04:56:02 ['1857-01-01T00:00:00','America/Sao_Paulo'],//Std offset -03:06:28 // DST boundary check out of DST (2 to 3 am counted as "out") ['2020-04-05T01:45:00','Australia/Sydney'],// DST offset +11:00 ['2020-04-05T01:59:59','Australia/Sydney'],// DST offset +11:00 ['2020-04-05T02:00:00','Australia/Sydney'],// Std offset +10:00 ['2020-04-05T02:30:00','Australia/Sydney'],// Std offset +10:00 ['2020-04-05T03:00:00','Australia/Sydney'],// Std offset +10:00 ['2020-04-05T03:15:00','Australia/Sydney'],// Std offset +10:00 // DST boundary check into DST (2 to 3 am counted as "in") ['2020-10-04T01:45:00','Australia/Sydney'],// Std offset +10:00 ['2020-10-04T02:00:00','Australia/Sydney'],// DST offset +11:00 ['2020-10-04T02:30:00','Australia/Sydney'],// DST offset +11:00 ['2020-10-04T02:59:59','Australia/Sydney'],// DST offset +11:00 ['2020-10-04T03:00:00','Australia/Sydney'],// DST offset +11:00 ['2020-10-04T03:15:00','Australia/Sydney'] // DST offset +11:00 ].forEach(([localTime,loc]) => { // Show results let timeUTC = getUTCString(localTime, loc); let offset = getUTCOffset(localTime, loc); showData(localTime, loc, offset, timeUTC); }); }; // Example use let timestamp = '2020-06-30 08:30:00'; let locBer = 'Europe/Berlin'; let locSng = 'Asia/Singapore'; // Get UTC timestamp and offset for Berlin let utc = getUTCString(timestamp, locBer); let off = getUTCOffset(timestamp, locBer); // Show times and offset - offset is just for display, not used to // generate Singapore timestamp console.log('Berlin: ' + timestamp + ' ' + off); // console.log('Singapore: ' + new Date(utc).toLocaleString( 'en-CA',{hour12:false, timeZone:locSng, timeZoneName:'short'} ).replace(',',''));
 table { border-collapse: collapse; } td { font-family: geneva, arial; font-size: 80%; padding: 5px; border: 1px solid #bbbbbb; } td:nth-child(2) { font-family: monospace; font-size: 100%; }
 <table id="t0"> <tr><th>Local time<th>Place<th>Offset<th>UTC<th>Check </table>

I tried to achieve something with the code from the post, but I am still getting a wrong answer.我试图用帖子中的代码实现一些目标,但我仍然得到错误的答案。 Don't know where my logic is wrong.不知道我的逻辑哪里错了。

Date.prototype.stdTimezoneOffset = function () {
            var jan = new Date(this.getFullYear(), 0, 1);
            var jul = new Date(this.getFullYear(), 6, 1);
            return Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset());
        }

        Date.prototype.isDstObserved = function () {
            return this.getTimezoneOffset() < this.stdTimezoneOffset();
        }

        let currentDate = '2020-12-22';
        const today = new Date();
        let schedule = [];

        if (!today.isDstObserved()) {
            schedule = [
                new Date(`${currentDate}T07:00:00Z`), // 9:00 in Germany
                new Date(`${currentDate}T08:00:00Z`), // 10:00
                new Date(`${currentDate}T09:00:00Z`) // 11:00
            ]
        } else {
            if (today.getTimezoneOffset() < new Date('2019-11-21').getTimezoneOffset()) {
                schedule = [
                    new Date(`${currentDate}T07:00:00Z`), // 9:00 in Germany
                    new Date(`${currentDate}T08:00:00Z`), // 10:00
                    new Date(`${currentDate}T09:00:00Z`) // 11:00
                ]
            } else {
                schedule = [
                    new Date(`${currentDate}T08:00:00Z`), // 9:00
                    new Date(`${currentDate}T09:00:00Z`), // 10:00
                    new Date(`${currentDate}T10:00:00Z`) // 11:00
                ]
            }
        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM