Skip to content

Commit

Permalink
Handle microseconds accurately in time traveling / Now timecop_freeze()
Browse files Browse the repository at this point in the history
and timecop_travel() accepts either DateTimeInterface or int / Implement
timecop_scale() / Implement Timecop::***() as alias of timecop_***()
  • Loading branch information
hnw committed Dec 30, 2016
1 parent b456223 commit 2f5db73
Show file tree
Hide file tree
Showing 18 changed files with 1,054 additions and 325 deletions.
29 changes: 18 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,53 +85,60 @@ var_dump($new_time == time()); // bool(false)

## CHANGELOG

###version 1.1.3, 2016/12/27
### version 1.2.0(alpha), 2016/12/30
- Big internal change (without BC break): handle microseconds accurately in time traveling.
- Now `timecop_freeze()` and `timecop_travel()` accepts either `DateTimeInterface` or `int`.
- With `DateTimeInterface` argument, freezed/traveled time would have fraction of second.
- Implement `timecop_scale()`: Make time go faster.
- Implement `Timecop::***()` as alias of `timecop_***()`. (For `freeze`, `travel`, `return`, `scale`)

### version 1.1.3, 2016/12/27
- Fix crash when non-object passed as 2nd argument of TimecopDateTime::__construct() (Fix [#9](https://github.com/hnw/php-timecop/issues/9))'
- Add CI environment (CentOS, Ubuntu 32-bit, PHP 7.1)

###version 1.1.2(alpha), 2016/04/23
### version 1.1.2(alpha), 2016/04/23
- Fix for stock PHP on Ubuntu

###version 1.1.0(alpha), 2016/04/18
### version 1.1.0(alpha), 2016/04/18
- Support PHP 7.0.x
- Now `new DateTime()` always returns `DateTime` instance
- The previous version returns `TimecopDateTime` instance when `timecop.func_override=1`.
- Implement `timecop_gettimeofday()` and `timecop_microtime()`

###version 1.0.6, 2016/04/15
### version 1.0.6, 2016/04/15
- Fix #10 (Timecop segfaults when set_error_handler throws an exception)

###version 1.0.5, 2013/11/26
### version 1.0.5, 2013/11/26
- Fix `TimecopDateTime::createFromFormat()` to reutrn `TimecopDateTime` instance on PHP >= 5.3.4
- The previous version returns `DateTime` instance
- Implement identical function `timecop_date_create_from_format()`
- BUG: not supporting "relative formats" for this method currently.
- Fix behavior of `TimecopDateTime::_construct()` when its 2nd argument is specified.

###version 1.0.4, 2013/03/11
### version 1.0.4, 2013/03/11
- Fix SIGSEGV in `TimecopDateTime::__construct()` called with NULL as 1st argument

###version 1.0.3, 2013/03/09
### version 1.0.3, 2013/03/09

- Fix the time traveling implementation for `TimecopDateTime::__construct()`
- Fix `timecop_date_create()` to return `TimecopDateTime` instance
- The previous version returns `DateTime` instance
- Add `TimecopDateTime::getTimestamp()`, `TimecopDateTime::setTimestamp()` only for PHP 5.2.x

###version 1.0.2, 2013/03/06
### version 1.0.2, 2013/03/06

- Implement `timecop_date_create()`

###Version 1.0.1, 2013/03/04
### Version 1.0.1, 2013/03/04

- Implement time traveling feature for `TimecopDateTime::__construct()` with 1 or 2 arguments
- The previous version works correctly only for no arguments calling: "new TimecopDateTime()"

###version 1.0.0, 2012/11/21
### version 1.0.0, 2012/11/21

- Fix memory leak

###version 0.0.1, 2012/06/19
### version 0.0.1, 2012/06/19

- Initial Release

Expand Down
4 changes: 2 additions & 2 deletions config.m4
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ if test "$PHP_TIMECOP" != "no"; then
fi

if test "$PHP_MAJOR_VERSION" -eq 5; then
PHP_NEW_EXTENSION(timecop, timecop_php5.c, $ext_shared)
PHP_NEW_EXTENSION(timecop, timecop_php5.c tc_timeval.c, $ext_shared)
else
PHP_NEW_EXTENSION(timecop, timecop_php7.c, $ext_shared)
PHP_NEW_EXTENSION(timecop, timecop_php7.c tc_timeval.c, $ext_shared)
fi
fi
2 changes: 1 addition & 1 deletion php-timecop.spec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
%define __ext_name timecop
Name: php-%{__ext_name}
Version: 1.1.3
Version: 1.2.0
Release: 1%{?dist}
Summary: php-timecop module

Expand Down
14 changes: 11 additions & 3 deletions php_timecop.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
#ifndef PHP_TIMECOP_H
#define PHP_TIMECOP_H

#define PHP_TIMECOP_VERSION "1.1.0"
#define PHP_TIMECOP_VERSION "1.2.0"

extern zend_module_entry timecop_module_entry;
#define phpext_timecop_ptr &timecop_module_entry
Expand All @@ -34,6 +34,7 @@ extern zend_module_entry timecop_module_entry;

#include <time.h>
#include "Zend/zend_interfaces.h"
#include "tc_timeval.h"

PHP_MINIT_FUNCTION(timecop);
PHP_MSHUTDOWN_FUNCTION(timecop);
Expand All @@ -43,6 +44,7 @@ PHP_MINFO_FUNCTION(timecop);

PHP_FUNCTION(timecop_freeze);
PHP_FUNCTION(timecop_travel);
PHP_FUNCTION(timecop_scale);
PHP_FUNCTION(timecop_return);
PHP_FUNCTION(timecop_time);
PHP_FUNCTION(timecop_mktime);
Expand All @@ -66,6 +68,9 @@ PHP_FUNCTION(timecop_date_create_from_format);
PHP_METHOD(TimecopDateTime, __construct);
PHP_METHOD(TimecopOrigDateTime, __construct);

PHP_METHOD(Timecop, freeze);
PHP_METHOD(Timecop, travel);

#if !defined(PHP_VERSION_ID) || PHP_VERSION_ID < 50300
PHP_METHOD(TimecopDateTime, getTimestamp);
PHP_METHOD(TimecopDateTime, setTimestamp);
Expand All @@ -86,9 +91,12 @@ ZEND_BEGIN_MODULE_GLOBALS(timecop)
zval *orig_request_time;
#endif
timecop_mode_t timecop_mode;
long freezed_timestamp;
long travel_offset;
tc_timeval freezed_time;
tc_timeval travel_origin;
tc_timeval travel_offset;
tc_timeval_long scaling_factor;
zend_class_entry *ce_DateTime;
zend_class_entry *ce_DateTimeInterface;
zend_class_entry *ce_TimecopDateTime;
ZEND_END_MODULE_GLOBALS(timecop)

Expand Down
74 changes: 74 additions & 0 deletions tc_timeval.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#include "tc_timeval.h"

int tc_timeval_add(tc_timeval *ret, const tc_timeval *arg1, const tc_timeval *arg2)
{
tc_timeval_long sec, usec;
usec = arg1->usec + arg2->usec;
sec = arg1->sec + arg2->sec;
if (usec < 0) {
sec -= ((-usec) / USEC_PER_SEC + 1);
usec = USEC_PER_SEC - ((-usec) % USEC_PER_SEC);
if (usec == USEC_PER_SEC) {
sec++;
usec = 0;
}
} else if (usec >= USEC_PER_SEC) {
sec += usec / USEC_PER_SEC;
usec = usec % USEC_PER_SEC;
}
ret->sec = sec;
ret->usec = usec;

return 0;
}
int tc_timeval_sub(tc_timeval *ret, const tc_timeval *arg1, const tc_timeval *arg2)
{
tc_timeval_long sec, usec;
usec = arg1->usec - arg2->usec;
sec = arg1->sec - arg2->sec;
if (usec < 0) {
sec -= ((-usec) / USEC_PER_SEC + 1);
usec = USEC_PER_SEC - ((-usec) % USEC_PER_SEC);
if (usec == USEC_PER_SEC) {
sec++;
usec = 0;
}
} else if (usec >= USEC_PER_SEC) {
sec += usec / USEC_PER_SEC;
usec = usec % USEC_PER_SEC;
}
ret->sec = sec;
ret->usec = usec;

return 0;
}
int tc_timeval_mul(tc_timeval *ret, const tc_timeval *arg1, const tc_timeval_long arg2)
{
tc_timeval_long sec, usec;
usec = arg1->usec * arg2;
sec = arg1->sec * arg2;
if (usec < 0) {
sec -= ((-usec) / USEC_PER_SEC + 1);
usec = USEC_PER_SEC - ((-usec) % USEC_PER_SEC);
if (usec == USEC_PER_SEC) {
sec++;
usec = 0;
}
} else if (usec >= USEC_PER_SEC) {
sec += usec / USEC_PER_SEC;
usec = usec % USEC_PER_SEC;
}
ret->sec = sec;
ret->usec = usec;

return 0;
}

/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/
32 changes: 32 additions & 0 deletions tc_timeval.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#ifndef TC_TIMEVAL_H
#define TC_TIMEVAL_H

#ifndef USEC_PER_SEC
# define USEC_PER_SEC 1000000
#endif

#if PHP_MAJOR_VERSION >= 7
typedef zend_long tc_timeval_long;
#else
typedef long tc_timeval_long;
#endif

typedef struct _tc_timeval {
tc_timeval_long sec;
tc_timeval_long usec;
} tc_timeval;

int tc_timeval_add(tc_timeval *ret, const tc_timeval *arg1, const tc_timeval *arg2);
int tc_timeval_sub(tc_timeval *ret, const tc_timeval *arg1, const tc_timeval *arg2);
int tc_timeval_mul(tc_timeval *ret, const tc_timeval *arg1, const long arg2);

#endif /* TC_TIMEVAL_H */

/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/
4 changes: 3 additions & 1 deletion tests/003.phpt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
--TEST--
Check for timecop_travel
--SKIPIF--
<?php
<?php
extension_loaded('timecop') or die('skip timecop not available');
$required_func = array("timecop_travel", "timecop_time");
foreach ($required_func as $func_name) {
Expand All @@ -10,6 +10,8 @@ Check for timecop_travel
}
}
?>
--INI--
date.timezone=UTC
--FILE--
<?php
timecop_travel(123);
Expand Down
6 changes: 3 additions & 3 deletions tests/005.phpt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
--TEST--
Check for timecop_gmmktime
--SKIPIF--
<?php
<?php
extension_loaded('timecop') or die('skip timecop not available');
$required_func = array("timecop_freeze", "timecop_freeze", "timecop_gmmktime");
$required_func = array("timecop_freeze", "timecop_gmmktime");
foreach ($required_func as $func_name) {
if (!function_exists($func_name)) {
die("skip $func_name() function is not available.");
Expand All @@ -12,7 +12,7 @@ Check for timecop_gmmktime
?>
--FILE--
<?php
timecop_travel(0);
timecop_freeze(0);
var_dump(timecop_gmmktime(0,0,0));
var_dump(timecop_gmmktime(16,0,0));
var_dump(timecop_gmmktime(16,0,0,1,1,1970));
Expand Down
1 change: 1 addition & 0 deletions tests/006.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Check for timecop.sync_request_time=1 and $_SERVER['REQUEST_TIME']
}
?>
--INI--
date.timezone=UTC
--FILE--
<?php
$orig_request_time = $_SERVER['REQUEST_TIME'];
Expand Down
1 change: 1 addition & 0 deletions tests/007.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Check for timecop.sync_request_time=0 and $_SERVER['REQUEST_TIME']
}
?>
--INI--
date.timezone=UTC
timecop.sync_request_time=0
--FILE--
<?php
Expand Down
26 changes: 26 additions & 0 deletions tests/019.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
Check for timecop_scale
--SKIPIF--
<?php
extension_loaded('timecop') or die('skip timecop not available');
$required_func = array("timecop_scale", "timecop_travel", "timecop_time");
foreach ($required_func as $func_name) {
if (!function_exists($func_name)) {
die("skip $func_name() function is not available.");
}
}
?>
--FILE--
<?php
timecop_travel(123);
timecop_scale(50);
usleep(200000); // 200ms * 50 = 10sec
var_dump(timecop_time());

timecop_scale(1);
timecop_travel(123);
sleep(1);
var_dump(timecop_time());
--EXPECT--
int(133)
int(124)
36 changes: 36 additions & 0 deletions tests/issue_014.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
Check for issue #14 (All PHPUnit assertions involving DateTime comparison fail with PHP 7.1)
--SKIPIF--
<?php
extension_loaded('timecop') or die('skip timecop not available');
--INI--
date.timezone=GMT
timecop.func_override=1
--FILE--
<?php
class CustomerInvoice
{
private $voidDate;
public function void()
{
$this->voidDate = new \DateTime();
}
public function getvoidDate()
{
return $this->voidDate;
}
}
function test_invoice_void()
{
$now = new \DateTime();
//timecop_freeze($now->getTimestamp());
Timecop::freeze($now);

$invoice = new CustomerInvoice();
$invoice->void();

var_dump($now == $invoice->getVoidDate());
}
test_invoice_void();
--EXPECT--
bool(true)
Loading

0 comments on commit 2f5db73

Please sign in to comment.