簡體   English   中英

僅計算一個特定時區的時區偏移量

[英]Calculate Timezone offset only for one particular timezone

我正在嘗試為一個在德國在線教學的人構建一個應用程序。 我想將該人的日程表存儲在一系列約會開始時間中。 例如:

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

問題是在一些國家,比如德國,有標准時間和夏季時間。 因此,雖然在夏季,德國的開始時間是 9:00、10:00 和 11:00,但在冬季,他們會提前一小時。 同時在新加坡等國家/地區,它們將保持不變: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
    ...
  ]
}

我需要這個日程表,以便我可以將它與我從谷歌日歷中獲取的每天約會列表進行比較,並排除已經被占用的約會列表。

我認為您要做的是根據另一個時區的時間戳並給定 IANA 代表位置來生成 UTC 時間戳。

一種方法是使用時間戳作為 UTC 生成該位置的日期,並查看小時和分鍾的差異。 那應該是偏移量。 然后將偏移量應用於原始時間戳。 然后,當應用時區偏移時,這應該會生成具有目標位置所需日期和時間的時間戳。

以下 function 使用 Intl.DateTimeFormat 和 IANA 代表位置來實現此算法。 它只是經過輕微測試,所以請進一步測試。 它為調整執行一個循環,以檢查應用偏移量是否不會將日期移動到 DST 邊界上,因此需要重新調整偏移量。

默認情況下,它返回等效時間的 ISO 8601 UTC 時間戳,如果可選的returnOffset參數設置為true ,它會返回偏移量。

我已經提供了 GMT 東部和西部的標准和夏令時時間戳的示例(但不多)。 請徹底測試並根據需要進行修改。

 /* @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>

我試圖用帖子中的代碼實現一些目標,但我仍然得到錯誤的答案。 不知道我的邏輯哪里錯了。

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