I have a PHP-MySQL API with Time Zone Support. My database is in UTC and all date time calculations are made in UTC time, just converting to local time when data is displayed to the user.
The problem is when trying to retrive data in a natural time interval for a user in a different time zone, for example weeks, months, years and so on. The DB column is type DateTime and store dates in UTC. If I need all rows grouped by month and just use the UTC date stored in the database I will get some wrong rows, if the Time Zone differences made some rows shift its month.
Example: row with value 2015-05-01 00:00:00 is in May in UTC, but should be in April for any user in a negative Time Zone.
So using UTC is not the solution here. I need first to convert those dates to client Time Zone.
Which aproach can I use to overcome this problem?
As an example this is view groups some data by week:
SELECT `individuals_id`,
Str_to_date(Concat(Yearweek(`sessions_date`, 5), ' Monday'), '%X%V %W') AS `sessions_date`,
`protocol_index`,
`zone_index`,
Avg(`asymmetry_dom`) AS `asymmetry_dom`,
Avg(`asymmetry_rl`) AS `asymmetry_rl`,
FROM `sessions_data_view`
GROUP BY `individuals_id`,
Yearweek(`sessions_date`, 5),
`protocol_index`,
`zone_index`
ORDER BY `individuals_id`,
Yearweek(`sessions_date`, 5),
`protocol_index`,
`zone_index`
The problem is that Yearweek() should have different output for a row depending on the user time zone. It is not posible to use the column sessions_date in UTC if want to give consistent result to the user.
Rigth now I do not know the user Time Zone, but this should not be a limitation, since the app is in its desing phase and anything can be changed.
The API is a PHP application getting HTTP requests. It talks to a PHP Database class that wraps all queries to the MariaDB database. All response from the aPI is given as JSON, dates formated as UTC strings. The data is shown via a web application. DateTime Javascript objects are responsible to convert the responses from the API to correct dates for the client time zone.
Firstly, the only reliable way you have of getting the client's timezone is through javascript, assuming their computer's time-settings are correct.
Solution One - Javascript to PHP
I suggest collecting the users timezone with the following javascript:
var timeZone = new Date().getTimezoneOffset();
You will need to store this value in the database. You then format the UTC time with PHP code similar to this:
$date = new DateTime();
$timezone = new DateTimeZone('Australia/Sydney');
$date->setTimezone($timezone);
$formatted_date = $date->format('Y-m-d H:i:s');
Solution Two - PHP to Javascript
You convert a UTC string into a unix timestamp with the following:
$time = strtotime($utc);
You then return that to the browser, and have javascript format it like this:
var date = new Date(timestamp * 1000),
datevalues = [
date.getFullYear(),
date.getMonth()+1,
date.getDate(),
date.getHours(),
date.getMinutes(),
date.getSeconds(),
];
Good luck!
edit: For converting MySQL for use with queries, use CONVERT_TZ, the format being:
CONVERT_TZ('date/time field','starting timezone','ending timezone')
For example:
select *, DATE_FORMAT(CONVERT_TZ(str_to_date(timestamp),'UTC','US/Eastern'), '%m/%d/%y %h:%i %p') as 'Date - Eastern' FROM table`
Related
Can someone please assist me with the following problem. I am using Carbon to get the current time. Once I got the time and send it to my (myphpadmin) database it displays the whole date and time and not just the time. Here are all the code being used.
Laravel Code:
$date = Carbon::parse(now())->timezone('GMT+2');
$time = $date->toTimeString();
$UserRequest->finished_at = $time;
SQL Database layout and format:
Display: (Incorrect)
I have literally tried all the custom formatting from Carbon docs nothing sends over just the time.
I need this format -> 12:09 pm
Table Structure:
The problem more complicated than you describe.
First looks as finished_at is datetime field, so you can not store only date in this field. Sure, you can change the column format to time and store only time part. But this approach can cause problem with overdate date (started_at may be previous day or early).
So I think you need not change your storing flow, but you can change representation flow by using appropriate format
Using mysql, in a post table I use CURRENT_TIMESTAMP to store the date_time of publication.
Everything was ok when I was working on wampserver on local.
But I hosted my site yesterday on a server located in France, 2 hours away from the time zone of my country.
I'm using a timeago plug-in which processes the date and displays the elapsed time (there is .. sec ago, there is .. min ago, there is .. h ago, there is .. year ago) since the publication of a content.
After posting, I see "2 hours ago" when I just published 30 seconds ago.
How can I take into account the time zone of my users compared to the date and time of the server of my host?
Sample: If I post a content, 30 seconds later Me and the user who is at china or anywhere must see "30 seconds ago".
I tried that:
$d ="2020-02-28T13:09:33Z";
<time class='timeago' datetime='<?= $d ?>'> </time>
But instead of giving me "30 seconds ago", It gives me "2 hours ago".
Thank you.
I suspect you are calculating the relative time on the client side. For example, by timego plugin.
Neither MySQL, nor PHP know user's location and timezone. So, it's better to inform JS about server timezone and let it calculate the offset right.
Use ISO 8601 date time format to let timeago pligin know about server timezone. All the rest is up to JS plugin:
<?php
$ts = strtotime($row['date_time']);
?>
<time class="timeago" datetime="<?= date(DATE_ISO8601, $ts) ?>">
<?= date('Y-m-d H:i:s', $ts) ?>
</time>
<script type="text/javascript">
jQuery(function($) {
$("time.DateTime").timeago();
});
</script>
MySQL stores DATETIME without timezone information, but datetime values that are not timezone-aware are semi-useless.
So you have different options.
First option: continue to store datetime values in your current timezone, but add a second column with the proper 'offset' versus UTC, for example +1 if you are in France. Then you can perform calculation on the fly to another timezone but it is tricky because of daylight saving settings. During a certain period of the year the offset will be +1, at other times it could be +2.
So I don't recommend this approach. It's not going to scale well over time.
Other option: you can store all datetime values as UTC in your database and
then in your application you recalculate them by applying the correct offset according to your own timezone (or that of your web visitors).
This is a better option.
Whatever you do, it is important to know the timezone that applies to a given datetime value. So choose one and stick to it.
PHP has functions to handle time zone conversions, see for example: https://www.php.net/manual/en/class.datetimezone.php
Note that TIMESTAMP is limited to 1970-2038... Year 2038 problem
I would like to ask how to add datetime ('Y-m-d h:i:s') format field for the SQL.
The name of my table is groups, the name of the field is date_add
i am using cake PHP 3, i want to use the timezone Australia/Perth but i don't know how to begin.
I successfully displayed added my date using
$group->date_add = date("Y-m-d h:i:s");
However, the result for the time is not correct with the timezone.
Datetime in PHP in general
Per the documentation of DateTime you can use all options available for the date() functions for formatting. Your main mistake in the formatting is the difference betweetn a y and a Y being the difference between a year in two and a year in four numbers.
Secondly you say you want to add the correct time zone. This is a bit odd however since you always want to add the same time zone. If you want to convey the fact that all your dates are Australia/Perth as information why do you not simply add that text after it?
If you mean this is a problem since you store the information in a different time zone to begin with and thus have a conversion problem you can set the correct time zone on the DateTime object itself. But you need to be sure the DateTime object is constructed with the correct original time zone to begin with. Observe the following code for an explanation:
<?php
$DateTime = new DateTime(); // This is now Europe/Amsterdam for my laptop
var_dump($DateTime->format('dmY h:i e'));
// result of var_dump is: string(31) "13102015 12:00 Europe/Amsterdam"
$DateTime->setTimeZone(new DateTimeZone('Europe/London'));
var_dump($DateTime->format('dmY h:i e'));
// result of var_dump is: string(28) "13102015 11:00 Europe/London"
Take aways:
e is the format modifier for the time zone
Conversion of time zones is possible with PHP's DateTime object. Find out what your default time zone is on your current PHP installation to see if you need to convert or not. See information on the date.timezone setting here: http://php.net/manual/en/datetime.configuration.php
Cake 3.0 specific
As Oops D'oh pointed out in the comments there are a lot of CakePHP specific things to know as well. Since he added an excellent part concerning that I suggest you read that as well.
How to filter rows from MySQL database ignoring the time portion of a given DateTime field in MySQL using JPA?
For example, the following segment of code counts the number of rows from a database table that lie between the two dates given in a column of type DateTime in MySQL.
CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<Long>criteriaQuery=criteriaBuilder.createQuery(Long.class);
Root<Discount> root = criteriaQuery.from(entityManager.getMetamodel().entity(Discount.class));
criteriaQuery.select(criteriaBuilder.countDistinct(root));
DateTimeFormatter dateTimeFormatter=DateTimeFormat.forPattern("dd-MMM-yyyy hh:mm:ss aa");
DateTime firstDate = dateTimeFormatter.parseDateTime("01-Oct-2013 11:34:26 AM").withZone(DateTimeZone.UTC);
DateTime secondDate = dateTimeFormatter.parseDateTime("31-Oct-2013 09:22:23 PM").withZone(DateTimeZone.UTC);
criteriaQuery.where(criteriaBuilder.between(root.get(Discount_.discountStartDate), firstDate, secondDate));
Long rowCount = entityManager.createQuery(criteriaQuery).getSingleResult();
The two parameters firstDate and secondDate will be in turn dynamic.
How to rewrite this query so that the comparison does not include the time portion in the SQL query which is to be delegated to MySQL.
The column discount_start_date in the entity Discount is designated as follows.
#Column(name = "discount_start_date")
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentDateTime")
private DateTime discountStartDate;
Seems like you are working too hard.
(a) Apparently, MySQL offers a DATE() function that extracts the date portion of a date-
time field. (I'm a Postgres guy, and don't know MySQL.) You could pursue an approach using that function call as part of your query. But I'm guessing it would faster performance if you first obtained your start and stop time by calculating with Joda-Time in Java before executing the SQL query, as seen below.
(b) Why not do this with a simple SQL query, a two criteria SELECT?
In pseudo-code:
Find Discount records that go into effect from the moment this month starts up until the moment the next month starts.
Use Java and Joda-Time to give you the start & stop values.
org.joda.time.DateTime startOfThisMonth = new org.joda.time.DateTime().dayOfMonth().withMinimumValue().withTimeAtStartOfDay();
org.joda.time.DateTime startofNextMonth = startOfThisMonth.plusMonths( 1 ).dayOfMonth().withMinimumValue().withTimeAtStartOfDay();
Caution: Above code uses default time zone. You should specify a time zone in the constructor.
MySql seems to lack sophisticated time-date handling with time zones etc. So I suppose you would convert those time zoned DateTime objects to UTC.
org.joda.time.DateTime startOfThisMonthInUtc = startOfThisMonth.toDateTime( org.joda.time.DateTimeZone.UTC );
org.joda.time.DateTime startofNextMonthInUtc = startofNextMonth.toDateTime( org.joda.time.DateTimeZone.UTC );
Then do what you do to get date-time values for MySQL.
Then form a query that looks something like this… (Note the use of >= versus < without the Equals sign.)
SELECT title_, amount_, start_date_
FROM discount_
WHERE discount_.start_datetime_ >= startOfThisMonthFromJodaTime
AND discount_.start_datetime_ < startOfNextMonthFromJodaTime
;
When working with date and time, it's generally better to work with the first moment of the day, first moment of the first day of month, etc. rather than try to find the last moment or end time. So my query is based on the idea of find rows whose values go up to, but do not include, the moment after the time frame in which I'm interested.
I've got a client application that's going to update a database every five minutes with the current time, and then I want to output this time as a last active table in a seperate VB application.
I know about mysql time, but I don't quite understand how I can use it to display when a client was last active.
I've looked around and found some stuff about mysql times but I don't fully understand it.
Any help would be great, I'm going to place the results in a ListView with 'Client Name' and 'Last Active' if this helps, and I already know how to connect to my database and retrieve information.
Thank you.
I'd recommend using a DATETIME for storage. The TIME data type is limited to a single "time of day" or a timespan. True, you're looking for the time of day, but to calculate the "Last Active" time you need the date attached. Consider these "Last Active" values (using a 24-hour clock):
3/26/2013 at 17:00:00 <-- this has the maximum time (5PM), but...
3/27/2013 at 08:15:00 <-- ...this is the most recent time because it happens the following day
In other words, you need the date so you can sort the time.
The MySQL DATETIME data type should be supported by VB.NET, but I've never used the two together so I can't guarantee it. To query and report just the time component of the date you have a ton of options. Here are two:
Query the entire date/time from MySQL and return it as a System.DateTime value to VB.NET. In VB.NET you can format it using DateTime.ToString to show only the time components. The MySQL query would go something like this:
SELECT ClientName, MAX(LastActive) AS LastActiveDateTime
FROM your_table
GROUP BY ClientName
Format the time in MySQL and return it as a String to VB.NET. In VB.NET you'll just need to display the string as is. The MySQL query would go something like this:
SELECT ClientName, DATE_FORMAT(MAX(LastActive), '%r') AS LastActiveTime
FROM your_table
GROUP BY ClientName
The format code %r in the above query will return the time in a 12-hour format with AM/PM, for example 07:55:29 PM. To return a 24-hour format (19:55:29), use %T instead.