I have to work with a postgres database 9.4 and i want to work with timestamp.
The first 'problem' is that when i create a timestamp column on postgres, i don't know what it does internally but when i query it returns '2010-10-30 00:00:00'
For me a timestamp is something like this 12569537329 (unix timestamp).
I say that because is a integer or a float, it's way easier for computer to deal comparing to string, and each country has his own time format, with unix timestamp is a number and end of story.
Querying from php the result is a string, so i have to make a bunch juggling and because of time zone, day light saving and other things something might could gone wrong.
I searched a lot of and can't find a way to work with unix timestamp on postgresql.
Can someone explain if there a way, or the right way to work and get as close as possible to unix timestamp.
UPDATE
One thing that i found that it gonna help me and it take a long time to discover that is possible on postgresql is change the Interval Output.
pg manual
In php the date interval for month is 'month' for pg is 'mon' on php it will understand mon as monday.
I think that if you have to juggle too much you are doing it wrong.
Gladly postgres let us to change that behavior for session or permanently.
So setting intervalstyle to iso_8601 it will work as php iso_8601 and will output P1M
Just convert the unix time_t to/from a timestamp, and use that in postgres:
CREATE TABLE omg
( seq SERIAL NOT NULL PRIMARY KEY
, stampthing TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO omg(stampthing) SELECT now();
INSERT INTO omg(stampthing) VALUES
('epoch'::timestamptz )
, ('epoch'::timestamptz + 12569537329 * '1 second'::interval)
;
SELECT stampthing
, DATE_PART('epoch',stampthing) AS original
FROM omg;
Output:
CREATE TABLE
INSERT 0 1
INSERT 0 2
stampthing | original
-------------------------------+------------------
2016-01-20 16:08:12.646172+01 | 1453302492.64617
1970-01-01 01:00:00+01 | 0
2368-04-24 20:08:49+02 | 12569537329
(3 rows)
If you just query a timestamp column in postgres, you'll get a formatted date. If you prefer the unix timestamp integer you can either cast it when you return it using a syntax like
select extract(epoch from '2011-11-15 00:00:00+01'::timestamp with time zone)::integer;
If you do this a lot, you may make a view with the extract.
or you can define your timestamp column as an integer and store your data using the extract()
Related
I have a mysql table that has a column with date type.
I'm going to store non-Gregorian date in this column (i.e. Jalali date).
I tested this by using phpMyadmin and storing a Jalali date and no error happend.
Is it a good idea to store non-Gregorian dates in date type?
If not; which is better? storing it as varchar or as timestamp or timestamp as Int or something else?
Is it a good idea to store non-Gregorian dates in date type?
No. Aside that some valid date in one calendar system doesn't exist in another calendar, functions working on DATE typed columns may not work properly. The matter is not just storing data, you need to process this data and for example compare them with CURDATE().
storing it as varchar or as timestamp or timestamp as Int or something else?
If you choose a proper formatting, use two digits for month and day and static number of digits for year, a character string type, CHAR or VARCHAR is fine. Comparing theme against each other is just a lexical comparison and you still can write your functions o procedures to extend functionality.
Choosing TIMESTAMP or DATE changes the question as former represents a specific time but latter represents a specific entry in calendar. If you want put time beside date they still differ in meaning. You should think about issues like daylight-saving time changes which cause some people prefer to put calendar entry (DATE) and some prefer seconds passed from 1 Jan 1970 (TIMESTAMP). e.g. there is two timestamps for 1393-06-30 23:30:00 in Hijri Shamsi calendar based on current Iran government laws.
You can store non-gregorian dates in an integer field in the database.
Examples:
year:1396, month:11, day:17 => 13961117
year:1393, month:4, day:9 => 13930409
by using this, you can query rows to find a specific date and dates that are <=> than a specific date, but unfortunately, you can't compare them against each other.
Internal storage and binary interface of almost all database systems have nothing to do with calendars. you just store date (and possibly time) into database, providing no calendaring information. It's only a simple number of days, seconds or milliseconds past from a specific point in time (usually midnight 1970-01-01).
So you just need to provide an API abstraction of dates in your application layer. Everything else will work. You convert all your dates to Gregorian or Unix timestamp, and send queries to MySQL as usual.
Non Gregorian calendar still operate by Year, Month, Day and Time so it can be stored in 4 separate columns like:
CREATE TABLE `Calendar` (
`Year` int,
`Month` tinyint,
`Day` tinyint,
`Time` time
);
I make possible to store dates without conversion and permit group by Year and Month
select unix_timestamp('2038-01-19') returns 2147472000
while select unix_timestamp('2038-01-20') returns 0
I have checked out the year 2038 problem.
My linux OS is 64 bit and installed mysql version is also 64 bits. What is the solution to this problem now?
mysql --version returns mysql Ver 14.14 Distrib 5.5.47, for Linux (x86_64) using readline 5.1
Php is 64 bit too.
Tried BigInt too, didn't work (returns the same thing).
Simply put, for MySQL, store dates as DATETIME rather than TIMESTAMP.
TIMESTAMP is 4 bytes, so there is no physical room to store more seconds than since 1970-1-1 to 2038-01-19...
DATETIME, instead, has a range of 1000-1-1 to 9999-12-31...
See also this complete question/answer: PHP & mySQL: Year 2038 Bug: What is it? How to solve it?
UPDATE:
One possible alternative I see, if you CAN'T change your fields types, is to interpet your timestamps differently...
I mean: if the first event your application will keep track of is - say - 2000-1-1, you could implement a filter on backend (or in a stored procedure inside the database), to add (2000-1-1 - 1970-1-1) seconds to your timestamps when reading, and subtract the same amount when reading... This should give you 30 more years of 'survival'...
MySQL documentation is in general extremely vague about the data types returned by functions and UNIX_TIMESTAMP() is not an exception. Unless we check the source code I think we can only make an educated guess.
At Date and Time Type Overview we can read that the TIMESTAMP data type itself has a documented range that doesn't depend on the server architecture:
The range is '1970-01-01 00:00:01.000000' UTC to
'2038-01-19 03:14:07.999999' UTC. TIMESTAMP values are stored as the
number of seconds since the epoch ('1970-01-01 00:00:00' UTC). A
TIMESTAMP cannot represent the value '1970-01-01 00:00:00' because
that is equivalent to 0 seconds from the epoch and the value 0 is
reserved for representing '0000-00-00 00:00:00', the “zero” TIMESTAMP
value.
Even if we make sure we pass a proper date type:
mysql> select
-> str_to_date('2038-01-20', '%Y-%m-%d'),
-> unix_timestamp(str_to_date('2038-01-20', '%Y-%m-%d'));
+---------------------------------------+-------------------------------------------------------+
| str_to_date('2038-01-20', '%Y-%m-%d') | unix_timestamp(str_to_date('2038-01-20', '%Y-%m-%d')) |
+---------------------------------------+-------------------------------------------------------+
| 2038-01-20 | 0 |
+---------------------------------------+-------------------------------------------------------+
1 row in set (0.01 sec)
... we still get 0, the silly function flag for errors:
If you pass an out-of-range date to UNIX_TIMESTAMP(), it returns 0.
So it's kind of safe to assume that UNIX_TIMESTAMP() returns a value of TIMESTAMP type thus 2038+ is not supported.
In short: you'll have to calculate timestamps somewhere else (i.e., your client code). Since there's a PHP tag:
$t = new DateTime('2038-01-20', new DateTimeZone('UTC'));
var_dump( $t->format('U') );
string(10) "2147558400"
P.S. MariaDB, the MySQL fork, has the same restriction but it documents it better:
Timestamps in MariaDB have a maximum value of 2147483647, equivalent
to 2038-01-19 05:14:07. This is due to the underlying 32-bit
limitation. Using the function on a date beyond this will result in
NULL being returned.
I'm using the following SQL query in postgres:
SELECT
date_trunc('month', s.thedate),
r.rank,
COUNT(r.rank)
FROM
serps s
LEFT JOIN ranks r ON r.serpid = s.serpid
GROUP BY
date_trunc('month', s.thedate), s.thedate, r.rank
ORDER BY
s.thedate ASC;
when I run that query directly against the database, I get the data all the data I need and the dates seem to be correct (formatted in Y-m-d g:i:s).
However, when I run it with PHP, Postgres instead of the date returns the timestamp.
Therefore, when I use that timestamp in PHP date, the whole date is incorrect.
For instance:
The first row Postgres displays it as:
"2013-08-01 00:00:00, 36, 1"
but PHP receives:
"1375315200000, 36, 1"
When I try to do:
echo date("Y-m-d", 1375315200000);
The output is:
45552-01-02
instead of
2013-08-01
At first I thought it was a padding issue, perhaps? I dropped the last three zeros in the timestamp so:
echo date("Y-m-d", 1375315200);
and that returns:
2013-07-31
My questions are:
1) Is it only a coincidence that after dropping three zeros, the timestamp represent a day before the actual date stored in the database?
2) Why Postgres interprets the timestamp correctly; whereas php doesn't? According to the documentation Postgres timestamp should be in the unix timestamp format.
The number they're returning is milliseconds in the Unix era, rather than seconds. Dividing by 1000 before feeding it to PHP is necessary.
With databases, be careful to check whether their timestamp is actually UTC/GMT, or has been offset to the server's timezone. I've seen both done. My server, located in California, is Pacific Time for MySQL timestamps. Be careful about sticking PHP timestamps into the database and then formatting with SQL, or vice-versa.
I use MySQL DATETIME column to store date & time. Dates are in UTC. I want to select item from one day. What i'm doing now:
SELECT * FROM data WHERE DATE(CONVERT_TZ(datetime, 'UTC', 'Australia/Sydney')) = '2012-06-01'
note that the timezone depends on user
Problem is that it is quite slow with table growing.
Is there any solution how to make it faster?
Currently your query has to compute the conversion for every row of the database. You probably could make things better by converting the other way round, so that the conversion only occurs once (or actually twice, as you'll have to form a range). Then a proper index on datetime should make things pretty fast.
SELECT * FROM data
WHERE datetime BETWEEN CONVERT_TZ('2012-06-01 00:00:00', 'Australia/Sydney', 'UTC')
AND CONVERT_TZ('2012-06-01 23:59:59', 'Australia/Sydney', 'UTC')
Or if you worry about a 23:60:00 leap second not getting matched by any query, you can do
SELECT * FROM data
WHERE datetime >= CONVERT_TZ('2012-06-01', 'Australia/Sydney', 'UTC')
AND datetime < CONVERT_TZ('2012-06-01' + INTERVAL 1 DAY, 'Australia/Sydney', 'UTC')
In the latter form, you wouldn't have to add the hours PHP-side but instead could simply pass the date as a string parameter.
Depending on your real goal, using TIMESTAMP instead of DATETIME may be a good solution.
TIMESTAMP stores the datetime as UTC, converting as it stores and as it is fetched, from/to the local timezone. This way, what I read from your table is automatically different than what you stored (assuming we are in different timezones).
Yes, use #MvG's approach of flipping the query. Yes, use his second form.
And index the column. In composite indexes, put the timestamp last, even though it is more selective.
DO NOT do SELECT *
Indexing - make sure apropriate colunms/id
fields are indexed.
Do time-conversion php-side.
OR make sure you do 1 & 2 and it may be wrapped into a Stored Proc, passing timezone as param.
Currently MySQL query will be as below:
SELECT * FROM data
WHERE datetime >= CONVERT_TZ('2012-06-01', '+00:00', '+10:00')
AND datetime < CONVERT_TZ('2012-06-01' + INTERVAL 1 DAY, '+10:00', '+00:00')
This will be done with PHP.
I basically want to get the number of rows that were inserted 30 minutes ago.
I have a time field on my table which is type TIMESTAMP and on update it's set to CURRENT_TIMESTAMP.
The date is stored in this format:
2011-05-27 04:29:17
My query is supposed to look something like this, however i just can't do it
SELECT COUNT(*) FROM mytable WHERE UNIX_TIMESTAMP(time) < '.time().'-1800
Where time() is PHP's function that fetches the UNIX time.
What it should basically do is print me the number of rows inserted from now to 30 minutes ago, but i just can't seem to make it work.
Can somebody help?
Small edit:
Another problem i am seeing is that php's function time() displays the unix time which is UTC. The time stored in mysql is probably GMT i.e whatever my computer's time/timezone is set to.
You can easily get rows stored from now to 30 mins ago by simply using:
SELECT count(*) FROM mytable WHERE `time` >= DATE_SUB(UTC_TIMESTAMP, INTERVAL 30 minute)
Usage of UTC_TIMESTAMP is just an example if you're storing your date/time data as UTC_TIMESTAMP(), you can probably use NOW() if necessary, depends on what you're storing really.
**EDIT**
Removed bad pointers and fixed example :)
Do you really need your computer's timezone to be different than UTC? why not just set it to UTC & save yourself the confusion? If that doesn't work, just use dateadd() on mysql to convert your mysql timestamp to UTC when checking?
My suggestion would be to write a small function to convert the mysql timestamp to your PHP timestamp format & load it into mysql. Then all you need to do is to call tmstamp(time_stamp) instead of time_stamp in your query. You can do the reverse too i.e. Convert PHP's "30 minutes ago" timestamp to mysql format and rerun your query (probably easier).
Usually it's just a formatting issue. It's not standardized across programs.