How to get number of days in month based on date? - sql

Is there a way to use extract from date in format YYYY-MM-DD how many days were in this month?
example:
for 2016-02-05 it will give 29 (Feb 2016 has 29 days)
for 2016-03-12 it will give 31
for 2015-02-05 it will give 28 (Feb 2015 had 28 days)
I'm using PostgreSQL
EDIT:
LAST_DAY function in postgres is not what i'm looking for. it returns DATE while I expect an Integer

One way to achieve this would be to subtract the beginning of the following month from the beginning of the current month:
db=> SELECT DATE_TRUNC('MONTH', '2016-02-05'::DATE + INTERVAL '1 MONTH') -
DATE_TRUNC('MONTH', '2016-02-05'::DATE);
?column?
----------
29 days
(1 row)

Just needed this today and seems that I came up with pretty much the same as Mureinik, just that I needed it numeric. (PostgreSQL couldn't convert from interval to number directly)
previous month:
select CAST(to_char(date_trunc('month', current_date) - (date_trunc('month', current_date) - interval '1 month'),'dd') as integer)
current month:
select CAST(to_char(date_trunc('month', current_date) + interval '1 month' - date_trunc('month', current_date), 'dd') as integer)

You can try next:
SELECT
DATE_PART('days',
DATE_TRUNC('month', TO_DATE('2016-02-05', 'YYYY-MM-DD'))
+ '1 MONTH'::INTERVAL
- DATE_TRUNC('month', TO_DATE('2016-02-05', 'YYYY-MM-DD'))
);
Note: there date is used twice. And used convert function TO_DATE

Related

postgresql show month start date and end date based on given dates

I need to pull out first date and last date of the month from the given from_date and to_date as input, For Example:-
I have my psql output table as the following:
Year
Term
Start Date
End Date
2022
Odd
01-02-2022
30-04-2022
2022
Even
01-07-2022
30-09-2022
I need the output as the following:-
Year
Term
Start Date
End Date
2022
Odd
01-02-2022
28-02-2022
2022
Odd
01-03-2022
31-03-2022
2022
Odd
01-04-2022
30-04-2022
2022
Even
01-07-2022
30-07-2022
2022
Even
01-08-2022
31-08-2022
2022
Even
01-09-2022
30-09-2022
I need the ouput in Postgresql, Pls help
Thanks
Your issue boils down to given a period with start and end dates, determine the first and last dates for each month in that period. In Postgres given a date you can determine the first (with date_trunc function) and last of the a month with the expressions:
-- for a given date
date_trunc('month', given_date) -- returns first day of month
date_trunc('month', given_date + interval '1 month' - interval '1 day') -- returns last day of month
Use the first expression above, with generate_series with dates, to create the first of each month in the period. The use the second expression to generate the end of each month. (see demo)
with range_dates (year, term, gsom) as
( select year
, term
, generate_series( date_trunc('month', od.start_date)::date
, date_trunc('month', od.end_date )::date
, interval '1 month'
)::date
from output_data od
)
select year
, term
, gsom start_date
, (gsom + interval '1 month' - interval '1 day')::date end_date
from range_dates
order by term desc, start_date;

Get the timestamp of the last and first day of a month in postgresSQL

I am currently trying to figure out the equivalent functions in PostgresSQL for the below MariaDB functions:
SELECT
CONCAT(DATE_ADD(DATE_ADD(LAST_DAY('2021-02-15 00:00:00'),INTERVAL 1 DAY),INTERVAL -1 MONTH), ' ', '00:00:00') AS first_day_of_the_month,
CONCAT(LAST_DAY('2021-02-15 23:59:59'), ' ' ,'23:59:59') AS last_day_of_the_month;
Expected Result:
first_day_of_the_month | last_day_of_the_month
------------------------|-------------------------------
2021-02-01 00:00:00 | 2021-02-28 23:59:59
I want to get the last day and the first day of the month of a certain date including the time 00:00:00 for the first day and 23:549:59 for the last day.
So far I am comming close to the expected result with these queries:
SELECT
(date_trunc('MONTH', '2021-02-15 00:00:00'::TIMESTAMP) + INTERVAL '1 MONTH - 1 day')::TIMESTAMP
+ '1 days'::INTERVAL - '1 months'::INTERVAL AS first_day_of_the_month,
(date_trunc('MONTH', '2021-02-15 23:59:59'::TIMESTAMP) + INTERVAL '1 MONTH - 1 day')::TIMESTAMP AS last_day_of_the_month;
However, as you can see in the DB-Fiddle for the last_day_of_the_month I am getting 00:00:00 instead of 23:59:59.
How do I need to change the query to get the correct timestamp?
demo:db<>fiddle
/* Option 1: Certain Date */
SELECT
date_trunc('month', timestamp '2021-02-15 00:00:00') AS first_timestamp,
date_trunc('month', timestamp '2021-02-15 23:59:59') + interval '1 month - 1 second' AS last_timestamp;
/* Option 2: Current_date */
SELECT
date_trunc('month', current_date)::timestamp AS first_timestamp,
date_trunc('month', current_date)::timestamp + interval '1 month - 1 second' AS last_timestamp;
First timestamp of month
date_trunc('month', ...) normalizes the date to the first possible timestamp of the month. So it returns 00:00:00 of the first day of the month
Last timestamp of month
If you add a month to the previous result, you get the first day/timestamp (00:00:00) of the next month. If you subtract a day from this, you get the last day of the current month. Of course, you can subtract just a second to get 23:59:59 of the last day of the month as you expected.
For the first day of the month:
date_trunc('month', the_date_column)
For the last day:
date_trunc('month', the_date_column) + interval '1 month' - interval '1 day'
Or if you prefer:
date_trunc('month', date) + interval '1 month - 1 day'
If you really one one second from midnight on the last day, replace day with second in the logic. I don't recommend that. In fact, if you want to define a range of date, define an open range and just use the first day of the next month. Then you can learn about tsrange which builds this into the language.

Query filtering by week

How to query (SELECT) in Postgresql, so that the results of a column with different dates, are between Sunday and Saturday of the current week.
Query fake example:
SELECT * FROM table WHERE datecolumn BETWEEN CURRENT WEEK
In another query, I have the number of the week in the year. How to make a SELECT for these dates, applying in the WHERE clause the specific week number in the specific year.
Query fake example:
SELECT * FROM table WHERE datecolumn BETWEEN WEEK15 FROM year 2020
Perhaps you can use something like this:
SELECT *
FROM table
WHERE
EXTRACT(week FROM datecolumn) = EXTRACT(week FROM NOW())
AND
EXTRACT(isoyear FROM datecolumn) = EXTRACT(isoyear FROM NOW())
The week is ISO-8601 week number. By definition, ISO weeks start on Mondays and the first week of a year contains January 4 of that year. In other words, the first Thursday of a year is in week 1 of that year.
In the ISO week-numbering system, it is possible for early-January dates to be part of the 52nd or 53rd week of the previous year, and for late-December dates to be part of the first week of the next year.
For example, 2005-01-01 is part of the 53rd week of year 2004, and 2006-01-01 is part of the 52nd week of year 2005, while 2012-12-31 is part of the first week of 2013.
It's recommended to use the isoyear field together with week to get consistent results.
If you need custom (non-ISO) week numbering - you will have to craft your own calculation.
I would recommend the following pair of conditions:
where
date_column >= current_date - extract(dow from current_date) * interval '1 day'
and date_column < current_date - (extract(dow from current_date) - 8) * interval '1 day'
Postgres' date_trunc(week, ...) starts weeks on Monday, so we need something a little more complicated, using extract(dow from ...), which returns 0 on Sundays.
The advantage of this approach is that it is SARGeable, since no function is applied to the column being filtered. This means that this would happily take advantage of an index on the date column.
I would use date_trunc(), but like this. For the current week:
where datecolumn >= date_trunc('week', now()) and
datecolumn < date_trunc('week', now()) + interval '1 week'
For the nth week of the year, this is trickier. I think this does what you want:
where datecolumn >= (date_trunc('week', now()) -
(extract(week from now()) - 1) * interval '1 week' +
<n> * interval '1 week'
) and
datecolumn < (date_trunc('week', now()) -
(extract(week from now()) - 1) * interval '1 week' +
(<n> + 1) * interval '1 week'
)
Both of these are structured so the computations are NOT on the columns, so they are compatible with using indexes.

Generate series of week intervals for given month

In a Postgres 9.1 database, I am trying to generate a series of weeks for a given month but with some constraints. I need all weeks to start on Monday and get cut when they start or end in another month.
Example:
For February, 2013 I want to generate a series like this:
start
------------------------
2013-02-01 00:00:00+00
2013-02-04 00:00:00+00
2013-02-11 00:00:00+00
2013-02-18 00:00:00+00
2013-02-25 00:00:00+00
The query that I have now looks like this:
SELECT GREATEST(date_trunc('week', dates.d),
date_trunc('month',dates.d)) as start
FROM generate_series(to_timestamp(1359676800),to_timestamp(1362095999), '1 week') as dates(d)
This query gets me the first 4 weeks but it's missing the week from the 25th. Is it possible to get the last week?
SELECT generate_series(date_trunc('week', date '2013-02-01' + interval '6 days')
, date_trunc('week', date '2013-02-01' + interval '1 month - 1 day')
, interval '1 week')::date AS day
UNION SELECT date '2013-02-01'
ORDER BY 1;
This variant does not need a subselect, GREATEST or GROUP BY and only generates the required rows. Simpler, faster. It's cheaper to UNION one row.
Add 6 days to the first day of the month before date_trunc('week', ...) to compute the first Monday of the month.
Add 1 month and subtract 1 day before date_trunc('week', ...) to get the last Monday of the month.
This can conveniently be stuffed into a single interval expression: '1 month - 1 day'
UNION (not UNION ALL) the first day of the month to add it unless it's already included as Monday.
Note that date + interval results in timestamp, which is the optimum here. Detailed explanation:
Generating time series between two dates in PostgreSQL
Automation
You can provide the start of the date series in a CTE:
WITH t(d) AS (SELECT date '2013-02-01') -- enter 1st of month once
SELECT generate_series(date_trunc('week', d + interval '6 days')
, date_trunc('week', d + interval '1 month - 1 day')
, interval '1 week')::date AS day
FROM t
UNION SELECT d FROM t
ORDER BY 1;
Or wrap it into a simple SQL function for convenience with repeated calls:
CREATE OR REPLACE FUNCTION f_week_starts_this_month(date)
RETURNS SETOF date AS
$func$
SELECT generate_series(date_trunc('week', $1 + interval '6 days')
, date_trunc('week', $1 + interval '1 month - 1 day')
, interval '1 week')::date AS day
UNION
SELECT $1
ORDER BY 1
$func$ LANGUAGE sql IMMUTABLE;
Call:
SELECT * FROM f_week_starts_this_month('2013-02-01');
You would pass the date for the first day of the month, but it works for any date. You the first day and all Mondays for the following month.
select
greatest(date_trunc('week', dates.d), date_trunc('month',dates.d)) as start
from generate_series('2013-02-01'::date, '2013-02-28', '1 day') as dates(d)
group by 1
order by 1

How do I determine the last day of the previous month using PostgreSQL?

I need to query a PostgreSQL database to determine records that fall within today's date and the last day of the previous month. In other words, I'd like to retrieve everything that falls between December 31, 2011 and today. This query will be re-used each month, so next month, the query will be based upon the current date and January 31, 2012.
I've seen this option, but I'd prefer to avoid using a function (if possible).
Both solutions include the last day of the previous month and also include all of "today".
For a date column:
SELECT *
FROM tbl
WHERE my_date BETWEEN date_trunc('month', now())::date - 1
AND now()::date
You can subtract plain integer values from a date (but not from a timestamp) to subtract days. This is the simplest and fastest way.
For a timestamp column:
SELECT *
FROM tbl
WHERE my_timestamp >= date_trunc('month', now()) - interval '1 day'
AND my_timestamp < date_trunc('day' , now()) + interval '1 day'
I use the < operator for the second condition to get precise results (read: "before tomorrow").
I do not cast to date in the second query. Instead I add an interval '1 day', to avoid casting back and forth.
Have a look at date / time types and functions in the manual.
For getting date of previous/last month:
SELECT (date_trunc('month', now())::date - 1) as last_month_date
Result: 2012-11-30
For getting number of days of previous/last month:
SELECT DATE_PART('days', date_trunc('month', now())::date - 1) last_month_days
Result: 30
Try this:
SELECT ...
WHERE date_field between (date_trunc('MONTH', now()) - INTERVAL '1 day')::date
and now()::date
...
Try
select current_date - cast((date_part('day', current_date) + 1) as int)
take from http://wiki.postgresql.org/wiki/Date_LastDay, and modified to return just the days in a month
CREATE OR REPLACE FUNCTION calc_days_in_month(date)
RETURNS double precision AS
$$
SELECT EXTRACT(DAY FROM (date_trunc('MONTH', $1) + INTERVAL '1 MONTH - 1 day')::date);
$$ LANGUAGE 'sql' IMMUTABLE STRICT;
select calc_days_in_month('1999-05-01')
returns 31
Reference is taken from this blog:
You can use below function:
CREATE OR REPLACE FUNCTION fn_GetLastDayOfMonth(DATE)
RETURNS DATE AS
$$
SELECT (date_trunc('MONTH', $1) + INTERVAL '1 MONTH - 1 day')::DATE;
$$ LANGUAGE 'sql'
IMMUTABLE STRICT;
Sample executions:
SELECT *FROM fn_GetLastDayOfMonth(NOW()::DATE);