I have date with format '2021-05-01T23:59:59.999-05:00'
Need to fetch utc offset value like 300, 330 etc.
Can someone help here.Any Answer without using moment.js is appreciated.
So extracting value -05:00 from '2021-05-01T23:59:59.999-05:00'
The function Date.getTimezoneOffset() will only ever give you the offset between the client machine timezone and UTC, it won't give you the UTC offset as specified in the ISO date string.
Given that the dates are in ISO-8601 format, we can parse the UTC offset from the data, it will be in the format ±[hh]:[mm], ±[hh][mm], ±[hh] or 'Z' for UTC / Zulu time.
Negative UTC offsets describe a time zone west of UTC±00:00, where the civil time is behind (or earlier) than UTC so the zone designator will look like "−03:00","−0300", or "−03".
Positive UTC offsets describe a time zone at or east of UTC±00:00, where the civil time is the same as or ahead (or later) than UTC so the zone designator will look like "+02:00","+0200", or "+02".
We'll use regular expressions to parse the timezone offsets, this will work on IE 11 too.
function getUTCOffsetMinutes(isoDate) {
// The pattern will be ±[hh]:[mm], ±[hh][mm], or ±[hh], or 'Z'
const offsetPattern = /([+-]\d{2}|Z):?(\d{2})?\s*$/;
if (!offsetPattern.test(isoDate)) {
throw new Error("Cannot parse UTC offset.")
}
const result = offsetPattern.exec(isoDate);
return (+result[1] || 0) * 60 + (+result[2] || 0);
}
const inputs = [
'2021-05-01T23:59:59.999+12:00',
'2021-05-01T23:59:59.999+10',
'2021-05-01T23:59:59.999+0530',
'2021-05-01T23:59:59.999+0300',
'2021-05-01T23:59:59.999Z',
'2021-05-01T23:59:59.999-05:00',
'2021-05-01T23:59:59.999-07:00',
];
for(var i = 0; i < inputs.length; i++) {
console.log('input:', inputs[i], 'offsetMinutes:', getUTCOffsetMinutes(inputs[i]));
}
Pretty much the same as #TerryLennox but deals with:
Seconds in the offset—fairly common before 1900, added as decimal part of minutes
Missing offset—local, returns undefined
function parseOffset(ts) {
let [all,hr,min,sec] = (''+ts).match(/([+-]\d{2}|Z):?(\d{2})?:?(\d{2})?$/) || [];
let sign = hr < 0 ? -1 : 1;
return !all ? all : // No match, return undefined
hr == 'Z' ? 0 :
hr * 60 + (min? sign * min : 0) + (sec? sign * sec / 60 : 0);
}
['2021-05-01T23:59:59.999-05:30',
'2021-05-01T23:59:59.999+05:30',
'2021-05-01T23:59:59.999-0530',
'2021-05-01T23:59:59.999+0530',
'2021-05-01T23:59:59.999-05',
'2021-05-01T23:59:59.999+05',
'2021-05-01T23:59:59.999+053012',
'2021-05-01T23:59:59.999+05:30:12',
'2021-05-01T23:59:59.999Z',
'2021-05-01T23:59:59.999',
'blah'
].forEach(ts => console.log(ts + ' -> ' + parseOffset(ts)));
Related
I am using the following function to convert my date in RFC3339. I want it to convert in upper limit.
Can anyone assist me, how do I convert it to upper limit?
const date = new Date();
// RFC 3339 format
const targetTime = date.toISOString();
Current output is:
2022-12-20T05:26:12.968Z
Expected output should be
2022-12-20T06:00:00Z
See this answer, very similar but you can replace Math.round with Math.ceil to round up like you want and in addition you'll need to get the percentage the hour is complete (assuming you don't want to round up exact hours).
const milliSecondsInHour = 60*60*1000;
const roundDateToNextHour = (date: Date) => {
const percentHourComplete = (x.getTime() % milliSecondsInHour) / milliSecondsInHour;
date.setHours(date.getHours() + Math.ceil(percentHourComplete));
date.setMinutes(0, 0, 0); // Resets also seconds and milliseconds
return date;
}
If the intention is to the next full UTC hour, test if UTC minutes, seconds or milliseconds are greater than zero. If any of them are, increment the hour and zero the other values, e.g.:
// If the provided date is not exactly on the UTC hour,
// return a date that is the next full UTC hour after
// the provided date.
function toFullUTCHour(date) {
let d = new Date(+date);
d.setUTCHours(d.getUTCHours() + (d.getUTCMinutes() || d.getUTCSeconds() || d.getUTCMilliseconds? 1 : 0), 0,0,0);
return d;
}
let d = new Date()
console.log(d.toISOString() + '\n' +
toFullUTCHour(d).toISOString());
// Simplified version, only works in general for UTC hour
function ceilUTCHour(date = new Date()) {
let d = new Date(+date);
d.setHours(d.getHours() + (d%3.6e6? 1 : 0), 0, 0, 0);
return d;
}
console.log(ceilUTCHour(d).toISOString());
Since, in ECMAScript, UTC days are always exactly 8.64e7 ms long and hours are always exactly 3.6e6 ms long, you can just get the remainder of the current UTC time value and if it's not zero (which will be almost always) add 1 to the UTC hour, then zero the minutes seconds and milliseconds as for the ceilUTCHour function above.
While working on multiple timezones, I have a case where I create shifts in multiple timezones, now users apply for those shifts.
It's highly time-sensitive. Now what I want to take input of which timezone the shift is in (example Australia/Sydney)
Solution : Now while before saving it into the database I am converting the timezone to UTCoffset meaning in example Australia/Sydney timezone I am setting offset with - example Australia offset is 600 then -600 setting the offer and storing into the db.
const getUTCOffset = timezone => Intl.DateTimeFormat([], {timeZone: timezone, timeZoneName: 'shortOffset'}).formatToParts().find(o => o.type === 'timeZoneName').value.match(/\-?\d+/)[0]*60;
(getUTCOffset('Australia/Sydney'))
Is there anything I am missing here? What could be the optimal solution for this?
I think what you're missing is that offsets change from time to time due to historic changes and daylight saving. You are much better off to store the IANA location and calculate the offset as required.
If you want to get the offset for a location in minutes (which is convenient) then consider:
function getOffsetInMinutes(loc, date = new Date()) {
let [year, sign, hr, min] = date.toLocaleString('en', {
year:'numeric',
timeZone:loc,
timeZoneName:'longOffset'
}).match(/\d+|\+|\-/g);
return (sign == '+'? 1 : -1) * (hr*60 + min*1);
}
['Asia/Kolkata',
'Australia/Sydney',
'America/New_York'
].forEach(
loc => console.log(loc + ': ' + getOffsetInMinutes(loc))
);
For browsers that don't support the newer timeZoneName option values (like Safari ), you can use a function that uses the value "short" instead. The following returns the offset as ±HH:mm, it's not hard to modify to return minutes as above.
// Return offset on date for loc in ±HH:mm format
function getTimezoneOffset(loc, date=new Date()) {
// Try English to get offset. If get abbreviation, use French
let offset;
['en','fr'].some(lang => {
// Get parts - can't get just timeZoneName, must get one other part at least
let parts = new Intl.DateTimeFormat(lang, {
minute: 'numeric',
timeZone: loc,
timeZoneName:'short'
}).formatToParts(date);
// Get offset from parts
let tzName = parts.filter(part => part.type == 'timeZoneName' && part.value);
// timeZoneName starting with GMT or UTC is offset - keep and stop looping
// Otherwise it's an abbreviation, keep looping
if (/^(GMT|UTC)/.test(tzName[0].value)) {
offset = tzName[0].value.replace(/GMT|UTC/,'') || '+0';
return true;
}
});
// Format offset as ±HH:mm
// Normalise minus sign as ASCII minus (charCode 45)
let sign = offset[0] == '\x2b'? '\x2b' : '\x2d';
let [h, m] = offset.substring(1).split(':');
return sign + h.padStart(2, '0') + ':' + (m || '00');
}
['Asia/Kolkata','Australia/Sydney','America/New_York'].forEach(
loc => console.log(loc + ': ' + getTimezoneOffset(loc))
);
I’m working on a website that teachers are entering their availability based on their local time zone
David from California is available Monday and Tuesday at 4 PM PST for example.
I want to show that availability to everyone else internationally in their local format
John from New York can see David is available at 7 PM EST
Is there standard way of doing this without storing local time zone in the db?
I was thinking just pick a random date (or even now) and stick the hour/minute to it, save it in UTC and to display it just ignore the date part. does that sound reasonable or there is a better way?
When storing local times, the related timezone data should be stored as well. The most portable identifiers at the moment are IANA representative locations like 'America/New_York'. That way changes to standard and daylight saving offsets are accommodated so that given a particular date, you can get details for one person's time and show it as a date and time for another person's location, adjusting for their offset on that date.
The following shows an algorithm, it uses a rough function from here, but I would strongly suggest using a library like Luxon instead, I just wanted to keep this plain JS.
The following gets a time and location for one user, then displays it as an equivalent time in the location of another user. Hopefully it's something along the lines of what you want to do.
// 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}`
);
Here's an example similar to the above using 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>
I would store:
initial time of the day: int
end time of the day: int
original timezone: string
then, showing that to users is a UI problem.
you could calculate dynamically two dates (based on the stored times) in the original timezone and convert it to any target timezone on the fly.
an alternative is checking the time difference between original and target timezones (without calculating any date) and adding it to the initial/end times.. but I guess it's easier to go for the first option as the date classes have that kind of utils.
Keeping track of start and end hours can result in weird timezone errors.
For example, if someone selects Monday 6pm-9pm in EST, that's actually Monday 11pm - Tuesday 2am in UTC. That means the time range stored in UTC is Start: 11pm and End: 2am, which requires lots of code to work around these different scenarios.
A better idea may be to keep track of the starting hour and the number of hours until the ending time (elapsed time).
In C# you can use
System.TimeZone.CurrentTimeZone.GetUtcOffset(someDate).Hours
But how can I get UTC offset in hours for a certain date (Date object) in javascript?
Vadim's answer might get you some decimal points after the division by 60; not all offsets are perfect multiples of 60 minutes. Here's what I'm using to format values for ISO 8601 strings:
function pad(value) {
return value < 10 ? '0' + value : value;
}
function createOffset(date) {
var sign = (date.getTimezoneOffset() > 0) ? "-" : "+";
var offset = Math.abs(date.getTimezoneOffset());
var hours = pad(Math.floor(offset / 60));
var minutes = pad(offset % 60);
return sign + hours + ":" + minutes;
}
This returns values like "+01:30" or "-05:00". You can extract the numeric values from my example if needed to do calculations.
Note that getTimezoneOffset() returns a the number of minutes difference from UTC, so that value appears to be opposite (negated) of what is needed for formats like ISO 8601. Hence why I used Math.abs() (which also helps with not getting negative minutes) and how I constructed the ternary.
I highly recommend using the moment.js library for time and date related Javascript code.
In which case you can get an ISO 8601 formatted UTC offset by running:
> moment().format("Z")
> "-08:00"
<script type="text/javascript">
var d = new Date()
var gmtHours = -d.getTimezoneOffset()/60;
document.write("The local time zone is: GMT " + gmtHours);
</script>
I'm interfacing with an api and they use .NET so all of my time stamps need to conform to .NET's Date Time format which looks something like this
/Date(1379142000000-0700)/
I would like to convert unix times to this format using javascript. I've seen this function for moment.js but this dosn't return the unix/epoch formatting and it's in the wrong direction.
How can I convert unix timestamp to .net time formatting with javascript?
solutions using moment.js are good, and bonus points for converting from .net to unix as well.
If you have a date object, it seems like you need the UTC millisecond time value and the timezone offset in hours and minutes (hhmm). So presuming that the UNIX time value is UTC and that the ".NET" time string is a local time value with offset, then:
function unixTimeToDotNetString(v) {
// Simple fn to add leading zero to single digit numbers
function z(n) {
return (n < 10 ? '0' : '') + n;
}
// Use UNIX UTC value to create a date object with local offset
var d = new Date(v * 1e3);
// Get the local offset (mins to add to local time to get UTC)
var offset = d.getTimezoneOffset();
// Calculate local time value by adding offset
var timeValue = +d + offset * 6e4;
// Get offset sign - reverse sense
var sign = offset < 0 ? '+' : '-';
// Build offset string as hhmm
offset = Math.abs(offset);
var hhmm = sign + z(offset / 60 | 0);
hhmm += z(offset % 60);
// Combine with time value
return timeValue + hhmm;
}
var unixTime = 1393552984;
console.log(unixTime + ' : ' + unixTimeToDotNetString(unixTime)); // 1393552984 : 1393517104000+1000
The difference between the two time values shoudl be equivalent to the offset in milliseconds (in this case where the timezone is UTC+1000 it's 36000000).