Getting A Month's Worth of Data With KnexJS - javascript

I'm trying to get a month's worth of data using KnexJS, I only have the year and month in the form 20xx-mm.
Here's my Knex query ..
export function getMonthlyData(yearAndMonth, startingFromDay, roomId) {
return db('events')
.where('roomId', roomId)
.whereBetween('start', [`${yearAndMonth}-${startingFromDay} 00:00:00+00`, `${yearAndMonth}-31 23:59:59+00`])
.select();
}
The issue is, I'm starting at a specific date and going through to the 31'st day, some months have 30 days, some 28 and 29. How do I go about to creating a query that helps me achieve this?
I have tried using SQL's MONTH function along with Knex's betweenRaw, but unfortunately, I have only the year and month, and the MONTH function expects a datetime.

This should solve it
export function getMonthlyData(yearAndMonth, startingFromDay, roomId) {
const startDate = `${yearAndMonth}-${startingFromDay}`;
return db('events')
.where('roomId', roomId)
.whereRaw(`start >= (TIMESTAMP '${startDate}')::DATE`)
.whereRaw(`start < date_trunc('MONTH', TIMESTAMP '${startDate}' + interval '1 month')::DATE`)
.select();
}

Related

Calculate datetime correctly in UTC from date string

I'm trying to calculate the datetime in UTC, i have the bellow code and using Luxon
weeklyDish.orderBeforeTime = timeZoneToUTC(
"Europe/Amsterdam",
year,
month,
day,
hours
);
function timeZoneToUTC(timezone, year, month, day, hours) {
const dateObj = `${year}-${month}-${day} ${hours}:00`;
const datetime = DateTime.fromFormat(dateObj, "yyyy-M-d H:mm", {
zone: timezone,
});
return datetime.toUTC().toString();
}
The code above always return the wrong hour.
How can I get the year, month, hour and return a UTC string to save in the DB?
I'm going to be migrating data that has date as string (example: "2020-12-13"), how can I convert it to UTC date and subtract days correctly?
You need to show an example to demonstrate your issue. The following shows use of Luxon's UTC and setZone methods that both seem to correctly convert a date set for "Europe/Amsterdam".
Note that the string passed to DateTime.fromISO must form a valid ISO 8601 timestamp like YYYY-MM-DDTHH.
let DateTime = luxon.DateTime;
let [tz, y, m, d, h] = ["Europe/Amsterdam", '2020', '11', '30', '12'];
let date = DateTime.fromISO(`${y}-${m}-${d}T${h}`, { zone: "Europe/Amsterdam" });
console.log(tz + '\n' + date.toString());
let dateUTC = date.setZone('UTC');
console.log('setZone to UTC\n' + dateUTC.toString());
let dateUTC2 = date.toUTC();
console.log('toUTC method\n' + dateUTC2.toString());
<script src="https://cdn.jsdelivr.net/npm/luxon#1.25.0/build/global/luxon.min.js"></script>
PS Amsterdam standard time is +1, daylight saving time is +2.
If the date is already parsed, you can use the date constructor directly. However, the constructor depends on the local timezone, luckily you can use Date.UTC instead.
The tricky part is about the timezone, which is not supported in the constructor, but it's a simple addition anyway.
So I'd wager something like so should work:
function timeZoneToUTC(timezone, year, month, day, hours) {
return new Date(Date.UTC(year, month - 1, day, hours + timezone));
}
Note: the month parameter is an index (0-based), so if you have 1=January, you need to decrease your month by one (as in my example).
Edit: uh, apparently, Date.UTC returns a timestamp, so you need to use the constructor anyway.

How to calculate time for this and next month in javascript?

I'm working on a program where I get dates like this:
2016-08-31T00:00:00
so my goal is to do 3 comparisons:
1.- Need to show "Due" if appoinment has already happended.
2.- Need to show "Due next Month" if appoinment is due next month.
3.- Need to show "Due this month" if appoinment is due this month.
So far I'm able to show message "Due" by doing this:
var someTime = "2016-08-31T00:00:00";
if(new Date(someTime).getTime() < new Date()){
console.log("Due");
}
So how can I get the "Due next Month" and "Due this month" calculations working? Thanks a lot in advance!
Why not something like this:
function appointment(srcDate) {
console.log('');
console.log(srcDate);
var today = new Date();
var todayNextMonth = today.getMonth() + 1;
todayNextMonth = todayNextMonth > 11 ? 0 : todayNextMonth;
if (srcDate < today) {
console.log("Due");
} else if (srcDate.getMonth() === today.getMonth()) {
console.log("Due this month");
} else if (srcDate.getMonth() === todayNextMonth) {
console.log("Due next month");
}
}
appointment(new Date("2016-08-31T00:00:00"));
appointment(new Date("2018-05-29T00:00:00"));
appointment(new Date("2018-06-15T00:00:00"));
See jsfiddle
Use a function, easier to test.
Keep in mind that getMonth() is zero-indexed, so januari equals 0, not 1 etc.
Not sure why you use getTime() to compare with a Date object, you can omit the getTime(). But keep in mind that if an appointment date is 5 minutes in the future, it will show 'Due this month'. You'll have to add in extra logic to show 'Due today' if you require that.
You can get the current month by doing:
const today = new Date();
const month = today.getMonth();
Technically then, you can add 1 to get the next month: const nextMonth = month + 1;
But understand that doing it like that can run into issues if the date is something like Jan 31 (which then the above will give you March as the next month).
See this question and answers for more information on that: Javascript Date: next month
Alternatively, if you're doing a lot of work with dates, you can use a library like moment.js.

How do I get my age in X days using Moment.js?

My current code is
function ageInNDays(days) {
return moment.duration(
moment('1990-10-10').diff(
moment().add(days, 'days'))
);
}
I .add(days, 'days') to today, then diff it against some date in the past. However, moment.duration doesn't exactly always return the number of calendar years that pass. It defines a year as 365 days and returns how many of those years have passed.
EDIT: I'm still looking for my age as a number of Years. Maybe if possible, something like 20 Years, 5 Months, and 10 Days format similar to how moment.duration() looks.
If my birthday is March 5th 1992, then my age should only increment when the calendar passes March 5th. My remainder age in days should only reset when the 5th of every month passes.
EDIT2: My only idea now is something like
age = moment().add(days, 'days').year() - moment('1995-01-05').year()
if ((today's date + added days) < birthday's date)
--age
From the documentation,
Trying to convert years to days makes no sense without context. It is much better to use moment#diff for calculating days or years between two moments than to use Durations.
So it looks like using diff is the answer:
function ageInNDays(days) {
return moment().add(days, 'days').diff('1990-10-10', 'years', true);
}
// ageInNDays(1000);
// 27.977483271480484
Note that gives you the fractional number of years (by virtue of the third argument). You could truncate that if you don't want it rounded (which the default implementation does):
function ageInNDays(days) {
return Math.floor(moment().add(days, 'days').diff('1990-10-10', 'years', true)) + ' years';
}
// ageInNDays(1000);
// 27 years
If you're really looking for age in days, you can use:
moment.duration(moment() - moment('1990-10-10')).asDays();
UPDATE
You can also use this to add days to your current age:
function ageInNDays(days) {
var age = moment.duration(moment() - moment('1990-10-10'));
return age.add(days, 'd').asDays();
}
Add param boolean true for decimals diferency
var currentDate = new Date();
var diff = moment(currentDate).diff('1990-10-10', 'years',true);
console.log("Year", diff);
Example
That will answer 21.23 depending on the date

Moment js getting next date given specified week day

I seem to have a bit of a problem getting the previous Monday given a particular date. I'm trying to use Moment js for the task. Obviously, I can do it by hand, but found it curious that I couldn't get it to work using the example in the moment.js documentation on their website: http://momentjs.com/docs/#/get-set/day/.
I was trying something like:
moment([2013, 08, 15, 15, 20]).day(-1).format('ddd, MMM DD')
which results in the 'two days ago' date, that being September 13 instead of the expected September 9th.
Does anybody have a clue here? Thanks.
Here is how it works:
moment().day(1) // this monday
moment().day(-6) // last monday, think of it as this monday - 7 days = 1 - 7 = -6
Same applies in other direction:
moment().day(8) // next monday, or this monday + 7 days = 1 + 7 = 8
Your code moment().day(-1) can be explained as this Sunday - 1 day = 0 - 1 = -1
or this Saturday - 7 days = 6 - 7 = -1
The accepted answer only works if you already know whether the day in question is in this week or next week. What if you don't know? You simply need the next available Thursday following some arbitrary date?
First, you want to know if the day in question is smaller or bigger than the day you want. If it's bigger, you want to use the next week. If it's smaller, you can use the same week's Monday or Thursday.
const dayINeed = 4; // for Thursday
if (moment().isoWeekday() <= dayINeed) {
return moment().isoWeekday(dayINeed);
} else...
If we're past the day we want already (if for instance, our Moment is a Friday, and we want the next available Thursday), then you want a solution that will give you "the Thursday of the week following our moment", regardless of what day our moment is, without any imperative adding/subtracting. In a nutshell, you want to first go into the next week, using moment().add(1, 'weeks'). Once you're in the following week, you can select any day of that week you want, using moment().day(1).
Together, this will give you the next available day that meets your requirements, regardless of where your initial moment sits in its week:
const dayINeed = 4; // for Thursday
// if we haven't yet passed the day of the week that I need:
if (moment().isoWeekday() <= dayINeed) {
// then just give me this week's instance of that day
return moment().isoWeekday(dayINeed);
} else {
// otherwise, give me next week's instance of that day
return moment().add(1, 'weeks').isoWeekday(dayINeed);
}
See also: https://stackoverflow.com/a/27305748/800457
function nextWeekday (day, weekday) {
const current = day.day()
const days = (7 + weekday - current) % 7
return day.clone().add(days, 'd')
}
// example: get next Friday starting from 7 Oct 2019
nextWeekday(moment('2019-10-07'), 5)) // 2019-10-11
I think the point is that using day() or isoWeekday() you get a date in the current week, no matter which day of the week is today. As a consequence, the date you get can be past, or still to come.
Example:
if today is Wednesday, moment().isoWeekday(5).format() would return the date of the upcoming Friday.
While
moment().isoWeekday(1).format() would return the previous Monday.
So when you say you want the date of, let's say, "last Tuesday", this date could belong to the current week or to the previous week, depending on which day is today.
A possible function to get the date of the last dayOfTheWeek is
function getDateOfPreviousDay(dayOfTheWeek) {
currentDayOfTheWeek = moment().isoWeekday();
if ( currentDayOfTheWeek >= dayOfTheWeek ) {
return moment().isoWeekday(dayOfTheWeek).format(); // a date in the current week
}
else {
return moment().add(-1,'weeks').isoWeekday(dayOfTheWeek).format(); // a date in the previous week
}
}
const upcomingDay = (dayIndex, format = "DD MMMM YYYY") => {
if (
Number(moment().format("D")) >= Number(moment().day(dayIndex).format("D"))
) {
return moment()
.day(7 + dayIndex)
.format(format);
}
return moment().day(dayIndex).format(format);
};

setDate() set the wrong date on 31st?

This is very weird I don't know what I'm doing wrong. I have a function to grab the date (i.e in this format: 06/24/2011), here's the function:
function checkDate(input){
var d = new Date();
var dspl = input.split("/");
if(dspl.length != 3)
return NaN;
d.setDate(dspl[1]);
d.setMonth(Number(dspl[0])-1);
if(dspl[2].length == 2)
d.setYear("20"+(dspl[2]+""));
else if(dspl[2].length == 4)
d.setYear(dspl[2]);
else
return NaN;
var dt = jsToMsDate(new Date(d));
return dt;
}
If I enter any date of the month, it would parse the date correctly, but if I enter 31st, i.e "01/31/2011", then it would turn into "01/01/2011". I'm not sure what to do and not really sure where the problem might be.
JavaScript's Date objects allow you to give invalid combinations of months and days; they automagically correct those for you (so for instance, if you set the day of the month to 31 when the month is June, it automatically makes it July 1st). That means if you set the fields individually, you can run into situations where that automagic correction gets in your way.
In your case, if you're going to set all three of those fields, you're better off using the form of the Date constructor that accepts them as arguments:
var dt = new Date(year, month, day);
(If you want hours, minutes, seconds, and milliseconds, you can add them as parameters as well.)
So looking at your code, an off-the-cuff update:
function checkDate(input){
var year, month, day, d, dt;
var dspl = input.split("/");
if(dspl.length != 3)
return NaN;
year = parseInt(dspl[2], 10);
month = parseInt(dspl[0], 10) - 1;
day = parseInt(dspl[1], 10);
if (isNaN(year) || isNaN(month) || isNaN(day)) {
return NaN;
}
if (year < 100) {
year += 2000;
}
d = new Date(year, month, day);
var dt = jsToMsDate(d);
return dt;
}
Some other notes on that update:
It's best to use parseInt to parse numbers from end users, and to always specify the radix (10 for decimal). (No, parseInt is not slower than Number or the unary + trick. People assume it is, but it isn't.)
No need to muck about with strings to add 2000 to years given with only two digits. But you can if you like. Note I weakened the validation there, allowing one-digit years for (say) 2001 and three-digit years for (say) 300 AD. So if you need it to be that strong, you'll need to readjust that.
No need to feed the date instance into new Date() again.
You need to set the month before setting the day (or as Marc B points out in his comment, use the Date(yearval, monthval, dayval) constructor).
When you create a Date object, it defaults to the current date. At the time of writing that's in June, so when you try to set the day to 31 it wraps.
...And because of similar behaviour in leap years, you should set the year before setting the month or day.
(It's a good job you developed this code in June rather than in July - the bug would have lurked undiscovered until September, and it would probably have been your users that found it rather than you. :-)
Right hierarchy is set year, then Month and at last add the Day.
This will return the exact date that you added.
function checkDate() {
//Wrong order- will return 1 May 2016
var start = new Date();
start.setDate(31);
start.setMonth(4);
start.setFullYear(2016);
alert(start)
//Right order - will return 31 May 2016
var end = new Date();
end.setFullYear(2016);
end.setMonth(4);
end.setDate(31);
alert(end)
}
<input type="button" value="Test" onclick="checkDate()" />
This is the right heirarchy to set date.
Why are you adding 1 the day position (position 1)? I think that is your problem.
d.setDate(dspl[1] + 1);

Categories