При хранении местного времени также должны храниться данные о часовом поясе. На данный момент наиболее переносимые идентификаторы - это представительства IANA, такие как «Америка / Нью-Йорк» Таким образом, изменения в стандартном и летнем смещениях учитываются, так что с учетом конкретной даты вы можете получить сведения о времени одного человека и отобразить его как дату и время для местоположения другого человека с поправкой на его смещение на эту дату.
Ниже приведен алгоритм, он использует грубую функцию из здесь , но я бы настоятельно рекомендовал использовать библиотеку, подобную Luxon , вместо этого, я просто хотел сохранить эту простую JS.
Следующее получает время и местоположение для одного пользователя, а затем отображает его как эквивалентное время в местоположении другого пользователя. Надеюсь, это что-то вроде того, что вы хотите сделать.
// Function from https://stackoverflow.com/a/61364310/257182
/* @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>