]> granicus.if.org Git - postgresql/commitdiff
Add make_date() and make_time() functions.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 17 Nov 2013 20:06:50 +0000 (15:06 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 17 Nov 2013 20:06:50 +0000 (15:06 -0500)
Pavel Stehule, reviewed by Jeevan Chalke and Atri Sharma

doc/src/sgml/func.sgml
src/backend/utils/adt/date.c
src/backend/utils/adt/datetime.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/utils/date.h
src/include/utils/datetime.h
src/test/regress/expected/date.out
src/test/regress/sql/date.sql

index 89f08aff979647bf352301c77fe7eb5fb62d17ed..a5c808effae322ea17e8537055ec870fe84bc4f6 100644 (file)
@@ -6690,6 +6690,48 @@ SELECT SUBSTRING('XY1234Z', 'Y*?([0-9]{1,3})');
         <entry></entry>
        </row>
 
+       <row>
+        <entry>
+         <indexterm>
+          <primary>make_date</primary>
+         </indexterm>
+         <literal>
+            <function>
+             make_date(<parameter>year</parameter> <type>int</type>,
+             <parameter>month</parameter> <type>int</type>,
+             <parameter>day</parameter> <type>int</type>)
+            </function>
+         </literal>
+        </entry>
+        <entry><type>date</type></entry>
+        <entry>
+         Create date from year, month and day fields
+        </entry>
+        <entry><literal>make_date(2013, 7, 15)</literal></entry>
+        <entry><literal>2013-07-15</literal></entry>
+       </row>
+
+       <row>
+        <entry>
+         <indexterm>
+          <primary>make_time</primary>
+         </indexterm>
+         <literal>
+          <function>
+           make_time(<parameter>hour</parameter> <type>int</type>,
+           <parameter>min</parameter> <type>int</type>,
+           <parameter>sec</parameter> <type>double precision</type>)
+          </function>
+         </literal>
+        </entry>
+        <entry><type>time</type></entry>
+        <entry>
+         Create time from hour, minute and seconds fields
+        </entry>
+        <entry><literal>make_time(8, 15, 23.5)</literal></entry>
+        <entry><literal>08:15:23.5</literal></entry>
+       </row>
+
        <row>
         <entry>
          <indexterm>
index 8677520cb6f577e79593a95423591771818fcec0..fe091daec8178e5142ed492a910aceadd36b6b2f 100644 (file)
@@ -235,6 +235,43 @@ date_send(PG_FUNCTION_ARGS)
        PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 }
 
+/*
+ *             make_date                       - date constructor
+ */
+Datum
+make_date(PG_FUNCTION_ARGS)
+{
+       struct pg_tm tm;
+       DateADT         date;
+       int                     dterr;
+
+       tm.tm_year = PG_GETARG_INT32(0);
+       tm.tm_mon = PG_GETARG_INT32(1);
+       tm.tm_mday = PG_GETARG_INT32(2);
+
+       /*
+        * Note: we'll reject zero or negative year values.  Perhaps negatives
+        * should be allowed to represent BC years?
+        */
+       dterr = ValidateDate(DTK_DATE_M, false, false, false, &tm);
+
+       if (dterr != 0)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
+                                errmsg("date field value out of range: %d-%02d-%02d",
+                                               tm.tm_year, tm.tm_mon, tm.tm_mday)));
+
+       if (!IS_VALID_JULIAN(tm.tm_year, tm.tm_mon, tm.tm_mday))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+                                errmsg("date out of range: %d-%02d-%02d",
+                                               tm.tm_year, tm.tm_mon, tm.tm_mday)));
+
+       date = date2j(tm.tm_year, tm.tm_mon, tm.tm_mday) - POSTGRES_EPOCH_JDATE;
+
+       PG_RETURN_DATEADT(date);
+}
+
 /*
  * Convert reserved date values to string.
  */
@@ -1208,6 +1245,39 @@ timetypmodout(PG_FUNCTION_ARGS)
        PG_RETURN_CSTRING(anytime_typmodout(false, typmod));
 }
 
+/*
+ *             make_time                       - time constructor
+ */
+Datum
+make_time(PG_FUNCTION_ARGS)
+{
+       int                     tm_hour = PG_GETARG_INT32(0);
+       int                     tm_min = PG_GETARG_INT32(1);
+       double          sec = PG_GETARG_FLOAT8(2);
+       TimeADT         time;
+
+       /* This should match the checks in DecodeTimeOnly */
+       if (tm_hour < 0 || tm_min < 0 || tm_min > MINS_PER_HOUR - 1 ||
+               sec < 0 || sec > SECS_PER_MINUTE ||
+               tm_hour > HOURS_PER_DAY ||
+       /* test for > 24:00:00 */
+               (tm_hour == HOURS_PER_DAY && (tm_min > 0 || sec > 0)))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATETIME_FIELD_OVERFLOW),
+                                errmsg("time field value out of range: %d:%02d:%02g",
+                                               tm_hour, tm_min, sec)));
+
+       /* This should match tm2time */
+#ifdef HAVE_INT64_TIMESTAMP
+       time = (((tm_hour * MINS_PER_HOUR + tm_min) * SECS_PER_MINUTE)
+                       * USECS_PER_SEC) + rint(sec * USECS_PER_SEC);
+#else
+       time = ((tm_hour * MINS_PER_HOUR + tm_min) * SECS_PER_MINUTE) + sec;
+#endif
+
+       PG_RETURN_TIMEADT(time);
+}
+
 
 /* time_transform()
  * Flatten calls to time_scale() and timetz_scale() that solely represent
index 1b8f109992a191e0f8ea58bd335833dadcaa5745..1c8291c8c59525878e626c6106e06cf323786301 100644 (file)
@@ -44,8 +44,6 @@ static int    DecodeTimezone(char *str, int *tzp);
 static const datetkn *datebsearch(const char *key, const datetkn *base, int nel);
 static int DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
                   struct pg_tm * tm);
-static int ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc,
-                        struct pg_tm * tm);
 static void TrimTrailingZeros(char *str);
 static void AppendSeconds(char *cp, int sec, fsec_t fsec,
                          int precision, bool fillzeros);
@@ -2270,7 +2268,7 @@ DecodeDate(char *str, int fmask, int *tmask, bool *is2digits,
  * Check valid year/month/day values, handle BC and DOY cases
  * Return 0 if okay, a DTERR code if not.
  */
-static int
+int
 ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc,
                         struct pg_tm * tm)
 {
index b0286157f711f5fd23d9020ec5a6479830a75930..2d80cc30b7eccbbfefe2fd64081c0418796e88be 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201311162
+#define CATALOG_VERSION_NO     201311171
 
 #endif
index c5d5d291558e23c9b1b6768f69810d2238f48709..6da4a50efe4a9bb74f962d2276b13dfdb930f7b9 100644 (file)
@@ -4673,6 +4673,12 @@ DESCR("int8range constructor");
 DATA(insert OID = 3946 (  int8range PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 3926 "20 20 25" _null_ _null_ _null_ _null_ range_constructor3 _null_ _null_ _null_ ));
 DESCR("int8range constructor");
 
+/* date, time constructors */
+DATA(insert OID = 3846 ( make_date     PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 1082 "23 23 23" _null_ _null_ "{year,month,day}" _null_ make_date _null_ _null_ _null_ ));
+DESCR("construct date");
+DATA(insert OID = 3847 ( make_time     PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 1083 "23 23 701" _null_ _null_ "{hour,min,sec}" _null_ make_time _null_ _null_ _null_ ));
+DESCR("construct time");
+
 /* spgist support functions */
 DATA(insert OID = 4001 (  spggettuple     PGNSP PGUID 12 1 0 0 0 f f f f t f v 2 0 16 "2281 2281" _null_ _null_ _null_ _null_  spggettuple _null_ _null_ _null_ ));
 DESCR("spgist(internal)");
index 7c3a1bec23d97a8451022f80e459fca62f9bf78a..83a5beacf0f81548f1c5ce622d14de17b78b5e84 100644 (file)
@@ -97,6 +97,7 @@ extern Datum date_in(PG_FUNCTION_ARGS);
 extern Datum date_out(PG_FUNCTION_ARGS);
 extern Datum date_recv(PG_FUNCTION_ARGS);
 extern Datum date_send(PG_FUNCTION_ARGS);
+extern Datum make_date(PG_FUNCTION_ARGS);
 extern Datum date_eq(PG_FUNCTION_ARGS);
 extern Datum date_ne(PG_FUNCTION_ARGS);
 extern Datum date_lt(PG_FUNCTION_ARGS);
@@ -154,6 +155,7 @@ extern Datum time_recv(PG_FUNCTION_ARGS);
 extern Datum time_send(PG_FUNCTION_ARGS);
 extern Datum timetypmodin(PG_FUNCTION_ARGS);
 extern Datum timetypmodout(PG_FUNCTION_ARGS);
+extern Datum make_time(PG_FUNCTION_ARGS);
 extern Datum time_transform(PG_FUNCTION_ARGS);
 extern Datum time_scale(PG_FUNCTION_ARGS);
 extern Datum time_eq(PG_FUNCTION_ARGS);
index 3cd921a0dbb098bbf3deb29f9b56cf973332f198..4e59e445ee59f1053b8116c6665cab8f1fb8d278 100644 (file)
@@ -294,6 +294,9 @@ extern void EncodeTimeOnly(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz
 extern void EncodeDateTime(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str);
 extern void EncodeInterval(struct pg_tm * tm, fsec_t fsec, int style, char *str);
 
+extern int ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc,
+                        struct pg_tm * tm);
+
 extern int     DecodeSpecial(int field, char *lowtoken, int *val);
 extern int     DecodeUnits(int field, char *lowtoken, int *val);
 
index b603745077ce3e30e35480eb9114e7c7e1db1e2c..8923f6090abf9df3236b0f95b720ed5c26179445 100644 (file)
@@ -1184,3 +1184,29 @@ select isfinite('infinity'::date), isfinite('-infinity'::date), isfinite('today'
  f        | f        | t
 (1 row)
 
+-- test constructors
+select make_date(2013, 7, 15);
+ make_date  
+------------
+ 07-15-2013
+(1 row)
+
+select make_time(8, 20, 0.0);
+ make_time 
+-----------
+ 08:20:00
+(1 row)
+
+-- should fail
+select make_date(2013, 2, 30);
+ERROR:  date field value out of range: 2013-02-30
+select make_date(2013, 13, 1);
+ERROR:  date field value out of range: 2013-13-01
+select make_date(2013, 11, -1);
+ERROR:  date field value out of range: 2013-11--1
+select make_date(-44, 3, 15);  -- perhaps we should allow this sometime?
+ERROR:  date field value out of range: -44-03-15
+select make_time(10, 55, 100.1);
+ERROR:  time field value out of range: 10:55:100.1
+select make_time(24, 0, 2.1);
+ERROR:  time field value out of range: 24:00:2.1
index d179ddf09b3c41adb2c20955f5a87ac7b37e40a4..a62e92a77ef6297356ce793d6a903f38161ee81b 100644 (file)
@@ -276,3 +276,14 @@ select 'infinity'::date, '-infinity'::date;
 select 'infinity'::date > 'today'::date as t;
 select '-infinity'::date < 'today'::date as t;
 select isfinite('infinity'::date), isfinite('-infinity'::date), isfinite('today'::date);
+
+-- test constructors
+select make_date(2013, 7, 15);
+select make_time(8, 20, 0.0);
+-- should fail
+select make_date(2013, 2, 30);
+select make_date(2013, 13, 1);
+select make_date(2013, 11, -1);
+select make_date(-44, 3, 15);  -- perhaps we should allow this sometime?
+select make_time(10, 55, 100.1);
+select make_time(24, 0, 2.1);