Zend_Date, ISO_8601, date parsing and local system clock - php

I have a strange problem with Zend_Date object.
It seems that setters perform different operations with different system clock dates.
Let's assume that system date is 28 January 2013, following code:
$now=new Zend_Date(Zend_Date::ISO_8601);
$now->now();
echo '<br/>now: ' . $now->toString();
echo '<br/>now->day: ' . $now->get(Zend_Date::DAY);
echo '<br/>now->month: ' . $now->get(Zend_Date::MONTH);
echo '<br/>now->year: ' . $now->get(Zend_Date::YEAR);
$end=new Zend_Date('2013-02-25 14:23:34', Zend_Date::ISO_8601);
echo '<br/>end: ' . $end->toString();
$end->setHour('23')->setMinute('59')->setSecond('59')->setDay($now->get(Zend_Date::DAY))->setMonth($now->get(Zend_Date::MONTH))->setYear($now->get(Zend_Date::YEAR));
echo '<br/>endAfterSetters: ' . $end->toString();
will produce following output:
now: 28-01-2013 14:04:28
now->day: 28
now->month: 01
now->year: 2013
end: 25-02-2013 14:23:34
endAfterSetters: 28-01-2013 23:59:59
But if you change system clock to 29 January 2013, output is different from expectations:
now: 29-01-2013 14:07:22
now->day: 29
now->month: 01
now->year: 2013
end: 25-02-2013 14:23:34
endAfterSetters: 01-01-2013 23:59:59
Last output is 01-01-2013 23:59:59, but should be 29-01-2013 23:59:59 !
It happens on PHP 5.3.2 and 5.3.16, Zend_Framework 10.7, latest Zend_Date 24880 version.
Everyting worked fine in the past.
Any ideas why it happens?
P.S.: I have also found jquery datatime plugin malfunciton while using it at 29,30,31 January... But i will describe it in other question.

Remember that your setters are called in sequence. So when you call setDay(29) you're telling it to change the date to 29th February 2013, which isn't a valid date, so it's rolling that over to make it 1st March 2013. Then you call setMonth(1), which changes the month to January, giving you 1st January 2013.
You can control this behaviour by passing the extend_month option to the Zend_Date constructor, see: http://framework.zend.com/manual/1.12/en/zend.date.overview.html#zend.date.options.extendmonth

Related

Using strtotime with TimeZone

I have a problem. I want to convert this DateTime: 2018-10-28 02:00:00 to a TimeStamp. Now the TimeStamp I am looking for is: 1540684800, but with my code I get this TimeStamp: 1540688400. I know it has something to do with my TimeZone, but I don't know how I can fix this.
I live in the Netherlands in Amsterdam.
Here is my code:
$LoopDateTime = "2018-10-28 02:00:00";
$search_key = (strtotime($LoopDateTime)*1000);
Can someone help me?
The time zone identifier for Amsterdam is Europe/Amsterdam and 1540688400 is the correct timestamp. There's surely an online tool to check but you can also verify it from PHP itself:
$date = new DateTime("#1540688400");
$date->setDateTimeZone(new DateTimeZone('Europe/Amsterdam'));
echo $date->format('r'); // Sun, 28 Oct 2018 02:00:00 +0100
However your code is not robust because depends on the configured timezone. You can just set it explicitly in a number of ways, e.g.:
$LoopDateTime = "2018-10-28 02:00:00";
$search_key = strtotime($LoopDateTime . ' Europe/Amsterdam') * 1000;
var_dump($search_key); // int(1540688400000)
Or:
date_default_timezone_set('Europe/Amsterdam');
$LoopDateTime = "2018-10-28 02:00:00";
$search_key = strtotime($LoopDateTime) * 1000;
var_dump($search_key); // int(1540688400000)
P.S. If I'm not wrong Sunday 28 Oct 2018 02:00:00 +0100 is the exact moment when most Europe has just switched from CEST (+0200) to CET (+0100).

PHP IntlDateFormatter wrong date/time conversion

I recently stumbled on a problem with PHP v7.0.4. when trying to format some dates in the past.
I work on a project, in which there is a thing called "empty date", basically a "1800-01-01" (used instead of the NULL value). I'm using GMT+1, "Europe/Berlin".
In process of handling it, and with the Date localization involved, the IntlDateFormatter started making some issues and I chased them down to having exceptions if the dates are <= 1893-04-01 (an early April fool thing?).
You can see some interesting examples below. Could someone please confirm that they get the same issue on their system? It should be reproducible with:
$formatter = new \IntlDateFormatter('en_US', \IntlDateFormatter::MEDIUM, \IntlDateFormatter::LONG);
echo $formatter->format(new \DateTime("1893-04-01 00:00:00")) . '<br />';
echo $formatter->format(new \DateTime("1893-04-02 00:00:00")) . '<br />';
Which should return:
"Mar 31, 1893, 11:53:28 PM GMT+0:53:28" and
"Apr 2, 1893, 12:00:00 AM GMT+1"
Or for even more visible behavior "change":
echo $formatter->format(new \DateTime("1893-04-01 00:06:31")) . '<br />';
echo $formatter->format(new \DateTime("1893-04-01 00:06:32")) . '<br />';
which should return:
"Mar 31, 1893, 11:59:59 PM GMT+0:53:28" and
"Apr 1, 1893, 12:06:32 AM GMT+1"
I presume it has something to do with historical changes of the timezones (something like this: https://github.com/eggert/tz/blob/2017b/asia#L891, although that is about Asia, and I'm using the GMT+1).
If we assume that I would actually need to use this date, 01.01.1800 - would anyone see any "normal" way around this "problem"?
You're correct, it has to do with a historical change of the timezone. The GMT offset for Europe/Berlin is +0:53:28 until April of 1893 when it jumps to a full +1.
Thus its very first GMT+1 was:
Apr 1, 1893, 12:06:32 AM GMT+1
as the second before that was:
Mar 31, 1893, 11:59:59 PM GMT+00:53:28.
Basically this means that Apr 1 1893 00:00:00 through 00:06:31 didn't exist.
The normal way of avoiding a lot of confusion around this sorta thing is to work only in UTC and deal with timezone conversions just for display. For example:
date_default_timezone_set('UTC');
$formatter = new \IntlDateFormatter(
'en_US',
\IntlDateFormatter::MEDIUM,
\IntlDateFormatter::LONG,
'Europe/Berlin'
);
echo $formatter->format(new \DateTime("1800-01-01 00:00:00")) , "\n";
echo $formatter->format(new \DateTime("1893-03-31 23:06:31")) , "\n";
echo $formatter->format(new \DateTime("1893-03-31 23:06:32")) , "\n";
echo $formatter->format(new \DateTime("1893-04-01 00:00:00")) , "\n";
Outputs:
Jan 1, 1800, 12:53:28 AM GMT+0:53:28
Mar 31, 1893, 11:59:59 PM GMT+0:53:28
Apr 1, 1893, 12:06:32 AM GMT+1
Apr 1, 1893, 1:00:00 AM GMT+1
If you'd rather you can also force IntlDateFormatter to use GMT+1 instead of your timezone:
$formatter = new \IntlDateFormatter(
'en_US',
\IntlDateFormatter::MEDIUM,
\IntlDateFormatter::LONG,
'GMT+01:00'
);

Wrong month printed on last day of August

I had this bug yesterday (August 31st) where the following printed out...
$timezone = new DateTimeZone('Europe/Helsinki');
$calendar_month_name = DateTime::createFromFormat('m', 9, $timezone)->format('F');
echo $calendar_month_name; // returns October
echo date('M'); // returns August
However, when the date changed to the 1st September, $calendar_month_name corrected itself.
Anyone know why this was? Thanks.
Edit I've turned my calendar back to be on the 31 Aug again, which is why echo date('M'); returns August.
Edit 2 Here is an example using date()
date_default_timezone_set('Europe/Helsinki');
echo date('F', mktime(0,0,0,9)); // returns October
In one line you are asking the name of the month 9 which is September.
On the last line you are asking the short name of the current month.
I think that this is what you intend, isn't?
$calendar_month_name = DateTime::createFromFormat('m', date('m'),$timezone)->format('F');
Cheers!

Unrecognized Date format

Good !
I am having some difficulties with extracting data from a date. The thing is that I get a number from an undocumented API.
"created": 734394
"last_chapter_date": 734883
I tried dividing it by 365,242 days (exact amount of days a year)
2010,705231052289
So apparently these are the number of days passed since 0.0.0000
I am currently trying something like that:
http://jsfiddle.net/LRUy5/4/
function zero21970(nDays) {
// 0 70 2013
// |-----|-----|
// 0 to date
var dateMils = nDays*24*60*60*100;
// 0 to 1970
zeroTo1970 = (1970*365.242)*24*60*60*100;
//subtract time from 0-1970 from the time 0-date
//to cut out the part from 1970-today
return new Date(dateMils-zeroTo1970);
}
//http://www.mangaeden.com/api/manga/4e70e9f6c092255ef7004344/
zero21970(734394) //-> Jan 26 1974
I need to save it in a database and work with it via php or javascript..
Does anyone recognize this kind of format or do you know a convenient way of formatting it?
Edit: I should add that the last chapter came out around 15.01.2013.. just to have something to grab.
Updated version:
I guess if the last chapter was from 2013, then the value is a number of days from 01.01.0001. So we can update the initial date as well as change setHours to setDate method for more accuracy:
var date = new Date("0001");
date.setDate(734883);
date.toGMTString(); // "Tue, 15 Jan 2013 00:00:00 GMT"
DEMO: http://jsfiddle.net/LRUy5/6/
Old version:
I found one solution that successfully works at my computer:
var date = new Date("0000");
date.setHours(734394 * 24);
date.toGMTString(); // "Mon, 13 Sep 2010 21:00:00 GMT"
DEMO: http://jsfiddle.net/LRUy5/5/
If you're using PHP, then you should replace
return new Date(dateMils-zeroTo1970);
with
return date('Y-m-d', (dateMils-zeroTo1970));

Wrong easter date in php

I try to calculate the easter date in php.
echo(date("2012: t.n.Y", easter_date(2012)).'<br>'); // 2012: 30.4.2012
This date is correct for the eastern orthodox churches. But I want the normal one!
My next try with the easter_days function:
function easter($year) {
$date = new DateTime($year.'-03-21');
$date->add(new DateInterval('P'.easter_days($year).'D'));
echo $year.": ".$date->format('t.m.Y') . "<br>\n";
}
easter(2012); // 2012: 30.4.2012
Tested oh PHP 5.2.6 and 5.3.6. I also tried to change the timezone with no success.
Your date format is wrong. t is the number of days in the given month (april = 30). Use d for day of the month:
echo(date("d.m.Y", easter_date(2012)).'<br>');
// will output: 08.04.2012
btw: orthodox easter date is April 15th this year.
If you want to use the DateTime class, the following will give you a DateTime object set to Easter. Use easter_date() instead of fiddling around with easter_days():
function easter($year, $format = 'd.m.Y') {
$easter = new DateTime('#' . easter_date($year));
// if your timezone is already correct, the following line can be removed
$easter->setTimezone(new DateTimeZone('Europe/Berlin'));
return $easter->format($format);
}
echo easter(2012); // 08.04.2012
echo easter(2012, 'd.m.Y H:i'); // 08.04.2012 00:00
Timezone
Setting the timezone is only necessary when the default timezone is wrong. Must be set afterwards as it is ignored in the constructor when a unix timestamp is provided.
If left out, the DateTime constructor may produce a wrong date (e.g. 07.04.2012 22:00 for 2012 instead of 08.04.2012 00:00)

Categories