I have some logic designed to generate a formatted duration based on an inputted start and stop time:
getFormattedDuration = async (stopValue, startValue) => {
const durValue = moment(stopValue, "H:mm").diff(moment(startValue, "H:mm"));
const t = moment.duration(durValue);
const duration = {
hours: t.hours(),
minutes: t.minutes()
}
const formattedDuration = `0${duration.hours}:${duration.minutes}`;
return formattedDuration;
// I'm prepending a `0` here because I know the duration will never be above a certain limit - i.e. it will never get to a double digit number for hours.
}
This works for the most part. But on occasion I will end up with something like this for output:
duration: Object {
"hours": 0,
"minutes": 8,
}
formattedDuration: 00:8
What I want here is 00:08. How can I pad with zeroes, as necessary, to ensure I get a correctly formatted duration value - which should be in this format hh:mm. Does moment.js have a function I can use to handle this?
You can use the format() function from moment.js.
const m = moment('00:8','HH:mm').format('HH:mm');
console.log(m) // "00:08"
m = moment('00:8','HH:mm').format('HH:mm');
console.log(m)
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js" integrity="sha512-qTXRIMyZIFb8iQcfjXWCO8+M5Tbc38Qi5WzdPOYZHIlZpzBHG3L3by84BBBOiRGiEb7KKtAOAs5qYdUiZiQNNQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
You could use to .padStart() to ensure there are leading zeros.
Related
I have string data in the format "hh:mm" e.g. 05:00. I want it in Milliseconds e.g 1800000
console.log(DateTime.fromISO("05:00") and i get the following output: 1654574400000 but what i want it in seconds so i can compare it against a different value. I have tried putting .toMillis() at the end
console.log(DateTime("05:00")).toMillis();
and it comes back with "Unhandled Rejection (TypeError): Class constructor DateTime cannot be invoked without 'new'".
You can parse "05:00" as a Duration, using Duration.fromISOTime that:
Create a Duration from an ISO 8601 time string.
and then display its value using as(unit):
Return the length of the duration in the specified unit.
Example:
const Duration = luxon.Duration;
console.log(Duration.fromISOTime('05:00').as('milliseconds'));
<script src="https://cdn.jsdelivr.net/npm/luxon#2.4.0/build/global/luxon.min.js"></script>
When a time is passed to fromISO, the current date is used. To get the time in milliseconds, parse it to a DateTime and subtract a DateTime for the start of the day, e.g.
let DateTime = luxon.DateTime;
function timeToMs(time) {
let then = DateTime.fromISO(time);
let now = DateTime.fromISO("00:00");
return then - now;
}
console.log(timeToMs('05:00'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/luxon/2.4.0/luxon.min.js"></script>
You can also use plain JS:
function timeToMS(time) {
let [h, m, s, ms] = time.split(/\D/);
return h*3.6e6 + (m||0)*6e4 + (s||0)*1e3 + (ms||0)*1;
}
console.log(timeToMS('05:00'));
console.log(timeToMS('01:01:01.001'));
I need to convert string: '20211114025320+0000' to JS Date object
(2021-11-14T02:53:20.000Z).
I have this format for info ('YYYYMMDDHHmmssZ')
maybe I need to use a custom function for this?
It looks like what you have is an ISO 8601 string with all the separators stripped away.
Therefore, a straight-forward way would be to add these separators back using a regex replace and then parsing it using the built-in parser:
function parseCustomDate (s) {
const isoString = s.replace(/^(....)(..)(..)(..)(..)(.*)$/, '$1-$2-$3T$4:$5:$6')
return new Date(isoString)
}
(Note that here the last capture group with the .* captures both seconds and the timezone specifier at once, because those don't need a separator between them anyway. To make it clearer, you could also use (..)(.*) and $6$7 instead of only (.*) and $6 as I did.)
Another way that is faster computationally but also more complex to read, understand and catch bugs in (in my opinion at least) would be to take the individual parts of the string and pass them to the Date.UTC constructor instead of going through the ISO string route:
function parseCustomDate (s) {
const year = Number(s.slice(0, 4))
const month = Number(s.slice(4, 6))
const day = Number(s.slice(6, 8))
const hour = Number(s.slice(8, 10))
const minute = Number(s.slice(10, 12))
const second = Number(s.slice(12, 14))
const tzSign = s.slice(14, 15)
const tzHour = Number(tzSign + s.slice(15, 17)) || 0
const tzMinute = Number(tzSign + s.slice(17, 19)) || 0
return new Date(Date.UTC(
year, month - 1, day,
hour - tzHour, minute - tzMinute, second
))
}
Explanation for the timezone handling here: JavaScript accepts "invalid" dates that have individual parts outside of the regular range by rolling them over, so e.g. 11:70:00 would become 12:10:00. This is why we can simply subtract the timezone parts (with each part also capturing the +/- sign), and we don't even have to mess with multiplying the minutes by 60 because we can handle them in the minutes part too.
I added the || 0 so that a string with a Z as timezone which would otherwise make tzHour and tzMinute NaN would also be handled as zero timezone offset.
A function to parse the string and pass the parts directly the Date constructor may be more efficient than parsing the string to generate another string that is then parsed by the built–in parser.
This method also avoids the built–in parser, which is usually considered a good idea.
// Parse YYYYMMDDHHmmss±HHmm or YYYYMMDDHHmmssZ
function parseTS(ts) {
// Get parts
let [C,Y,M,D,H,m,s,sign,oH,oM] = ts.match(/\d\d|\D/g);
// Deal with offset: ±HHmm or Z
if (sign == 'Z') {
sign = oH = oM = 0;
}
let oSign = sign == '+' ? -1 : +1;
// Create date from parts, adjust H, m for offset
return new Date(Date.UTC(C+Y, M-1, D, +H + oH*oSign, +m + oM*oSign, s));
}
['20211114082320+0530',
'20211114025320+0000',
'20211114025320Z',
'20211113225320-0400'
].forEach(ts => console.log(ts + '\n' + parseTS(ts).toISOString()));
{
"time": 1627375726.8367202,
"tzoffset": -25200,
"ok": 1
}
Using above JSON I need to get date, time and timezone. I have tried many ways but could not succeed. Is there a way to get date, time and timezone. I am using JavaScript.
Thanks,
Check out there Documentation
A Little Example is here
moment.parseZone("2013-01-01T00:00:00-13:00").utcOffset(); // -780 ("-13:00" in total minutes)
We can use your specified unix time and offset to get the current date and time using the moment.utcOffset function. We have to divide your tzoffset by 60, since it is (presumably) in seconds whereas moment expects minutes.
It's usually not possible to get a unique timezone value, since your tzoffset is going to be shared by multiple zones, the best we can do is get a list of these:
const input = {
"time": 1627375726.8367202,
"tzoffset": -25200,
"ok": 1
};
console.log("UTC time:", moment.unix(input.time).utc().format('YYYY-MM-DD HH:mm'))
console.log("Time in Timezone:", moment.unix(input.time).utcOffset(input.tzoffset / 60).format('YYYY-MM-DD HH:mm'))
console.log("Time in Timezone (with offset):", moment.unix(input.time).utcOffset(input.tzoffset / 60).format())
console.log("Possible timezones:", getPossibleTimezones(input.time, input.tzoffset))
function getPossibleTimezones(time, tzoffset) {
return moment.tz.names().filter(zone => moment.tz(moment.unix(time), zone).utcOffset() === input.tzoffset / 60);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js" referrerpolicy="no-referrer"></script>
<script src="https://momentjs.com/downloads/moment-timezone-with-data.js"></script>
I'd also consider using luxon since moment is now in maintenance.
We can use the FixedOffsetZone to get local time in that zone:
const input = {
"time": 1627375726.8367202,
"tzoffset": -25200,
"ok": 1
};
let { DateTime, FixedOffsetZone } = luxon;
let zone = new FixedOffsetZone(input.tzoffset / 60)
console.log("UTC time:", DateTime.fromSeconds(input.time, { zone: 'UTC' }).toISO())
console.log("Time in timezone:", DateTime.fromSeconds(input.time, { zone: zone }).toISO())
<script src="https://cdnjs.cloudflare.com/ajax/libs/luxon/2.0.1/luxon.min.js" integrity="sha512-bI2nHaBnCCpELzO7o0RB58ULEQuWW9HRXP/qyxg/u6WakLJb6wz0nVR9dy0bdKKGo0qOBa3s8v9FGv54Mbp3aA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
I have duration string that looks like:
1:16.352
where 1 is minutes part, 16 is seconds part and 352 is millisecond part.
I wanted to use Duration.fromISOTime but I get:
{
"reason": "unparsable",
"explanation": "the input \"1:16.352\" can't be parsed as ISO 8601"
}
Is there a clean way of parsing such duration in Luxon?
Duration.fromISOTime does not work since 1:16.352 is not an ISO 8601 time string, the hour part is missing (see ISO 8601 Times).
A workaround to build a Luxon Duration object could be the following:
const DateTime = luxon.DateTime;
const Duration = luxon.Duration;
const startOfHour = DateTime.local().startOf('hour').toMillis();
const dt = DateTime.fromFormat("1:16.352", "m:ss.SSS"). toMillis();
const dur = Duration.fromMillis(dt - startOfHour);
console.log(dur.toFormat("m 'minute' s 'second' S 'millis'"));
<script src="https://cdn.jsdelivr.net/npm/luxon#1.26.0/build/global/luxon.js"></script>
Similarly to #VincenzoC I adjusted my input string:
const Duration = luxon.Duration;
var output;
const durationInput = "1:16.352"
if (durationInput.match(/:/g) || [].length === 1) {
const semicolonLocation = durationInput.indexOf(":");
if (semicolonLocation === 1) {
output = "00:0" + durationInput;
}
if (semicolonLocation === 2) {
output = "00:" + durationInput;
}
}
console.log(Duration.fromISOTime(output));
<script src="https://cdn.jsdelivr.net/npm/luxon#1.26.0/build/global/luxon.js"></script>
As I mentioned in the comments, you can use the fromObject static method combined with simply splitting the input into minutes, seconds, and milliseconds configuration options. In your case a trivial regular expression (\d+):(\d+)\.(\d+) should do the trick, no temporary dates or normalization required.
const { Duration } = luxon;
const durationInput = "1:16.352";
const fromCustom = (input) => {
const [, minutes, seconds, milliseconds ] = input.match(/(\d+):(\d+)\.(\d+)/);
return Duration.fromObject({
minutes, seconds, milliseconds
});
};
console.log(fromCustom(durationInput));
<script src="https://cdn.jsdelivr.net/npm/luxon#1.26.0/build/global/luxon.js"></script>
I've got a server instance (NodeJS) that receives a set of objects, and schedules them for sending push notifications to users.
Some of these objects, are periodic, and this periodicity is handled by a string like this:
90=>Mon&Tue&Thu=>16:00
Which is read as:
offset_minutes=>days_of_the_week=>initial_hour
Then, what I do is to check whether the current day matches one of the given days in the string, and then, modify the date to the given hour in the "initial_hour", and finally, substract the "offset_minutes" amount of minutes from the Date object.
Seems straightforward until now, right? Well, not that much. Let's first see the code:
const isToday = weekDays.split("&")
.map(a => {
switch (a) {
case 'Mon': return 1;
case 'Tue': return 2;
case 'Wed': return 3;
case 'Thu': return 4;
case 'Fri': return 5;
case 'Sat': return 6;
case 'Sun': return 7;
}
})
.some(v => v == currentDay);
if (isToday) {
let finalDate = moment(today)
.set("hour", Number(hour))
.set("minute", Number(mins));
if (offset) {
finalDate.subtract('minutes', Number(offset));
}
return finalDate.toDate();
Everything works well, until I do the MomentJS transformations. When I output a Date object with the ".toDate()" method, this object is always set to 2 hours before the expected time. But if I use the .toISOString() method, I get the proper time for all the occurrencies.
I guess that something is wrong with my Date objects, setting them up at a different timezone than the one I have. A couple of examples:
For the string 90=>Mon&Tue&Thu=>16:00 I get the Date object: 2019-10-14T14:00:11.852Z
For the string 30=>Mon&Tue&Wed&Thu&Fri&Sat&Sun=>18:30 I get the Date object: 2019-10-14T16:30:11.866Z
I would like to know what's the explanation for such a behavior, and if I can do something to change it so the normal Javascript Date object points to the same hour than my momentjs object, or the .toISOString() output.
Thank you!
The posted code is incomplete and doesn't demonstrate the issue described.
I've reimplemented the code without moment.js as best I can and simplified it. It seems to work fine:
function parseThing(s) {
// Parse input string
let b = s.split('=>');
let offset = +b[0];
let days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
let weekDays = b[1].split('&').map(day => days.indexOf(day));
let [hr, min] = b[2].split(':');
// Get a date for today
let date = new Date();
// If today included, return an adjusted date
if (weekDays.includes(date.getDay())) {
date.setHours(hr, min, 0, 0);
if (offset) {
date.setMinutes(date.getMinutes()+ Number(offset));
}
return date;
}
// If today isn't included, return null
return null;
}
let s0 = '90=>Mon&Tue&Thu=>16:00';
let s1 = '0=>Mon&Tue&Wed&Thu&Fri&Sat&Sun=>18:30';
console.log(parseThing(s0).toString());
console.log(parseThing(s1).toString());
Where the local day is one of those in the string (Mon, Tue, Thu) it returns a Date equivalent to a local time of 17:30, which is 90 minutes offset from 16:00, which seems to be correct.
PS I've changed Sunday to 0 as I can't see any rationale for it to be 7. Also seconds and milliseconds are zeroed too.