如何以通用时间格式存储小时和分钟?

How can I store Hour and Minute in a universal time format?

我在一个网站上工作,教师根据当地时区输入他们的空闲时间 例如,来自加利福尼亚州的大卫在太平洋标准时间周一和周二下午 4 点有空。 我想以本地格式向国际上的其他所有人展示这种可用性 来自纽约的 John 可以看到 David 在美国东部标准时间晚上 7 点有空 是否有标准方法可以在不将本地时区存储在数据库中的情况下执行此操作? 我在想只是选择一个随机日期(甚至现在)并将 hour/minute 粘贴到它上面,将其保存在 UTC 中并显示它只是忽略日期部分。这听起来合理还是有更好的方法?

我会存储:

  • 一天的初始时间:int
  • 一天的结束时间:整数
  • 原始时区:字符串

然后,向用户显示这是一个 UI 问题。 您可以动态计算原始时区中的两个日期(基于存储的时间),并将其动态转换为任何目标时区。

另一种方法是检查原始时区和目标时区之间的时差(不计算任何日期)并将其添加到 initial/end 次..但我想选择第一个选项更容易,因为 the date classes have that kind of utils.

跟踪开始和结束时间可能会导致奇怪的时区错误。

例如,如果有人在 EST 中选择 Monday 6pm-9pm,那么在 UTC 中实际上是 Monday 11pm - Tuesday 2am。这意味着存储在 UTC 中的时间范围是 Start: 11pmEnd: 2am,这需要大量代码来解决这些不同的场景。

更好的主意可能是跟踪开始时间和到结束时间(经过时间)的小时数。

存储本地时间时,还应存储相关的时区数据。目前最便携的标识符是 IANA 代表位置,如 'America/New_York'。这样可以适应标准和夏令时偏移量的更改,以便在给定特定日期的情况下,您可以获得一个人的时间的详细信息,并将其显示为另一个人所在位置的日期和时间,并根据他们在该日期的偏移量进行调整。

下面是一个算法,它使用了here, but I would strongly suggest using a library like Luxon中的粗略函数,我只是想保留这个简单的JS。

以下获取一个用户的时间和位置,然后将其显示为另一个用户位置的等效时间。希望这符合您想要做的事情。

// Function from 
/* @param {string} isoString - ISO 8601 timestamp without timezone
**                             e.g. YYYY-MM-DDTHH:mm:ss or YYYY-MM-DD HH:mm:ss
** @param {string} loc - IANA representateive location
**                       e.g. 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};
  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));
  }
  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) {
 
    formatterOpts.timeZone = loc;
    let formatter = new Intl.DateTimeFormat('en', formatterOpts);
    let oDate = parse(isoString);
    let utcDate = new Date(oDate);
    let maxLoops = 3,
        p, diff;
    do {
      p = toParts(utcDate, formatter);
      diff = new Date(Date.UTC(p.year, p.month-1, p.day, p.hour, p.minute, p.second)) - oDate;
      if (diff) {
        utcDate.setTime(utcDate.getTime() - diff);
      }
    } while (diff && maxLoops--)
    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';
    }
    let oDiff = dDiff || oDate - utcDate;
    let sign = oDiff > 0? '+' : '-';
    oDiff = Math.abs(oDiff);
    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 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);
}

/* @param {string} person - name of person
** @param {string} date - date to get times in YYYY-MM-DD format
** @param {string} loc - IANA rep. loc. e.g. America/New_York
** @returns {string} timestamp for loc
*/
function showTimes(person, date, loc) {
  // Get loc and time for person
  let sourceLoc  = data[person].loc; 
  let sourceTime = data[person].time;
  // Get UTC date for time
  let sourceDate = date + 'T' + sourceTime + ':00';
  let sourceOffset =  getUTCOffset(sourceDate, sourceLoc);
  let utcDate = new Date(sourceDate + sourceOffset);
  // Return local date for loc
  return utcDate.toLocaleString('en-CA',{timeZone: loc, timeZoneName:'long', hour12: false});
}

let data = {
  john: {
    loc: 'America/Los_Angeles', // IANA representative location
    time: '16:15'               // Must be in HH:mm format
  },
  sally: {
    loc: 'America/New_York',
    time: '08:30'
  }
}

let date = '2020-02-03';
let user1 = 'john';
let user2 = 'sally';

// Standard time
// Show John's time in Sally's location on Monday, 3 February 2020
console.log(
`${date} ${data[user1].time} for ${user1} in ${data[user1].loc } is\n\
${showTimes(user1,date, data[user2].loc)} for ${user2}`
 );
 
 // Daylight saving time
 // Show Sally's time in John's location on Friday, 26 June 2020
 date = '2020-06-26';
console.log(
`${date} ${data[user2].time} for ${user2} in ${data[user2].loc } is\n\
${showTimes(user2,date, data[user1].loc)} for ${user1}`
 );

这里有一个类似于上面使用 Luxon 的例子:

let DateTime  = luxon.DateTime;

let data = {
  john: {
    loc: 'America/Los_Angeles', // IANA representative location
    startTime: '16:15'          // Must be in HH:mm format
  },
  sally: {
    loc: 'America/New_York',
    startTime: '08:30'
  }
}

console.log('----- Standard time -----');
// What is the date and time at Sally's location when John starts on
// on Monday, 3 February 2020?
let targetDate = '2020-02-03';
let johnStartString = targetDate + 'T' + data.john.startTime;
let johnStartDate = DateTime.fromISO(johnStartString, {zone: data.john.loc});

// ISO string for John's startTime
console.log('When John starts at : ' + johnStartDate.toISO());

// Create a date for Sally's loc based on John's
let sallyDate = johnStartDate.setZone(data.sally.loc);
console.log('For Sally it\'s      : ' + sallyDate.toISO());

console.log('----- Daylight Saving time -----');
// What is the date and time at John's location when Sally starts on
// on Monday, 1 June 2020?
targetDate = '2020-06-01';
let sallyStartString = targetDate + 'T' + data.sally.startTime;
sallyStartDate = DateTime.fromISO(sallyStartString, {zone: data.sally.loc});

// ISO string for Sally's startTime
console.log('When Sally starts at: ' + sallyStartDate.toISO());

// Create a date for John's loc based on Sally's
let johnDate = sallyStartDate.setZone(data.john.loc);
console.log('For John it\'s       : ' + johnDate.toISO());
<script src="https://cdn.jsdelivr.net/npm/luxon@1.23.0/build/global/luxon.min.js"></script>