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.
Related
I want to ask about changing a datetime value of PHP with datetime value from MySQL data.
I have try to do this at PHP:
$sitgl = date('Y-m-d', strtotime(2012-01-12));
$sijam = date('H:i:s', strtotime(13:00:00));
$awal = $sitgl.' '.$sijam;
$awal2 = date('Y-m-d H:i:s', strtotime($awal));
$debrangkat = strtotime($awal2);
And I'm trying to convert same datetime at MySQL like this (convert it to seconds):
SELECT date_start_book, time_start_book, (TO_DAYS(CAST(date_start_book AS DATE))*86400) + TIME_TO_SEC(CAST(time_start_book AS TIME)) FROM `t_request_queue` WHERE `request_id` = '1301-0087'
which is date_start_book value is 2012-01-12 and time_start_book value is 13:00:00
My question is: why the PHP code return value : 1357970400 but the MySQL value return 63525214800 ?
what must I do to make both of value is same? Is strtotime() not return a seconds or why?
First of all as others have suggested that php code is really hurting brain. You could make that Unix Timestamp in just one line. But to answer your real question. MYSQL TO_DAYS works different than PHP UNIX Timestamp
According to MySQL Website
Given a date date, returns a day number (the number of days since year 0).
mysql> SELECT TO_DAYS(950501);
-> 728779
mysql> SELECT TO_DAYS('2007-10-07');
-> 733321
TO_DAYS() is not intended for use with values that precede the advent of the Gregorian calendar (1582), because it does not take into account the days that were lost when the calendar was changed. For dates before 1582 (and possibly a later year in other locales), results from this function are not reliable
And according to PHP Website timestamp is
Returns the current time measured in the number of seconds since the
Unix Epoch (January 1 1970 00:00:00 GMT).
And hence the difference in two values. Their starting point is way too distant from each other. MySQL starts from year 0 and PHP starts from year 1970.
Suggestion
I would suggest you save php's timestamp in mysql rather than a formatted date time. This will help you stay consistent and allow you to perform any date or time comparisons easily.
Finally, I change the PHP to datetime and at query I'm using ADD_DAYS to add a date with a seconds then I compare it with the PHP datetime result.
So many thanks to all contributor.
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.
In my php application I have this code:
<?php echo date("d/m/ Y ",strtotime($row["m_date"]));?>
In it, $row["m_date"] is fetching from a database.
The problem is that all the dates are printing perfectly except 27/2/2011. It's printing 1/1/1970 instead.
The date in the database is fine, and prints correctly in a PDF.
I'll assume you're getting the date from the database as the string 27/2/2011 because that's most probably what happens (correct me if I'm wrong).
PHP considers the string 27/2/2011 as being in the m/d/Y format, not d/m/Y and tries to parse under that assumption. Because the date is not valid under that format strtotime returns false. Giving false as the timestamp parameter to date is taken as 0, which is the timestamp for January 1st 1970.
What you need to do is either get your date in another format (or better still, as a timestamp) from the database, or parse it yourself (say using explode).
Good luck,
Alin
The database should be able to return the date to you as a UNIX timestamp. For example, MySQL has the UNIX_TIMESTAMP() function.
SELECT UNIX_TIMESTAMP(date_column) FROM table;
Postgres has date_part
SELECT DATE_PART('epoch', date_column) FROM table;
Most other databases should have similar features. If you can get the date out as a UNIX time stamp you can pass that directly to date() without having to use strtotime() as well.
All of this does of course assume you're using a temporal datatype for the columns in question (timestamp, datetime, timestamp with time zone, etc) and not just storing a string. You are using a temporal type, right? If not, then why not?
if you are storing the date in the database as a timestamp this should work
<?php echo date("d/m/Y",$row["m_date"]);?>
if you are storing the date in the database as a date or datetime this should work
<?php echo date("d/m/Y",strtotime($row["m_date"]));?>
How is the m_date stored in the databases? Is it a datetime object? Or a string.
Problem with strtotime is that it isn't real good at deciphering written dates. So something like 27/2/2011 gives problems while 27/02/2011 gives no problems at all.
So there are 2 solutions:
Make sure all the dates that get entered into the database are of the correct format (dd/mm/yyyy).
Write a regular expression that adds a leading zero to all single characters.
retrieving items from table that fall between a date range.
the date (db table field name is called: submission_date) is being stored in the database as d-M-y (ex: 21-Dec-10)
This is being stored in an oracle database, as sysdate. (it needs to stay in the database as that format, so changing the format of how it is stored is not an option)
I want to convert 21-Dec-10 to 20101221, so I can compare it to date the user has posted, which are two values, end_date, begin_date
All I need is to properly convert submission_date to Ymd (20101221)
below is in theory what I want to do:
select
*
from
table
where
(convert(Ymd=>submission_date) >= $begin_date
AND
convert(Ymd=>submission_date) <= $end_date)
If the column submission_date is of DATE datatype and $begin_date and $end_date are strings of yyyymmdd format then you could use the following query to retrieve rows that fall between a date range:
SELECT *
FROM tab
WHERE submission_date BETWEEN
TO_DATE ( $begin_date, 'yyyymmdd') AND
TO_DATE ( $end_date, 'yyyymmdd');
By not applying a function on submission_date, we give Oracle a chance to use an index on that column, if an index exists.
When you said,
This is being stored in an oracle database, as sysdate.
To me that's saying that the datatype of the column is DATE.
First let's disabuse you of the notion that DATE is stored in a String FORMAT inside Oracle. This is completely erroneous.
If the datatype of the column is DATE then the actual value is stored using 7bytes but can be thought of as a number. The fact that when you query it to examine the value you see a dd-mon-yy format is a function of NLS settings or client options. Remember Oracle is a server, any tool that let's you look at the information stored there is a portal to that info. In other words, you're not really looking at the data, you're looking at something that went and got the data and then put it on the screen for you to see. make sense?
Ok so now that you know there's no such thing as DATE's stored in a certain format, DATE MATH becomes simple.
SELECT Submission_date FROM tab
Returns a date formatted as a string based on client settings or NLS settings.
SELECT Submission_date - 1 FROM tab
Returns a date which is the same time of day, one day earlier
Notice I didn't have to do crazy things to the number 1 to perform date math and that's because the date datatype supports math processing that works like it's a number so subtraction is a simple thing.
If your variables of $begin_date are passed to Oracle as dates then they too need nothing done to them because again, they can be used like numbers.
WHERE Submission_date BETWEEN $Begin AND $End will work.
Bonus Material
BETWEEN is inclusive on both ends.
More Bonus Material
A date is actually stored like this:
Byte 1 -> Century
Byte 2 -> Year
Byte 3 -> Month
Byte 4 -> Day
Byte 5 -> Hour
Byte 6 -> Minute
Byte 7 -> Second
This allows for extremely broad ranges of dates to be supported. It's really the date math library that can make that datatype do cool things like add leap years and get the conversion from Julian to Gregorian and such.
Bonus Material Redux
SQL Server stores dates as two 4 byte integers packed together into a BINARY(8). The first 4-bytes are the elapsed days since SQL Server's base date of 1/1/1900. The Second 4-bytes Store the Time of Day Represented as the Number of Milliseconds After Midnight in quanta of 3.33 milliseconds.
It's a mistake to assume that if the value can be stored, the RDBMS supports it. I believe that SQL Server doesn't handle BC dates at all nor does it handle dates well into the future a la the year 9999. MSSS2008 did introduce a new datatype to deal with those large dates.
I'm using the America/New York timezone. In the Fall we "fall back" an hour -- effectively "gaining" one hour at 2am. At the transition point the following happens:
it's 01:59:00 -04:00
then 1 minute later it becomes:
01:00:00 -05:00
So if you simply say "1:30am" it's ambiguous as to whether or not you're referring to the first time 1:30 rolls around or the second. I'm trying to save scheduling data to a MySQL database and can't determine how to save the times properly.
Here's the problem:
"2009-11-01 00:30:00" is stored internally as 2009-11-01 00:30:00 -04:00
"2009-11-01 01:30:00" is stored internally as 2009-11-01 01:30:00 -05:00
This is fine and fairly expected. But how do I save anything to 01:30:00 -04:00? The documentation does not show any support for specifying the offset and, accordingly, when I've tried specifying the offset it's been duly ignored.
The only solutions I've thought of involve setting the server to a timezone that doesn't use daylight savings time and doing the necessary transformations in my scripts (I'm using PHP for this). But that doesn't seem like it should be necessary.
Many thanks for any suggestions.
I've got it figured out for my purposes. I'll summarize what I learned (sorry, these notes are verbose; they're as much for my future referral as anything else).
Contrary to what I said in one of my previous comments, DATETIME and TIMESTAMP fields do behave differently. TIMESTAMP fields (as the docs indicate) take whatever you send them in "YYYY-MM-DD hh:mm:ss" format and convert it from your current timezone to UTC time. The reverse happens transparently whenever you retrieve the data. DATETIME fields do not make this conversion. They take whatever you send them and just store it directly.
Neither the DATETIME nor the TIMESTAMP field types can accurately store data in a timezone that observes DST. If you store "2009-11-01 01:30:00" the fields have no way to distinguish which version of 1:30am you wanted -- the -04:00 or -05:00 version.
Ok, so we must store our data in a non DST timezone (such as UTC). TIMESTAMP fields are unable to handle this data accurately for reasons I'll explain: if your system is set to a DST timezone then what you put into TIMESTAMP may not be what you get back out. Even if you send it data that you've already converted to UTC, it will still assume the data's in your local timezone and do yet another conversion to UTC. This TIMESTAMP-enforced local-to-UTC-back-to-local roundtrip is lossy when your local timezone observes DST (since "2009-11-01 01:30:00" maps to 2 different possible times).
With DATETIME you can store your data in any timezone you want and be confident that you'll get back whatever you send it (you don't get forced into the lossy roundtrip conversions that TIMESTAMP fields foist on you). So the solution is to use a DATETIME field and before saving to the field convert from your system time zone into whatever non-DST zone you want to save it in (I think UTC is probably the best option). This allows you to build the conversion logic into your scripting language so that you can explicitly save the UTC equivalent of "2009-11-01 01:30:00 -04:00" or ""2009-11-01 01:30:00 -05:00".
Another important thing to note is that MySQL's date/time math functions don't work properly around DST boundaries if you store your dates in a DST TZ. So all the more reason to save in UTC.
In a nutshell I now do this:
When retrieving the data from the database:
Explicitly interpret the data from the database as UTC outside of MySQL in order to get an accurate Unix timestamp. I use PHP's strtotime() function or its DateTime class for this. It can not be reliably done inside of MySQL using MySQL's CONVERT_TZ() or UNIX_TIMESTAMP() functions because CONVERT_TZ will only output a 'YYYY-MM-DD hh:mm:ss' value which suffers from ambiguity problems, and UNIX_TIMESTAMP() assumes its input is in the system timezone, not the timezone the data was ACTUALLY stored in (UTC).
When storing the data to the database:
Convert your date to the precise UTC time that you desire outside of MySQL. For example: with PHP's DateTime class you can specify "2009-11-01 1:30:00 EST" distinctly from "2009-11-01 1:30:00 EDT", then convert it to UTC and save the correct UTC time to your DATETIME field.
Phew. Thanks so much for everyone's input and help. Hopefully this saves someone else some headaches down the road.
BTW, I am seeing this on MySQL 5.0.22 and 5.0.27
MySQL's date types are, frankly, broken and cannot store all times correctly unless your system is set to a constant offset timezone, like UTC or GMT-5. (I'm using MySQL 5.0.45)
This is because you can't store any time during the hour before Daylight Saving Time ends. No matter how you input dates, every date function will treat these times as if they are during the hour after the switch.
My system's timezone is America/New_York. Let's try storing 1257051600 (Sun, 01 Nov 2009 06:00:00 +0100).
Here's using the proprietary INTERVAL syntax:
SELECT UNIX_TIMESTAMP('2009-11-01 00:00:00' + INTERVAL 3599 SECOND); # 1257051599
SELECT UNIX_TIMESTAMP('2009-11-01 00:00:00' + INTERVAL 3600 SECOND); # 1257055200
SELECT UNIX_TIMESTAMP('2009-11-01 01:00:00' - INTERVAL 1 SECOND); # 1257051599
SELECT UNIX_TIMESTAMP('2009-11-01 01:00:00' - INTERVAL 0 SECOND); # 1257055200
Even FROM_UNIXTIME() won't return the accurate time.
SELECT UNIX_TIMESTAMP(FROM_UNIXTIME(1257051599)); # 1257051599
SELECT UNIX_TIMESTAMP(FROM_UNIXTIME(1257051600)); # 1257055200
Oddly enough, DATETIME will still store and return (in string form only!) times within the "lost" hour when DST starts (e.g. 2009-03-08 02:59:59). But using these dates in any MySQL function is risky:
SELECT UNIX_TIMESTAMP('2009-03-08 01:59:59'); # 1236495599
SELECT UNIX_TIMESTAMP('2009-03-08 02:00:00'); # 1236495600
# ...
SELECT UNIX_TIMESTAMP('2009-03-08 02:59:59'); # 1236495600
SELECT UNIX_TIMESTAMP('2009-03-08 03:00:00'); # 1236495600
The takeaway: If you need to store and retrieve every time in the year, you have a few undesirable options:
Set system timezone to GMT + some constant offset. E.g. UTC
Store dates as INTs (as Aaron discovered, TIMESTAMP isn't even reliable)
Pretend the DATETIME type has some constant offset timezone. E.g. If you're in America/New_York, convert your date to GMT-5 outside of MySQL, then store as a DATETIME (this turns out to be essential: see Aaron's answer). Then you must take great care using MySQL's date/time functions, because some assume your values are of the system timezone, others (esp. time arithmetic functions) are "timezone agnostic" (they may behave as if the times are UTC).
Aaron and I suspect that auto-generating TIMESTAMP columns are also broken. Both 2009-11-01 01:30 -0400 and 2009-11-01 01:30 -0500 will be stored as the ambiguous 2009-11-01 01:30.
I think micahwittman's link has the best practical solution to these MySQL limitations: Set the session timezone to UTC when you connect:
SET SESSION time_zone = '+0:00'
Then you just send it Unix timestamps and everything should be fine.
But how do I save anything to 01:30:00
-04:00?
You can convert to UTC like:
SELECT CONVERT_TZ('2009-11-29 01:30:00','-04:00','+00:00');
Even better, save the dates as a TIMESTAMP field. That's always stored in UTC, and UTC doesn't know about summer/winter time.
You can convert from UTC to localtime using CONVERT_TZ:
SELECT CONVERT_TZ(UTC_TIMESTAMP(),'+00:00','SYSTEM');
Where '+00:00' is UTC, the from timezone , and 'SYSTEM' is the local timezone of the OS where MySQL runs.
Mysql inherently solves this problem using time_zone_name table from mysql db.
Use CONVERT_TZ while CRUD to update the datetime without worrying about daylight savings time.
SELECT
CONVERT_TZ('2019-04-01 00:00:00','Europe/London','UTC') AS time1,
CONVERT_TZ('2019-03-01 00:00:00','Europe/London','UTC') AS time2;
This thread made me freak since we use TIMESTAMP columns with On UPDATE CURRENT_TIMESTAMP (ie: recordTimestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) to track changed records and ETL to a datawarehouse.
In case someone wonder, in this case, TIMESTAMP behave correctly and you can differentiate between the two similar dates by converting the TIMESTAMP to unix timestamp:
select TestFact.*, UNIX_TIMESTAMP(recordTimestamp) from TestFact;
id recordTimestamp UNIX_TIMESTAMP(recordTimestamp)
1 2012-11-04 01:00:10.0 1352005210
2 2012-11-04 01:00:10.0 1352008810
I was working on logging counts of visits of pages and displaying the counts in graph (using Flot jQuery plugin). I filled the table with test data and everything looked fine, but I noticed that at the end of the graph the points were one day off according to labels on x-axis. After examination I noticed that the view count for day 2015-10-25 was retrieved twice from the database and passed to Flot, so every day after this date was moved by one day to right.
After looking for a bug in my code for a while I realized that this date is when the DST takes place. Then I came to this SO page...
...but the suggested solutions was an overkill for what I needed or they had other disadvantages. I am not very worried about not being able to distinguish between ambiguous timestamps. I just need to count and display records per days.
First, I retrieve the date range:
SELECT
DATE(MIN(created_timestamp)) AS min_date,
DATE(MAX(created_timestamp)) AS max_date
FROM page_display_log
WHERE item_id = :item_id
Then, in a for loop, starting with min_date, ending with max_date, by step of one day (60*60*24), I'm retrieving the counts:
for( $day = $min_date_timestamp; $day <= $max_date_timestamp; $day += 60 * 60 * 24 ) {
$query = "
SELECT COUNT(*) AS count_per_day
FROM page_display_log
WHERE
item_id = :item_id AND
(
created_timestamp BETWEEN
'" . date( "Y-m-d 00:00:00", $day ) . "' AND
'" . date( "Y-m-d 23:59:59", $day ) . "'
)
";
//execute query and do stuff with the result
}
My final and quick solution to my problem was this:
$min_date_timestamp += 60 * 60 * 2; // To avoid DST problems
for( $day = $min_date_timestamp; $day <= $max_da.....
So I am not staring the loop in the beginning of the day, but two hours later. The day is still the same, and I am still retrieving correct counts, since I explicitly ask the database for records between 00:00:00 and 23:59:59 of the day, regardless of the actual time of the timestamp. And when the time jumps by one hour, I am still in the correct day.
Note: I know this is 5 year old thread, and I know this is not an answer to OPs question, but it might help people like me who encountered this page looking for solution to the problem I described.