I'm using Live Connect to create calendar events. According to their docs, the start_time given for an event should indicate how many hours off the time is from UTC (i.e. +0700 or -0300). As a first stab , I've got some code that works, pieced together from the php manual. However, it "feels" pretty verbose. So, from a stylistic point of view, might there be a way to clean up what I've got into something more succinct? Note that the $time_zone is something that I know based on a given user.
$dateTimeZone = new DateTimeZone($time_zone);
$dateTime= new DateTime("now", $dateTimeZone);
$gmt_offset = ($dateTime->getOffset())/3600;
$negative = ($gmt_offset<0);
$gmt_offset = abs($gmt_offset);
if ($gmt_offset < 10) {
$gmt_offset = '0'.$gmt_offset.'00';
} else {
$gmt_offset = $gmt_offset.'00';
}
if ($negative) {
$gmt_offset = '-'.$gmt_offset;
} else {
$gmt_offset = '+'.$gmt_offset;
}
Thank you for your input.
-Eric
$gmt_offset = $dateTime->format('O');
From the PHP manual page for date():
format character: O
Description: Difference to Greenwich time (GMT) in hours
Example returned values: Example: +0200
Related
I have these two functions:
function time_is_older_than($timestamp, $time_string)
{
if (strtotime($timestamp) < strtotime('-' . $time_string))
return true;
return false;
}
function time_is_younger_than($timestamp, $time_string)
{
if (strtotime($timestamp) > strtotime('-' . $time_string))
return true;
return false;
}
They enable me to do neat things like:
if (time_is_older_than($last_time_some_action_happened, '5 minutes'))
do_it_again();
They normally work, except for during one hour every six months, when my timezone switches over to "summer time" or "winter time". This means that the clocks are increased or put back one hour at midnight (according to this timezone).
The PHP manual states this for strtotime:
The Unix timestamp that this function returns does not contain information about time zones. In order to do calculations with date/time information, you should use the more capable DateTimeImmutable.
However, if I provide the exact same date/time string, with "+08:00" added in the end versus "+00:00", for example, I get different numbers of seconds returned. So strtotime() does understand timezones when it parses the provided time, even if the returned integer obviously doesn't contain this information. (Nor is it expected or required to by me.)
I've spent countless hours trying to debug this, testing countless things, and just sitting here thinking, but I can't figure out what exactly would make the code I have fail, specifically for one hour. And especially what about it I need to change. Setting the second parameter for strtotime() seems likely, but I just couldn't make it work correctly.
My hottest "lead" for quite some time was that the strtotime('-' . $time_string) part is ending up using a different timezone than the timestamp strings provided, but I do provide timezone data to it most of the time! An example of $last_time_some_action_happened might be something like 2020-10-28 02:22:41.123456+01.
I set the timezone with date_default_timezone_set().
I suspect that I only need to make some very minor change, but I've been experimenting so much and so long now, even taking rests in between, that my brain can no longer see this clearly. I bet the solution is something awfully simple.
Please don't tell me to use DateTimeImmutable. This would fundamentally change my entire structure and require me to do things very differently. Perhaps I should, and even will, at some point, but for now, I just wish to fix this rare but still very annoying bug in my existing code. (If it's possible at all, which I very much believe is the case.)
I'm able to reproduce the issue you are having:
date_default_timezone_set('Pacific/Auckland');
// Daylight saving time 2020 in New Zealand began at 2:00am on Sunday, 27 September
$current = strtotime('2020-09-27 02:04:00');
$d1 = strtotime('2020-09-27 02:05:00', $current);
$d2 = strtotime('-5 minutes', $current);
var_dump($d1 > $d2); // false
var_dump(date('Y-m-d H:i:s', $d1)); // 2020-09-27 03:05:00
var_dump(date('Y-m-d H:i:s', $d2)); // 2020-09-27 03:59:00
This person looks to be having the same issue as you and may appear to be a bug.
DateTime::modify and DST switch
The solution is to convert the dates to UTC then compare:
// Convert to UTC and compare
$d1 = new \DateTime('2020-09-27 02:05:00', new \DateTimeZone('Pacific/Auckland'));
$d2 = new \DateTime('2020-09-27 02:04:00', new \DateTimeZone('Pacific/Auckland'));
$d2->setTimezone(new \DateTimeZone('UTC'));
$d2->modify('-5 minutes');
$d2->setTimezone(new \DateTimeZone('Pacific/Auckland'));
var_dump($d1 > $d2); // true
var_dump($d1->format(\DateTimeInterface::RFC3339_EXTENDED)); // 2020-09-27T03:05:00.000+13:00
var_dump($d2->format(\DateTimeInterface::RFC3339_EXTENDED)); // 2020-09-27T01:59:00.000+12:00
I've updated your functions:
function time_is_older_than($datetime, $time_string)
{
$d1 = new \DateTime($datetime);
$d1->setTimezone(new \DateTimeZone('UTC'));
$d2 = new \DateTime();
$d2->setTimezone(new \DateTimeZone('UTC'));
$d2->modify('-' . $time_string);
return $d1 < $d2;
}
function time_is_younger_than($datetime, $time_string)
{
$d1 = new \DateTime($datetime);
$d1->setTimezone(new \DateTimeZone('UTC'));
$d2 = new \DateTime();
$d2->setTimezone(new \DateTimeZone('UTC'));
$d2->modify('-' . $time_string);
return $d1 > $d2;
}
Could you consider a solution:
In the timestamp string(like Thu, 21 Dec 2000 16:01:07 +0200), add a timezone tag which specify timezone without difference of daylight saving time.
I'm hoping that my concern will be answered as soon as possible. :) So here it is: I'm having a hard time solving this problem, as shown below:
$beg_week = "2014 - 49"; //yyyy - w
$stop_wk = "2015 - 5"
while($beg_week <= $stop_wk)
{
/* do logic to add week in the format "yyyy - w", but also
* have to consider the year */
$beg_week = '';
}
So, how can I add a week in a 'yyyy - w' format, without using strtotime()?
Convert your start and end times to timestamps using date_parse_from_format() and mktime(). Alternatively, use an SQL function like MySQL's UNIX_TIMESTAMP() if retrieving data from a datetime field.
Use date_parse_from_format() to break the date into its components.
Use mktime() to get a timestamp
Add a week's worth of seconds (60 * 60 * 24 * 7)
Use date() to output the next week.
Note: date_parse*() won't log/store an error in the returned array if you have more than 29 or 28 days in February (for a leap or regular year, respectively). This might/might not matter, depending on what you're using it for.
There is no need to jump through hoops with date() and mktime() for this. The DateTime classes can handle it simply and cleanly, something like this should work for you:-
$beg_week = (new \DateTime())->setISODate(2014, 49);
$stop_week = (new \DateTime())->setISODate(2015, 5);
$interval = new \DateInterval('P7D');
while($beg_week < $stop_week){
echo $beg_week->format('Y-m-d') . "<br/>\n";
$beg_week->add($interval);
}
Sorry it took so long before I am able to solved and share the method/approach that I used for this matter, since I've got an additional project than this. So what i did was:
First, build a function that gets the max week of a year(thanks to #salathe),
function getIsoWeekYear($year)
{
$date = new DateTime;
$date->setISODate($year,53);
return ($date->format("W") === "53" ? 53 : 52);
}
Then to increment the value of a week, considering also the given year,
$beg_week = "2014 - 50"; //just a sample and not actually a string
$end_week = "2015 - 05";
while($beg_week<=$end_week)
(
$out_data[] = $beg_week;
$b_week_exp = explode(" - ",$beg_week);
$b_yr_temp = $b_week_exp[0];
$b_wk_temp = $b_week_exp[1];
$max_wk_of_yr = getIsoWeeksInYear($b_yr_temp);
$out_year = $b_yr_temp;
$out_week_no = $b_wk_temp+1;
if($out_week_no > $max_wk_of_yr)
{
$out_year = $b_yr_temp+1;
$out_week_no = "1";
}
$beg_week = $out_year." - ".sprintf("%02s", $out_week_no);
)
That's it, if you will print_r the $out_data, you will have an array of,
2014 - 50
2014 - 51
2014 - 52
2015 - 01
2015 - 02
2015 - 03
2015 - 04
2015 - 05
Well, this logic is what I want, to have loop from $beg_week up to $end_week, because there's also a logic that I am executing in it. A very simple trick, for this very simple problem! :) Sorry, SOMETIMES I am that sluggish not to answer my own question/problem. I hope this one will help to anyone who'll also encounter this same scenario. Thank you!
What's the simplest way to do this?
$known_time = '19:33:39' //GMT
I want to get the current GMT Time and calculate if it is past $known_time
I do not care about the dates, I simply want to know if the time of day right now in GMT is later than $known_time
My attempts so far were clumsy as I first checked the hour...than the minute. I'm sure there's a more elegant method. Thank you!
I don't know what elegant is for you but alternatively, you could just also DateTime objects:
$now = new DateTime('GMT');
$known_time = new DateTime('19:33:39', new DateTimeZone('GMT'));
if($now >= $known_time) {
echo 'now is greater';
} else {
echo 'now is less than the time you provided';
}
you can use PHP 5.2 or above and the following functions are available there:
$date_a = new DateTime('2010-10-20 08:10:00');
$date_b = new DateTime('2008-12-13 10:42:00');
$interval = date_diff($date_a,$date_b);
echo $interval->format('%h:%i:%s');
or
what you can do is use: strtotime($yourtime)-strtotime(othertime), this will give you the difference.and then use round() to basically round the numbers..
I'm not that good at javascript (yet), so I need some help, with an alternative version of this php script (In javascript)
function until($format = ""){
$now = strtotime("now");
$nextTuesday = strtotime("-1 hour next tuesday");
$until = $nextTuesday - $now;
if(empty($format)){
return $until;
}else{
return date("$format",$until);
}
}
Just need it to count down, until next tuesday, in a really short way (Not in 20+ lines, like all the other script I've seen)
It should still return a timestamp, if it's possible (Need it for an offline app)
So if anyone could help me, I would be really happy (Not that I'm not happy right now, but I would be even happier) :D
You may want to take a look at the phpjs site. They have code showing how a substantial number of PHP functions can be done in JS.
Specifically: strtotime and date
JS doesn't have anything remotely close to strtotime. You'd have to determine "next tuesday" yourself. Once you've got that, you can extract a timestamp value using .getTime(), which will be the number of milliseconds since Jan 1/1970. This value can also be fed back into a new date object as a parameter, so you can do date math using simple numbers externally, then create a new date object again using the result.
e.g.
var now = new Date();
var ts = now.getTime();
var next_week = ts + (86400 * 7 * 1000);
next_week_object = new Date(next_week);
Once you've got the "next tuesday" code figured out, the rest is trivial
To get milliseconds till the next tuesday (nearest in the future):
function f_until(){
var now = new Date(Date.now());
var nextT = new Date(Date.now());
var cD = nextT.getDay();
if(cD < 2)nextT.setDate(nextT.getDate() + (2-cD));
else nextT.setDate(nextT.getDate() + (9-cD));
nextT.setHours(nextT.getHours() - 1);
//alert('next tuesday: '+nextT.toString());
return nextT.getTime() - now.getTime();
}
In PHP, you can tell if a given date is during the Daylight Savings Time period by using something like this:
$isDST = date("I", $myDate); // 1 or 0
The problem is that this only tells you whether that one point in time is in DST. Is there a reliable way to check whether DST is in effect at any time in that timezone?
Edit to clarify:
Brisbane, Australia does not observe daylight savings at any time of the year. All year around, it is GMT+10.
Sydney, Australia does, from October to March when it changes from GMT+10 to GMT+11.
I'm wondering if there would be some existing method, or a way to implement a method which works as such:
timezoneDoesDST('Australia/Brisbane'); // false
timezoneDoesDST('Australia/Sydney'); // true
I've found a method which works using PHP's DateTimezone class (PHP 5.2+)
function timezoneDoesDST($tzId) {
$tz = new DateTimeZone($tzId);
$trans = $tz->getTransitions();
return ((count($trans) && $trans[count($trans) - 1]['ts'] > time()));
}
or, if you're running PHP 5.3+
function timezoneDoesDST($tzId) {
$tz = new DateTimeZone($tzId);
return count($tz->getTransitions(time())) > 0;
}
The getTransitions() function gives you information about each time the offset changes for a timezone. This includes historical data (Brisbane had daylight savings in 1916.. who knew?), so this function checks if there's an offset change in the future or not.
Actually nickf method didn't works for me so I reworked it a little ...
/**
* Finds wherever a TZ is experimenting dst or not
* #author hertzel Armengol <emudojo # gmail.com>
* #params string TimeZone -> US/Pacific for example
*
*/
function timezoneExhibitsDST($tzId) {
$tz = new DateTimeZone($tzId);
$date = new DateTime("now",$tz);
$trans = $tz->getTransitions();
foreach ($trans as $k => $t)
if ($t["ts"] > $date->format('U')) {
return $trans[$k-1]['isdst'];
}
}
// Usage
var_dump(timezoneExhibitsDST("US/Pacific")); --> prints false
var_dump(timezoneExhibitsDST("Europe/London")); --> prints false
var_dump(timezoneExhibitsDST("America/Chicago")); --> prints false
same function call will return true in 1 month (March) hope it helps
DateTimeZone::getTransitions might help.
You could probably wing it:
$hasDst = date("I", strtotime('June 1')) !== date("I", strtotime('Jan 1'));
Otherwise you'd need to parse the text-based zoneinfo data files.
I don't think so, but since almost every country that observes DST changes its time for an entire season or two, you could try to test 4 points during any given year.
For example, test date("I", $date) for 2009/01/01, 2009/04/01, 2009/07/01 and 2009/10/01. If that timezone falls into DST, then at least one of those dates will return 1.
date has to be on the user/server timezone for it to work, and you can't use a range with date as you do with getTransitions