问题
The Postgres database claims it supports the ISO-8601 standard. In ISO-8601 a date format "yyyy", i.e. consisting of only the year, is fine and acceptable. But I can't find a way to add only a year to a Postgres database field of the type "date". Any idea if I'm doing something wrong or is this feature missing in Postgres?
I've seen other posts advising to set the date to "yyyy-01-01" but that is not what I want and need (since it marks a specific day of a month of a year).
Scenario
The scenario is the following. We are collecting information on people. For many we do have exact dates. But some have no dates or only years, or year and month but no day. We do have to be able to find people born before some year, or after some other year. This is easy if you have a full date. I hoped there would be some feature implemented in Postgres that would handle cases of incomplete dates.
回答1:
To get the year of a date
data type:
select extract(year from '2014-01-01'::date) as the_year;
the_year
----------
2014
If you only need the year then use a smallint with a check constraint
create table t (
the_year smallint check(
the_year between 0 and extract(year from current_date)
)
);
insert into t (the_year) values (2030);
ERROR: new row for relation "t" violates check constraint "t_the_year_check"
DETAIL: Failing row contains (2030).
insert into t (the_year) values (2014);
INSERT 0 1
But if you will store the whole date then it makes no sense to separate into 3 columns.
Note that the semantics of the column are up to the application. If a column is of the date
type but the application only considers the year then that column means the year.
Check the Date/Time Functions and Operators
One solution to the partial date problem pointed by @a_horse in the comments is to create a column indicating the precision of that date
create table t (
the_date date,
the_date_precision varchar(5)
);
insert into t (the_date, the_date_precision) values
(current_date, 'year'),
(current_date, 'month'),
(current_date, 'day')
;
select
case the_date_precision
when 'year' then to_char(the_date, 'YYYY')
when 'month' then to_char(the_date, 'YYYY-MM')
else to_char(the_date, 'YYYY-MM-DD')
end as the_date
from t
;
the_date
------------
2014
2014-02
2014-02-06
The above is the KISS aproach but I think the next implementation is more elegant
create table t (
the_date date,
the_date_precision smallint
);
insert into t (the_date, the_date_precision) values
(current_date, 1),
(current_date, 2),
(current_date, 3)
;
select
array_to_string(
(
string_to_array(to_char(the_date, 'YYYY-MM-DD'), '-')
)[1:the_date_precision]
, '-'
) as the_date
from t
;
the_date
------------
2014
2014-02
2014-02-06
That select expression could be turned into a function to be easier to reproduce. Or just a view
create view view_t as
select *,
array_to_string(
(
string_to_array(to_char(the_date, 'YYYY-MM-DD'), '-')
)[1:the_date_precision]
, '-'
) as the_date_output
from t
;
select * from view_t;
the_date | the_date_precision | the_date_output
------------+--------------------+-----------------
2014-02-06 | 1 | 2014
2014-02-06 | 2 | 2014-02
2014-02-06 | 3 | 2014-02-06
来源:https://stackoverflow.com/questions/21599997/iso-8601-in-postgres-how-to-insert-only-year-in-type-date-incomplete-date-tim