Dealing with Timezones and Dates in PHP - php

I am running a service hosted on a server in the US which reads an XML feed that has been created with a local date - currently just the UK, but I want to ensure the service works with all timezones.
My process looks at the date of a post in a feed and compares it with the date/time right now(on the server in the US).
The solution I came up with localises the system to the originator of the feed and then creates a timestamp with which to compare 'now' with:
protected function datemath($thedate){
$currenttimezone = date_default_timezone_get();
date_default_timezone_set($this->feedtimezone);
$thedate = mktime substr($thedate,11,2),substr($thedate,14,2),
substr($thedate,17,2),substr($thedate,3,2),substr($thedate,0,2),
substr($thedate,6,4));
date_default_timezone_set($currenttimezone);
return $thedate;
}
My question is this... Is this a reasonable way of handling this issue or is there a better, more standardized way that I really should know?

Here's a function I wrote to do timezone conversions. Should be pretty self-explanatory:
function switch_timezone($format, $time = null,
$to = "America/Los_Angeles", $from = "America/Los_Angeles")
{
if ($time == null) $time = time();
$from_tz = new DateTimeZone($from);
$to_tz = new DateTimeZone($to);
if (is_int($time)) $time = '#' . $time;
$dt = date_create($time, $from_tz);
if ($dt)
{
$dt->setTimezone($to_tz);
return $dt->format($format);
}
return date($format, $time);
}

After a bit more checking of other peoples code I see the function
strtotime($thedate);
is a little bit more succinct than using mktime and also allows for different time formats.

Related

How to get users timezone in htaccess file on server [duplicate]

I'm trying to get the user's timezone as a string on singup. example:
$timezone = "Asia/Tel_Aviv";
While researching the issue, I got how to Get timezone offset with Javascript, but I'm still unclear about how can I translate the timezone offset to a timezone string in php, as shown above?
Or, which other method cas I use in Javascript / PHP for getting the timezone string for each user?
I'm really not sure how to approach this.
You can't do this in PHP alone.
You can use Javascript to set the value in a cookie, then use PHP to read the cookie on the next page (re)load.
Javascript:
var dateVar = new Date()
var offset = dateVar.getTimezoneOffset();
document.cookie = "offset="+offset;
PHP:
echo $_COOKIE['offset'];
Use this to convert the offset to the friendly timezone name in PHP. Javascript returns the offset in minutes, while this PHP function expects the input to be in seconds - so multiply by 60. The third parameter is a boolean value of whether or not you are in Daylight Savings Time. Read the manual and update the code to fit your needs.
echo timezone_name_from_abbr("", intval($_COOKIE['offset'])*60, 0);
http://php.net/manual/en/function.timezone-name-from-abbr.php
You cannot get the timezone name from an offset. That's because there are many timezones which have the same offset at any given time, so you can't pick one based on an offset. (If you do, this will bite you in the butt later when the timezone goes into or out of DST, changing the offset.
Your best bet is to do geolocation by IP address (google it, lots of material out there) as a best first guess and then give the user an option to choose his timezone himself.
function tzone(){
if(isset($_SESSION["tz"])){ $return = $_SESSION["tz"]; } else {
$ip = $_SERVER["REMOTE_ADDR"];
$getip = file_get_contents("http://freegeoip.net/json/$ip");
$getip = json_decode($getip);
$lat = $getip->latitude; $lng = $getip->longitude; $country = $getip->country_name;
$getzone = file_get_contents("http://api.geonames.org/timezoneJSON?lat=$lat&lng=$lng&username=demo"); //you can change "demo" to your own username. its free service
$getzone = json_decode($getzone);
$zone = $getzone->timezoneId;
$_SESSION["tz"] = $zone;
$return = $_SESSION["tz"];
}
return $return;
}
You need to follow answers of Nicholas Pickering and decezeā™¦ partially. Do not use PHP's timezone_name_from_abbr function(Ref).
Follow these steps to get UTC time of any timezone set in user system:
Javascript (client-side):
var dateVar = new Date();
var offset = dateVar.getTimezoneOffset();
//getTimezoneOffset - returns the timezone difference between UTC and Local Time
document.cookie = "offset="+offset;
Php (server-side):
public function convert_utc_time($date)
{
$time_difference = isset($_COOKIE['offset'])?$_COOKIE['offset']:'';
if($time_difference != ''){
$time = strtotime($date);
$time = $time + ($time_difference*60); //minutes * 60 seconds
$date = date("Y-m-d H:i:s", $time);
} //on failure of js, default timezone is set as UTC below
return $date;
}
..
..
//in my function
$timezone = 'UTC';
$date = $this->convert_utc_time($post_date); //$post_date('Y-m-d H:i:s')
echo strtotime($date. ' '. $timezone)

Google API TimeStamp protocol

I'm trying to retrieve metrics from Google My Business API.
However I cant figure out what to pass as a timestamp for the time periods.
The error I'm getting is this...
Invalid value at 'basic_request.time_range.end_time' (type.googleapis.com/google.protobuf.Timestamp),
Field 'endTime', Invalid data type for timestamp, value is 1606780800
My code is this
$time = new \Google_Service_MyBusiness_TimeRange;
$start = strtotime("2020-01-01");
$end = strtotime("2020-12-01");
$time->setStartTime($start);
$time->setEndTime($end);
In the class for the TimeRange it shows they simply must be timestamps
class Google_Service_MyBusiness_TimeRange extends \Google_Model
{
protected $internal_gapi_mappings = array( );
/* #params Unix Timestamps */
private $endTime;
private $startTime;
However... I was looking at this.
https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Timestamp
And it shows that you have to pass something on the lines of Timestamp(seconds, nanos).
So it seems like google wants an array of the seconds, and the nano seconds??
Heres other docs to help
https://developers.google.com/my-business/reference/rpc/google.mybusiness.v4#google.mybusiness.v4.TimeRange
Anyone run into this problem?
The documentation you link to says:
Range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z.
Suggesting that you should use a similar format.
$time = new \Google_Service_MyBusiness_TimeRange;
$start = \DateTime::createFromFormat(
"y-m-d H:i:s",
"2020-01-01 00:00:00",
new \DateTimeZone("UTC")
)
->format("Y-m-d\TH:i:s\Z");
$end = \DateTime::createFromFormat(
"y-m-d H:i:s",
"2020-11-30 23:59:59",
new \DateTimeZone("UTC")
)
->format("Y-m-d\TH:i:s\Z");
$time->setStartTime($start);
$time->setEndTime($end);
I wasn't able to find much about this library online, but what little there is does follow this format. "Z" at the end indicates UTC, and I've hard-coded it in my example. It should be able to get replaced with your local timezone, but you may have to try perhaps "O" or "P" in the format string.
Here is some working code:
Note, $startDate and $endDate are just strings passed in from a jQuery calendar, ex "12/27/2020".
The key here is to use DATE_ATOM, which produces a timestamp in the format 2020-12-27T13:22:12+00:00
$gmbStartDate = date(DATE_ATOM, strtotime($startDate . " 12:01 AM "));
$gmbEndDate = date(DATE_ATOM, strtotime($endDate . " 11:59 PM "));
$time = new Google_Service_MyBusiness_TimeRange();
$time->setStartTime($gmbStartDate);
$time->setEndTime($gmbEndDate);
$basicMetricsRequest->setTimeRange($time);
Note that the timestamps are expressed as UTC, so in this example, the insights may be slightly off based on the time zone for the business. If you want to get the insights for your time zone, you'll have to add/subtract the appropriate number of seconds for your timezone.

Convert User Submitted Date To UTC

I'm trying to figure out how to accept a date/time from a form, which is consequently in the user's timezone, and change it to UTC before inserting it into the database. For some reason, no amount of searching has netted me an answer.
My form will POST whatever date is selected by the user to my code, so I expect to be able to do something like this. Note: the $userDate may be relative to any number of timezones based on user's location
$userDate = $_POST['user_date'] // 2014-05-15 16:37:23
I anticipate using Date().getTimezoneOffset() on my form to also submit the users UTC offset (as detailed here).
$userOffset = $_POST['user_offset']
Then before inserting the date into my database, I would like to convert it to UTC -- but I am stumped on how to do that with PHP (I'm actually using Laravel so if you know of a way using Carbon, that would be even easier, but I couldn't find it in their docs).
I've been half tempted to manually parse the offset and convert it to number of seconds and add or subtract it to strtotime() output of the $userDate and then convert it back into a date format using date() -- but there has to be a better way!
What am I missing here? Does PHP have a function I just don't know about that lets me do something like:
$userDate = '2014-05-15 16:37:23';
$userOffset = '+04:00';
$utcDate = date_apply_offset($userDate, $userOffset);
echo $utcDate; // Outputs: 2014-05-15 20:37:23
Or am I making this harder than it has to be?
EDIT
Based on the solution provided by #vascowhite, I went with the following (added into question to improve answers for those seeking guidance)
I ended up using a function from moment.js since I was already using it to convert UTC to user's timezone on display.
HTML:
<input id="user_offset" type="hidden" name="user_offset" value="">
Javascript:
var offset = moment().format('ZZ');
$('#user_offset').val(offset);
PHP (in a custom date class):
class MyDate {
/**
* Convert Date to UTC
*
* #param string $date Any date parsable with strtotime()
* #param string $offset UTC offset of date
*/
public static function toUTC($date, $offset = '+0:00')
{
if ($timestamp = strtotime($date) && ! empty($offset) )
{
$newDate = date('Y-m-d H:i:s', $timestamp);
$newDate = new \DateTime($date . ' ' . $offset);
$newDate->setTimezone(new DateTimeZone('UTC'));
$date = $newDate->format('Y-m-d H:i:s');
}
return $date;
}
}
// To convert
$userDate = trim($_POST['user_offset']);
$userOffset = trim($_POST['user_date']);
$utc = MyDate::toUTC($userDate, $userOffset)
That class method isn't perfect, and in the event something goes wrong, it just returns the date back -- when really it should throw an exception.
This is a simple task with the DateTime classes:-
$userDate = '2014-05-15 16:37:23';
$userOffset = '+04:00';
$date = new \DateTime($userDate . ' ' . $userOffset);
var_dump($date);
$date->setTimezone(new \DateTimeZone('UTC'));
var_dump($date);
You can then format the date as you wish for output eg:-
echo $date->format('Y-m-d H:i:s');
or:-
$utcDate = $date->format('Y-m-d H:i:s');
echo $utcDate; // Outputs: 2014-05-15 20:37:23
See it working.
If you are doing any work with dates and times in PHP it is worth taking the time to become familiar with these extremely useful classes.
For all sorts of date/time manipulations you can make use of moment.php
For your example all what is needed are two lines of code:
$m = new \Moment\Moment('2014-05-15 16:37:23', '+0400');
echo $m->setTimezone('UTC')->format(); // 2014-05-15T12:37:23+0000
There is much more which helps to deal with date/time issues: https://github.com/fightbulc/moment.php
Cheers

Converting UTC to a different time zone in php

I'm using the below method to convert the UTC time to an other time zone. But the below method seems to returning back the UTC time. Will any of you be kind enough to point out whats wrong in the method I'm using?
static function formatDateMerchantTimeZone($t, $tz) {
if (isset($t)) {
return date('Y-m-d H:i:s', strtotime($t , $tz));
} else {
return null;
}
}
$t is the datetime I pass
$tz is the time zone such as America/Los_Angeles
It surprises me that many people are unaware of, or do not use the DateTime classes. They make tasks like this almost trivial.
I have assumed that the date string you pass to the function is in the UTC timezone.
function formatDateMerchantTimeZone($t, $tz)
{
$date = new \DateTime($t, new \DateTimeZone('UTC'));
$date->setTimezone(new \DateTimeZone($tz));
return $date->format('Y-m-d H:i:s');
}
See it working
Strtotime converts a timestamp in string format to a valid date time like '09-29-2013 07:00:00' as second parameter, it does not convert a timezone to a time. php has numerous functions for timezones such as timezone_offset that does calculate the difference between two timezones. take a look in the documentation for more info:
http://php.net/manual/en/function.timezone-offset-get.php
static function formatDateMerchantTimeZone($t, $tz) {
if (isset($t)) {
date_default_timezone_set($tz);
return date('Y-m-d H:i:s', strtotime($t));
} else {
return null;
}
}
From php.net first comment.
To avoid frustrating confusion I recommend always calling
date_default_timezone_set('UTC') before using strtotime().
Because the UNIX Epoch is always in UTC; you will most likely output the wrong time if you do not do this.
try this:
<?php
/** Returns the offset from the origin timezone to the remote timezone, in seconds.
* #param $remote_tz;
* #param $origin_tz; If null the servers current timezone is used as the origin.
* #return int;
*/
function get_timezone_offset($remote_tz, $origin_tz = null) {
if($origin_tz === null) {
if(!is_string($origin_tz = date_default_timezone_get())) {
return false; // A UTC timestamp was returned -- bail out!
}
}
$origin_dtz = new DateTimeZone($origin_tz);
$remote_dtz = new DateTimeZone($remote_tz);
$origin_dt = new DateTime("now", $origin_dtz);
$remote_dt = new DateTime("now", $remote_dtz);
$offset = $origin_dtz->getOffset($origin_dt) - $remote_dtz->getOffset($remote_dt);
return $offset;
}
?>
Examples:
<?php
// This will return 10800 (3 hours) ...
$offset = get_timezone_offset('America/Los_Angeles','America/New_York');
// or, if your server time is already set to 'America/New_York'...
$offset = get_timezone_offset('America/Los_Angeles');
// You can then take $offset and adjust your timestamp.
$offset_time = time() + $offset;
?>

default date() should be 3 months ahead

My server has many application instances.
I came across a problem that one of my application instance needs to be tested with the future date. i.e I want to test the application as it is running in 2013.
If i change the system date then it will work fine but the other instances will also get effected.
I want the future date for only one instance and the rest should work as it is.
i.e if i use date('Y-m-d'); it should jump for 3 months and display the future date.
and i dont want to add seconds to the default date as that might be a huge change in my application.
And that's why you write your application in a way that is testable.
Not good:
function doSomething() {
$date = date('Y-m-d');
...
}
Good:
function doSomething($ts = null) {
if (!$ts) {
$ts = time();
}
$date = date('Y-m-d', $ts);
...
}
you can make your own date function. It would serve as a hook to all date usage.
function mydate($format) {
$jump = ' +3 months';
return date($format, strtotime(date($format) . $jump));
}
you can than change all occurrences of date to mydate. If you decide to switch back to present, just leave $jump = ''
You can just do
date('Y-m-d', time() + 3 * 30 * 24 * 3600);
I recommend using the PHP5 DateTime classes. They're a bit more wordy, but much more powerful than the old-style PHP date handling functions.
$dateNow = new DateTime();
$dateAhead = $dateNow->add(DateInterval::createFromDateString('3 months'));
print $dateAhead->format('Y-m-d');

Categories