Moment get next date in arrays of dates - node.js

I have an array of dates in the format of March 1st or May 2nd and I want to use moment.js to iterate through those dates and figure out which one is closest to the current day in nodejs.

You must cycle every element in the array, and calculate the absolute difference (with Math.abs()) between the date of array element and now (moment()).
You must specify the format of array elements, in this case the moment format is MMMM Do.
const arr = ['March 1st', 'April 1st', 'April 28th', 'May 1st', 'May 2nd', 'September 26th'];
let lastDiff;
let nearest;
for (const date of arr) {
const diff = Math.abs(moment(date, 'MMMM Do').diff(moment(), 'days'));
if (lastDiff === undefined || lastDiff > diff) {
lastDiff = diff;
nearest = date;
}
}
console.log(nearest)
console.log(moment(nearest, 'MMMM Do').format())
This method considers that year is the same of now.

We can use Array.reduce to find the nearest date to the current day. We parse each date to get a moment instance, then get the absolute offset from the current date. If this is lower than the nearest value, we replace it.
const dates = ["January 1st", "March 28th", "April 12th", "April 25th", "May 2nd", "September 8th"];
const now = moment();
const nearestDate = dates.reduce((nearest, dateStr) => {
const momentDate = moment(dateStr, "MMMM D");
const timeFromNow = Math.abs(momentDate - now);
const nearestTimeFromNow = Math.abs(nearest - now);
if (!nearestTimeFromNow || timeFromNow < nearestTimeFromNow) {
nearest = momentDate;
}
return nearest;
})
console.log("Nearest date to now:", nearestDate.format("MMMM Do"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>

Related

How to construct the right datetime ranges for timezoned clients using Firestore?

I have a Cloud function that runs everyday, every 10 mins. It calculates some numbers regarding orders and customers for each restaurant branch.
Every branch has working hours, for example:
06:00 - 03:00 (24H)
This means from 6:00 Tuesday to 3:00 Wednesday.
I made the function so that it handles the current day hours and whatever that is left of the previous day.
Tuesday(remaining 3 hours for Monday):
00:00 - 03:00
Tuesday:
06:00 - 23:59:59:999
Here's how the code handles generating the ranges:
const getRangeForOneDay = async (forPreviousDay = false) => {
const lowerBound = "06:00"
const upperBound = "03:00";
const lowerBoundSplit = ["06", "00"];
const startOfToday = new Date();
startOfToday.setHours(
forPreviousDay ? 0 : parseInt(lowerBoundSplit[0]),
forPreviousDay ? 0 : parseInt(lowerBoundSplit[1]),
0,
0
);
const upperBoundSplit = ["03", "00"];
const endOfToday = new Date();
endOfToday.setHours(
forPreviousDay ? parseInt(upperBoundSplit[0]) : 23,
forPreviousDay ? parseInt(upperBoundSplit[1]) : 59,
forPreviousDay ? 0 : 59,
forPreviousDay ? 0 : 999
);
return [startOfToday, endOfToday];
};
const rangeToday = getRangeForOneDay(false);
const rangeYesterday = getRangeForOneDay(true);
const ranges = [rangeToday, rangeYesterday];
const branchId = "some real id";
for (const range of ranges) {
const [startOfToday, endOfToday] = range;
const orders = await admin
.firestore()
.collection("Orders")
.where("branch_id", "==", branchId)
.where("create_on", ">=", startOfToday)
.where("create_on", "<=", endOfToday)
.orderBy("create_on", "desc")
.get();
console.log(`Orders length = ${orders.size}`);
}
Here's how the ranges would look like:
Current Day(Tuesday):
From Date = 2022-11-08T06:00:00.000Z
To Date = 2022-11-08T23:59:59.999Z
Previous Day(Monday remaining hours data):
From Date = 2022-11-08T00:00:00.000Z
To Date = 2022-11-08T03:00:00.000Z
Both ranges are correct. You can see that the second range is for the previous day calculations, yet it takes place during the first 3 hours of the current day(Monday), which is also what I want.
My problem is that, for the second range, the query always returns 0. Despite creating an order between 12AM and 3AM Today(Tuesday) Nov 11th.
The order I created was at 12:44:33 AM, but still it does not show up!
What am I doing wrong in the ranges?
I just want the code to work for this exact case. The normal case(first range), should work fine I think.
By the way, the timezone I am in is Toronto(-5). Though I don't think it matters since Firestore saves dates in UTC, just displays them on the Cloud Firestore in local timezone.
The time ranges in output are in UTC (notice the Z instead of -05:00). If the time range is supposed to be Toronto time then you'll have to convert it first. Try using luxon and refactoring to code as shown below:
import { DateTime } from "luxon";
const getRangeForOneDay = () => {
const today = DateTime.now().setZone("America/Toronto").startOf("day");
return [
[today.toJSDate(), today.plus({ hour: 3 }).toJSDate()],
[today.set({ hour: 6 }).toJSDate(), today.endOf("day").toJSDate()],
];
};
The time ranges that you've mentioned are technically on the same day of your timezone or I renamed the var names above.
const ranges = getRangeForOneDay();
console.log('>>> ranges', ranges)
const branchId = "some real id";
for (const range of ranges) {
const [startOfToday, endOfToday] = range;
const orders = await admin
.firestore()
.collection("Orders")
.where("branch_id", "==", branchId)
.where("create_on", ">=", startOfToday)
.where("create_on", "<=", endOfToday)
.orderBy("create_on", "desc")
.get();
console.log(`Orders length = ${orders.size}`);
}
Now document with time 00:44 should fall in 00:00 - 03:00 UTC-5 time range.

How to find the date of next given name of the day in node.js

I have to find the date of the next given day in node.js.
Consider date as 2016-07-13(wednesday), I have to find the date of next given day(ex monday, tuesday,etc)
Suppose consider its monday it should be 2016-07-18.
EDIT:
Consider date 2016-07-13, based on this date what is the nearest monday(desired) date.
EDIT
var target;
if(desiredIndex < moment(start).day()){
target = moment(start).day(7 + desiredIndex).toDate()
} else {
target = moment(start).day(desiredIndex).toDate();
}
I would use moment.js and use .day() to set the target day of week as below. Add 7 to force "next week" semantics.
const moment = require('moment')
const dayOfWeekIndex = [
'sunday',
'monday',
'tuesday',
'wednesday',
'thursday',
'friday',
'saturday']
const start = '2016-07-13'
const desired = 'tuesday' // change to suit taste.
const desiredIndex = dayOfWeekIndex.indexOf(desired)
// 7 means "next week"
const target = moment(start).day(7 + desiredIndex).toDate()
console.log(target)
What I would do is use Moment.js and use the moment.add(1, 'days').calendar(); operation.
The way I would do it, inside a function is saving the result into a date object, adding one by one in a for loop until your object contains the desired week day you trying to get, then you return that date.

How to determine two date is same day at local time in nodejs?

Here are two date, 2015-11-10 09:00:00+08:00 and 2015-11-10 01:00:00+08:00 is same day in China. But they are different day in UTC.
So... How can I determine two UTC date is same day at different timezone in node.js?
Sorry for my ambiguous description and poor English.
I have two strings: start_time and end_time. And clint upload two other strings: timezone_str, utc_time_str.
I want to determine the client time is between start and end time.
e.g
start_time: 2015-11-10 00:00:00
end_time: 2015-11-11 00:00:00
utc_time_str: 2015-11-10 02:00:00Z
timezone_str: America/New_York
except client_time.isBetween(start_time, end_time) == false.
Thanks to #Matt Johnson's answer, moment-timezone can solve my problem.
client_time = moment(utc_time_str).tz(timezone_str);
start = moment.tz(start_time, timezone_str);
end = moment.tz(end, timezone_str);
client_time.isBetween(start, end) === false
The most reliable way would be to use the parseZone function in moment.js to retain the provided offset. Then use the isSame function with the 'day' argument, which tests for same year, month, and day.
var m1 = moment.parseZone("2015-11-10 09:00:00+08:00");
var m2 = moment.parseZone("2015-11-10 01:00:00+08:00");
if (m1.isSame(m2, 'day')) {
// ...
}
With this approach, it does not matter what offset was given, nor does it matter what the time zone is on the computer where the code is running. It simply compares the two dates as provided.
calculate local "hours" like this
var d1 = new Date('2015-11-10 09:00:00+08:00')
var d2 = new Date('2015-11-10 01:00:00+08:00')
var h1 = d1.getHours() - d1.getTimezoneOffset() / 60
var h2 = d2.getHours() - d2.getTimezoneOffset() / 60
var date1 = d1.getDate() + (h1 >= 24? 1: 0)
var date2 = d2.getDate() + (h2 >= 24? 1: 0)
just compare date1 and date2
Solution without MomentJS
If you have the timezone string of the user that is in accordance with the tz database, such as "America/New_York" — which you can get client side for example by doing: const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
Then you could, for each date, create a new date with the timezone offset added to the original date, for example by doing const dateInTimezone = new Date(new Date(myDate).toLocaleString("en-US", { timeZone: timezone }));
And then reset the time values to exactly midnight of the same day by doing dateInTimezone.setHours(0, 0, 0, 0);
Then you could compare the milliseconds since Epoch values (date we start counting UTC from) of the two dates to see whether they start in the same day or not: const isSameDay = startOfDate1.valueOf() === startOfDate2.valueOf();
Ideally, your starting dates would always be in UTC, since both your server and client could be in all sorts of timezones. So before storing the data, convert the original date to its UTC version in milliseconds since Epoch, by doing myOrignalDate.valueOf() or myOrignalDate.getTime() — this way, you will ensure that timezone offsets added later will be true.
Example
function isSameDayInTimezone(d1, d2, timezone) {
const start_of_d1 = new Date(new Date(d1).toLocaleString("en-US", { timeZone: timezone })).setHours(0, 0, 0, 0).valueOf();
const start_of_d2 = new Date(new Date(d2).toLocaleString("en-US", { timeZone: timezone })).setHours(0, 0, 0, 0).valueOf();
return start_of_d1 === start_of_d2;
}
/* Initialize dates. Note that this will be the UTC time as milliseconds since
Epoch of the date in whichever timezone it was originally captured in. If I
capture it in my timezone, it would currently be UTC/GMT+3, hence if I were
to want to perform server-side actions taking into account my timezone, I'd
also need to capture my tz database timezone string first, to then be able
to, server-side, add my timezone offset to the captured UTC time. */
const date1 = new Date('August 31, 2022 20:16:00').valueOf();
const date2 = new Date('August 31, 2022 23:50:00').valueOf();
const date3 = new Date('August 31, 2022 00:00:00').valueOf();
const date4 = new Date('August 30, 2022 15:00:00').valueOf();
const fixedTimezone = "Europe/Tallinn";
const localTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
/* With fixed timezone */
console.log(isSameDayInTimezone(date1, date2, fixedTimezone));
console.log(isSameDayInTimezone(date1, date3, fixedTimezone));
console.log(isSameDayInTimezone(date1, date4, fixedTimezone));
/* With local timezone */
console.log(isSameDayInTimezone(date1, date2, localTimezone));
console.log(isSameDayInTimezone(date1, date3, localTimezone));
console.log(isSameDayInTimezone(date1, date4, localTimezone));
Voila, no MomentJS needed.

MomentJS: Compare time in different timezones

I have array of timezones and I want to know which one is the 'earliest one' where earliest is the one in the earliest time zone.
For example:
Timezone X is Thursday 5pm
Timezone Y is Thursday 7pm
Than X is the earliest among them.
I'm using momentjs and momentjs-timzone in NodeJS (not the browser package)
I'm trying to compare the millisecond timestamp but this give me the same value for both:
if (momenttz().tz(timezoneX).format('x') > momenttz().tz(timezoneY).format('x'))
In NodeJS syntax it can accomplished by formatting timezones to offset values, finding the minimum component and returning its Timezone value:
var moment = require('moment-timezone');
var inputTzArray = ['Pacific/Chatham', 'America/St_Johns', 'Asia/Jerusalem', 'America/New_York', 'America/Los_Angeles'];
var tzOffsets = []
for (var i in inputTzArray) {
var s = moment.tz(inputTzArray[i]).format('Z'); //format to offset
tzOffsets[i] = parseFloat(s.replace(':','.')); //parse int offset
}
console.log(tzOffsets);
var minIndex = tzOffsets.indexOf(Math.min.apply(Math, tzOffsets)); //find minimum's index
console.log(inputTzArray[minIndex]); //Output earliest timezone
A live demo by running "node server.js" in the interactive console in the link: http://runnable.com/VJ3M3XdZAVg-EVOP

How do I display todays date in Node.js Jade?

I am new to Node.js and Jade and I have tried to use #{Date.now()} and it's giving me numbers. How do I display the date in mm/dd/yy format?
You can use moment.js
First, add moment to your express application locals
express = require('express');
...
app = express();
app.locals.moment = require('moment');
Then you can use moment within a jade template like this:
p #{moment(Date.now()).format('MM/DD/YYYY')}
Moment takes Date.now() by default, so yo can also write:
p #{moment().format('MM/DD/YYYY')}
Sources:
Making use of utility libraries in server-side Jade templates
Moment.js
This is old, but I do the following:
return (new Date()).toLocaleDateString()
Returns a date 'mm/dd/yyyy' format, in no additional libs required.
Or you could use the moment.js library to handle dates and format them accordingly to your needs
I actually started using date-util which is 2 parts both clientside and server side.
URL https://github.com/JerrySievert/node-date-utils
Using within a Browser
<script type="text/javascript" src="date-utils.min.js"></script>
Using with Node.js
$ npm install date-utils
require('date-utils');
Note: This did not work in the REPL before Node.js 0.6 due to how Node.js handles context in the REPL.
Static Methods
Date.today(); // today, 00:00:00
Date.yesterday(); // yesterday, 00:00:00
Date.tomorrow(); // tomorrow, 00:00:00
Date.validateDay(day, year, month); // true/false whether a date is valid
Date.validateYear(year); // true/false whether a year is valid
Date.validateMonth(month); // true/false whether a month is valid
Date.validateHour(hour); // true/false whether an hour is valid
Date.validateMinute(minute); // true/false whether a minute is valid
Date.validateSecond(second); // true/false whether a second is valid
Date.validateMillisecond(millisecond); // true/false whether a millisecond is valid
Date.compare(date1, date2); // -1 if date1 is smaller than date2, 0 if equal, 1 if date2 is smaller than date1
Date.equals(date1, date2); // true/false if date1 is equal to date2
Date.getDayNumberFromName(name); // su/sun/sunday - 0, mo/mon/monday - 1, etc
Date.getMonthNumberFromName(name); // jan/january - 0, feb/february - 1, etc
Date.isLeapYear(year); // true/false whether the year is a leap year
Date.getDaysInMonth(monthNumber); // number of days in the month
Instance Methods
d.clone(); // returns a new copy of date object set to the same time
d.getMonthAbbr(); // abreviated month name, Jan, Feb, etc
d.getMonthName(); // fill month name, January, February, etc
d.getUTCOffset(); // returns the UTC offset
d.getOrdinalNumber(); // day number of the year, 1-366 (leap year)
d.clearTime(); // sets time to 00:00:00
d.setTimeToNow(); // sets time to current time
d.toFormat(format); // returns date formatted with:
// YYYY - Four digit year
// MMMM - Full month name. ie January
// MMM - Short month name. ie Jan
// MM - Zero padded month ie 01
// M - Month ie 1
// DDDD - Full day or week name ie Tuesday
// DDD - Abbreviated day of the week ie Tue
// DD - Zero padded day ie 08
// D - Day ie 8
// HH24 - Hours in 24 notation ie 18
// HH - Padded Hours ie 06
// H - Hours ie 6
// MI - Padded Minutes
// SS - Padded Seconds
// PP - AM or PM
// P - am or pm
d.toYMD(separator); // returns YYYY-MM-DD by default, separator changes delimiter
d.between(date1, date2); // true/false if the date/time is between date1 and date2
d.compareTo(date); // -1 if date is smaller than this, 0 if equal, 1 if date is larger than this
d.equals(date); // true/false, true if dates are equal
d.isBefore(date); // true/false, true if this is before date passed
d.isAfter(date); // true/false, true if this is after date passed
d.getDaysBetween(date); // returns number of full days between this and passed
d.getHoursBetween(date); // returns number of hours days between this and passed
d.getMinutesBetween(date); // returns number of full minutes between this and passed
d.getSecondsBetween(date); // returns number of full seconds between this and passed
d.add({ milliseconds: 30,
minutes: 1,
hours: 4,
seconds: 30,
days: 2,
weeks: 1,
months: 3,
years: 2}); // adds time to existing time
d.addMilliseconds(number); // add milliseconds to existing time
d.addSeconds(number); // add seconds to existing time
d.addMinutes(number); // add minutes to existing time
d.addHours(number); // add hours to existing time
d.addDays(number); // add days to existing time
d.addWeeks(number); // add weeks to existing time
d.addMonths(number); // add months to existing time
d.addYears(number); // add years to existing time
You will need to use the methods on the Date object to achieve what you're after. See https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Date
For example, the code below should do what you need:
var dateNow = new Date();
var dd = dateNow.getDate();
var monthSingleDigit = dateNow.getMonth() + 1,
mm = monthSingleDigit < 10 ? '0' + monthSingleDigit : monthSingleDigit;
var yy = dateNow.getFullYear().toString().substr(2);
var formattedDate = mm + '/' + dd + '/' + yy;
So if you were using say jade with express and node you could do something like:
res.render('jadeTemplateName', {
dateNow: function() {
var dateNow = new Date();
var dd = dateNow.getDate();
var monthSingleDigit = dateNow.getMonth() + 1,
mm = monthSingleDigit < 10 ? '0' + monthSingleDigit : monthSingleDigit;
var yy = dateNow.getFullYear().toString().substr(2);
return (mm + '/' + dd + '/' + yy);
}
})
and in your jade template say if you wanted to add the date to a span:
span Today's date is #{dateNow()}
add the class .post-date to a tag holding the date
document.addEventListener('DOMContentLoaded', function() {
var list = document.getElementsByClassName('post-date');
// get the number of selected elements
// iterate over elements and output their HTML content
for (var i=0; i<list.length; i++){
//console.log(list[i].innerHTML);
var string=list[i].innerHTML;
var length = 15;
var trimmedString = string.substring(0, length);
list[i].innerHTML=trimmedString ;
}
})
I found a solution
1.Create a function in pug using - syntax
2.pass the varible to the function when pug.js is binding variables to the pug template
-function prettyDate(dateString){
-var date = new Date(dateString);
-var d = date.getDate();
-var monthNames = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun","Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ];
-var m = monthNames[date.getMonth()];
-var y = date.getFullYear();
-return d+' '+m+' '+y;
-}
3.span.post-date #{prettyDate(val.date)};

Resources