2

There is some option to store only hours and minutes in the database (No seconds)?

I know that I can use the function to_char in my query, but this is not the solution for me.

I am using/editing a system written in PHP that I cannot edit (I mean PHP), it is something like WYSIWYG, and it injects the values ​​directly into the forms (Talking about editing existing), and the form should be in HH:MM and as long as the injection does not cause an application error , saving (Even without editing) causes an error due to validation because it incject HH:MM:SS because that is the column type.

So far, to work around the problem, I have created text columns, but it creates other complications and I was wondering if it could be done in a different way

LordF
  • 407
  • 5
  • 18
  • Why not just store regular date/time and then truncate off seconds when you need to report? – Tim Biegeleisen Nov 24 '21 at 07:38
  • I edited the post and added an explanation of why. – LordF Nov 24 '21 at 07:44
  • That explanation is too vague, and why can't you edit PHP code? – Laurenz Albe Nov 24 '21 at 07:55
  • Okay, so brighter. My client purchased a system which is written in PHP. Unfortunately it's coded (Probably by Zend Guard) and I can't fully edit it. As part of this system, functionalities have been added that allow you to add new functionalities using WYSIWYG and my own PHP code. – LordF Nov 24 '21 at 07:59
  • No, unfortunately I can't. There are forms to add/edit buisness objects created by WYSIWYG and i cannot change the code of it (PHP) because is encoded. And that code incject directly value from Database, so on editing is always HH:MM:SS, and then i cannot even save the object because of validation wants HH:MM. I cannot discribe it brighter and better – LordF Nov 24 '21 at 08:07
  • But i cannot use a query because i cannot change the php of form to load data from existing object so i can't use CAST or TO_CHAR() or even SELECT – LordF Nov 24 '21 at 08:09
  • @FrankHeikens Yep it's right that 11:00 it's correct for DB, so it puts 11:00:00, but then if i edit this row (In GUI), it put's 11:00:00 to form. And then I cannot save because system have internal mechanizm of validation that i cannot change and requires HH:MM – LordF Nov 24 '21 at 08:10
  • With all due respect, Frank, your comments are neither innovative nor helping me. You're basically trying to tell me that I have no problem and that I have to edit a PHP code bin that has been encoded with some Zend Guard or something similar – LordF Nov 24 '21 at 08:13
  • 1
    To clarify: column on the db is of type [time](https://www.postgresql.org/docs/14/datatype-datetime.html#:~:text=time%20%5B%20(p)%20%5D%20%5B%20without%20time%20zone%20%5D) and the field in your app is in format HH:MM. When you edit the field in your app, it first pulls the current value in its default format HH:MM:SS from the db, without validating it. Then if you don't manually truncate it back to HH:MM, the entry fails in-app validation (it now sees HH:MM:SS while expecting only HH:MM), even though the database would accept either format. – Zegarek Nov 24 '21 at 09:38
  • @Zegarek Exactly, so I would need the format in the database to be HH: MM, or some other mechanism that will result in the return of data in the form of HH: MM directly from the database (probably there is PDO or pg_xxx () functions), but unfortunately not I have an option to change this query so I can't do plain TO_CHAR or any other postgress method. – LordF Nov 24 '21 at 10:00
  • 1
    @LordF sounds like you're doomed to use strange workarounds. [It's possible to switch the default time output format](https://stackoverflow.com/a/8724846/5298879) but the predefined options [don't include a plain HH:MM](https://www.postgresql.org/docs/14/datatype-datetime.html#DATATYPE-DATETIME-OUTPUT). If your table doesn't have many dependencies and the app's parser won't protest seeing a different type, you could alter the table to change the name of the time column, then add a new column using the old name and setting it to `to_char(old_column_name,'HH:MM')` to trick selects into using it. – Zegarek Nov 24 '21 at 10:24
  • 1
    But that assumes there are no `select *` from that table anywhere in the app, which this would break, and that the app does not check the type, only parses or even just passes that incoming value. Preexisting `insert` statements without target columns specified should work as long as the new column is the last one. You could also compile your own, modded version of PostgreSQL to include a default time output format option of your choice (which would also likely break something else). – Zegarek Nov 24 '21 at 10:30
  • Yes, wild workarounds are something I'm used to. Your suggestions are really interesting. While the second one is doomed to failure (because there are also fields that have the HH: MM: SS format in other formulas) – LordF Nov 24 '21 at 11:23
  • The first one actually solves the problem, but it seems to me to be terribly unstable, and the two causes another problem, which I want to escape. Until now, my columns were of the text type and from this point of view of the application it was ok, but it caused other problems so this solution is a return to starting point, but in a more convoluted way. Thank you nevertheless. – LordF Nov 24 '21 at 11:24
  • I'll try to get around it in a different way; with each application request a config.php file is loaded, which is not encrypted, maybe I will be able to inject some PHP code there that will change the data before writing to the database, but this is also complicated, because $ _POST and $ _GET are terribly complex and unclear. Ehh :( – LordF Nov 24 '21 at 11:24

1 Answers1

1

Assuming that

  1. You can't modify your PHP application.
  2. You wish to keep a column of type time for other purposes.
  3. There are no select * anywhere that expect the column layout of that table to remain unchanged.
  4. You only need this one piece of the application to select, insert and update that table as if HH:MM was its default output format.
  5. The application won't mind that it'll read text instead of time type data, as long as it's HH:MM.

You can do a column swap and handle incoming data in a trigger, also maintaining integrity between the actual time-type column and its text representation column. That way anything that doesn't rely on the data being type `time will get HH:MM format from that particular table.

drop table if exists test_70092210;
create table test_70092210 as select now()::time time_column;
select time_column from test_70092210;
--   time_column
-------------------
-- 12:06:23.890971
--(1 row)
alter table test_70092210 rename column time_column to time_column_raw;--column swap
alter table test_70092210 add column time_column text;--column swap
update test_70092210 set time_column=to_char(time_column_raw,'HH24:MI');
select time_column from test_70092210;
-- time_column
---------------
-- 12:06
--(1 row)

While updates and inserts can be handled using triggers:

CREATE or replace FUNCTION test_70092210_time_column_insert_handler() RETURNS trigger AS $test_70092210_time_column_insert_handler$
    BEGIN
        NEW.time_column_raw=coalesce(   NEW.time_column_raw::time,
                                        NEW.time_column::time);
        NEW.time_column=coalesce(   to_char(NEW.time_column_raw::time,'HH24:MI'),
                                    to_char(NEW.time_column::time,'HH24:MI'));
        RETURN NEW;
    END;
$test_70092210_time_column_insert_handler$ LANGUAGE plpgsql;
create or replace trigger test_70092210_time_column_insert_handler_trigger before insert on test_70092210
for each row execute function test_70092210_time_column_insert_handler();

CREATE or replace FUNCTION test_70092210_time_column_update_handler() RETURNS trigger AS $test_70092210_time_column_update_handler$
    BEGIN
        NEW.time_column_raw=case 
                                when NEW.time_column_raw<>OLD.time_column_raw
                                    then NEW.time_column_raw::time
                                else    
                                    NEW.time_column::time
                            end;
        NEW.time_column=case 
                            when NEW.time_column_raw<>OLD.time_column_raw
                                then to_char(NEW.time_column_raw::time,'HH24:MI')
                            else
                                to_char(NEW.time_column::time,'HH24:MI')
                        end;
        RETURN NEW;
    END;
$test_70092210_time_column_update_handler$ LANGUAGE plpgsql;
create or replace trigger test_70092210_time_column_update_handler_trigger before update on test_70092210
for each row execute function test_70092210_time_column_update_handler();

insert into test_70092210 select now()-'01:30'::time;
select time_column from test_70092210;
-- time_column
---------------
-- 12:06
-- 10:37
--(2 rows)
update test_70092210 set time_column='16:23' where ctid in (select min(ctid) from test_70092210);
select time_column from test_70092210;
-- time_column
---------------
-- 10:37
-- 16:23
--(2 rows)

Having the cookie and eating it too:

select * from test_70092210;
-- time_column_raw | time_column
-------------------+-------------
-- 10:37:19.91891  | 10:37
-- 16:23:00        | 16:23
--(2 rows)
Zegarek
  • 6,424
  • 1
  • 13
  • 24
  • I forgot about triggers, actually it will be a workaround, even some other problems I can work around with their help, thanks. – LordF Nov 25 '21 at 09:59