fix sunday being first day of week (#2483)

This commit is contained in:
Kevin Papst
2021-04-19 18:32:21 +02:00
committed by GitHub
parent b6462b406d
commit 7ddcde62dd
17 changed files with 136 additions and 71 deletions

View File

@@ -11,18 +11,18 @@
<a href="https://www.bountysource.com/teams/kimai2"><img alt="Bountysource" src="https://img.shields.io/bountysource/team/kimai2/activity"></a> <a href="https://www.bountysource.com/teams/kimai2"><img alt="Bountysource" src="https://img.shields.io/bountysource/team/kimai2/activity"></a>
</p> </p>
<h1 align="center">Kimai 2 - Time Tracker</h1> <h1 align="center">Kimai - time-tracker</h1>
Kimai is a free, open source and online time tracking software designed for small businesses and freelancers. Kimai is a free, open source and online time-tracking software designed for small businesses and freelancers.
It is built with modern technologies such as Symfony, Bootstrap, RESTful API, Doctrine, AdminLTE, Webpack, ES6 etc. It is built with modern technologies such as Symfony, Bootstrap, RESTful API, Doctrine, AdminLTE, Webpack, ES6 etc.
## Introduction ## Introduction
- [Home](https://www.kimai.org) - Kimai project homepage - [Home](https://www.kimai.org) - Kimai project homepage
- [Blog](https://www.kimai.org/blog/) - Get the latest news - [Blog](https://www.kimai.org/blog/) - Read the latest news
- [Documentation](https://www.kimai.org/documentation/) - Learn how to use Kimai - [Documentation](https://www.kimai.org/documentation/) - Learn how to use Kimai
- [Translations](https://hosted.weblate.org/projects/kimai/#languages) - Kimai in your language - [Translations](https://hosted.weblate.org/projects/kimai/#languages) - Kimai in your language
- [Migration](https://www.kimai.org/documentation/migration-v1.html) - Import data from v1 - [Migration](https://www.kimai.org/documentation/migration-v1.html) - Import data from Kimai 1
### Requirements ### Requirements
@@ -34,11 +34,11 @@ It is built with modern technologies such as Symfony, Bootstrap, RESTful API, Do
### About ### About
This is the new version of the open source time tracker Kimai. It is stable and production ready, ships This is the new version of the open source time tracker Kimai. It is stable and production ready, ships
with a lot of advanced features, including but not limited to: with many advanced features, including but not limited to:
JSON API, invoicing, data exports, multi-timer and punch-in punch-out mode, tagging, multi-user and multi-timezones, JSON API, invoicing, data exports, multi-timer and punch-in punch-out mode, tagging, multi-user and multi-timezones,
authentication via SAML/LDAP/Database, customizable role permissions, responsive and ready for your mobile device, authentication via SAML/LDAP/Database, customizable role and team permissions, responsive and ready for your mobile device,
user specific rates, advanced search & filtering, money and time budgets with report, support for plugins and many more. user specific rates, advanced search & filtering, money and time budgets, multiple reports, support for plugins and many more.
## Installation ## Installation
@@ -68,8 +68,7 @@ It is open for changes and input from the community, your [ideas and questions](
Release versions will be created on a regular base (approx. one release every 4-8 weeks). Release versions will be created on a regular base (approx. one release every 4-8 weeks).
Every code change, whether it's a new feature or a bugfix, will be done on the master branch. Every code change, whether it's a new feature or a bugfix, will be done on the master branch.
Kimai is actively developed in my spare time and I put my effort into the software instead of backporting changes for old versions. Kimai is actively developed in my spare time, I put my effort into the software instead of backporting changes.
The only exception is a critical security issue, which I would fix in the latest stable version as well.
## Credits ## Credits

View File

@@ -36,8 +36,12 @@ export default class KimaiDateRangePicker extends KimaiPlugin {
rangesList[TRANSLATE.get('today')] = [moment(), moment()]; rangesList[TRANSLATE.get('today')] = [moment(), moment()];
rangesList[TRANSLATE.get('yesterday')] = [moment().subtract(1, 'days'), moment().subtract(1, 'days')]; rangesList[TRANSLATE.get('yesterday')] = [moment().subtract(1, 'days'), moment().subtract(1, 'days')];
rangesList[TRANSLATE.get('thisWeek')] = [moment().startOf('week'), moment().endOf('week')]; rangesList[TRANSLATE.get('thisWeek')] = [moment().startOf('isoWeek'), moment().endOf('isoWeek')];
rangesList[TRANSLATE.get('lastWeek')] = [moment().subtract(1, 'week').startOf('week'), moment().subtract(1, 'week').endOf('week')]; rangesList[TRANSLATE.get('lastWeek')] = [moment().subtract(1, 'week').startOf('isoWeek'), moment().subtract(1, 'week').endOf('isoWeek')];
if (firstDow === 0) { // sunday = 0
rangesList[TRANSLATE.get('thisWeek')] = [moment().startOf('isoWeek').subtract(1, 'day'), moment().endOf('isoWeek').subtract(1, 'day')];
rangesList[TRANSLATE.get('lastWeek')] = [moment().subtract(1, 'week').startOf('isoWeek').subtract(1, 'day'), moment().subtract(1, 'week').endOf('isoWeek').subtract(1, 'day')];
}
rangesList[TRANSLATE.get('thisMonth')] = [moment().startOf('month'), moment().endOf('month')]; rangesList[TRANSLATE.get('thisMonth')] = [moment().startOf('month'), moment().endOf('month')];
rangesList[TRANSLATE.get('lastMonth')] = [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]; rangesList[TRANSLATE.get('lastMonth')] = [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')];
rangesList[TRANSLATE.get('thisYear')] = [moment().startOf('year'), moment().endOf('year')]; rangesList[TRANSLATE.get('thisYear')] = [moment().startOf('year'), moment().endOf('year')];

File diff suppressed because one or more lines are too long

View File

@@ -5,7 +5,7 @@
"build/runtime.098eaae1.js", "build/runtime.098eaae1.js",
"build/0.9ec1fb96.js", "build/0.9ec1fb96.js",
"build/1.2890d91a.js", "build/1.2890d91a.js",
"build/app.467556fb.js" "build/app.2ee3c27f.js"
], ],
"css": [ "css": [
"build/app.8e8f1a48.css" "build/app.8e8f1a48.css"
@@ -53,7 +53,7 @@
"build/runtime.098eaae1.js": "sha384-xNNrNinl64G3nCUrIskgSjU0mUXXCB9lj6XCSInBTwxSKXk8uTMafnLHtdWdIGtd", "build/runtime.098eaae1.js": "sha384-xNNrNinl64G3nCUrIskgSjU0mUXXCB9lj6XCSInBTwxSKXk8uTMafnLHtdWdIGtd",
"build/0.9ec1fb96.js": "sha384-1dI1TIwKkV71L1OH+DnDqaG1su73sfoXwMVzfTuAEIg0HgVImtDf01RtbASXV7yK", "build/0.9ec1fb96.js": "sha384-1dI1TIwKkV71L1OH+DnDqaG1su73sfoXwMVzfTuAEIg0HgVImtDf01RtbASXV7yK",
"build/1.2890d91a.js": "sha384-r7OpYErcdJM7NmHiLm3n6J144cfkYc57YYAKn/QabksGh/Rd+tpcy6/kLwpVSD0b", "build/1.2890d91a.js": "sha384-r7OpYErcdJM7NmHiLm3n6J144cfkYc57YYAKn/QabksGh/Rd+tpcy6/kLwpVSD0b",
"build/app.467556fb.js": "sha384-QwfljTQx0UHT8MRamQWxV2kAtQEVoPLtlEafY5VPg0kwx+7gL7Omjqj/7ifGIW7x", "build/app.2ee3c27f.js": "sha384-YyGA9sqAqi7lk0IYrAj5E2CQZGZbMQuSCv2Auz7fbjxcuRJvh9hOWkV6r3qOJKNA",
"build/app.8e8f1a48.css": "sha384-k9l5JFdLXPYnXgp8zIzSKC49AvFX5wDYx5+Pc7/NVMnOvnFUC0BzArW8H3mmGJPJ", "build/app.8e8f1a48.css": "sha384-k9l5JFdLXPYnXgp8zIzSKC49AvFX5wDYx5+Pc7/NVMnOvnFUC0BzArW8H3mmGJPJ",
"build/invoice.74279541.js": "sha384-2BXic5Sgorf2tXai6zSAN4wLY2dbg06L03/xMKW6itMcszvtnRArKzfBh6DNcF3f", "build/invoice.74279541.js": "sha384-2BXic5Sgorf2tXai6zSAN4wLY2dbg06L03/xMKW6itMcszvtnRArKzfBh6DNcF3f",
"build/invoice.13d8ef4e.css": "sha384-B6RN/wZJToSBCZk2JeLokIqWEhbh+Eb9arYbt9dM+YoC2Z6PnCeTwTqSGyexWWJh", "build/invoice.13d8ef4e.css": "sha384-B6RN/wZJToSBCZk2JeLokIqWEhbh+Eb9arYbt9dM+YoC2Z6PnCeTwTqSGyexWWJh",

View File

@@ -3,7 +3,7 @@
"build/1.2890d91a.js": "build/1.2890d91a.js", "build/1.2890d91a.js": "build/1.2890d91a.js",
"build/2.42abdacc.js": "build/2.42abdacc.js", "build/2.42abdacc.js": "build/2.42abdacc.js",
"build/app.css": "build/app.8e8f1a48.css", "build/app.css": "build/app.8e8f1a48.css",
"build/app.js": "build/app.467556fb.js", "build/app.js": "build/app.2ee3c27f.js",
"build/calendar.css": "build/calendar.1408f57e.css", "build/calendar.css": "build/calendar.1408f57e.css",
"build/calendar.js": "build/calendar.070aab88.js", "build/calendar.js": "build/calendar.070aab88.js",
"build/chart.js": "build/chart.34d60a88.js", "build/chart.js": "build/chart.34d60a88.js",

View File

@@ -30,6 +30,6 @@ abstract class BaseApiController extends AbstractController
$user = $this->getUser(); $user = $this->getUser();
} }
return new DateTimeFactory(new \DateTimeZone($user->getTimezone())); return DateTimeFactory::createByUser($user);
} }
} }

View File

@@ -157,7 +157,7 @@ class InvoiceCreateCommand extends Command
} }
$timezone = new \DateTimeZone($timezone); $timezone = new \DateTimeZone($timezone);
$dateFactory = new DateTimeFactory($timezone); $dateFactory = new DateTimeFactory($timezone, $user->isFirstDayOfWeekSunday());
if (!empty($input->getOption('start')) && empty($input->getOption('end'))) { if (!empty($input->getOption('start')) && empty($input->getOption('end'))) {
$io->error('You need to supply a end date if a start date was given'); $io->error('You need to supply a end date if a start date was given');

View File

@@ -165,7 +165,7 @@ abstract class AbstractController extends BaseAbstractController implements Serv
$user = $this->getUser(); $user = $this->getUser();
} }
return new DateTimeFactory(new \DateTimeZone($user->getTimezone())); return DateTimeFactory::createByUser($user);
} }
protected function getLocaleFormats(string $locale): LocaleFormats protected function getLocaleFormats(string $locale): LocaleFormats

View File

@@ -69,6 +69,7 @@ class User extends BaseUser implements UserInterface
public const ROLE_USER = 'ROLE_USER'; public const ROLE_USER = 'ROLE_USER';
public const ROLE_TEAMLEAD = 'ROLE_TEAMLEAD'; public const ROLE_TEAMLEAD = 'ROLE_TEAMLEAD';
public const ROLE_ADMIN = 'ROLE_ADMIN'; public const ROLE_ADMIN = 'ROLE_ADMIN';
//public const ROLE_SUPER_ADMIN = 'ROLE_SUPER_ADMIN';
public const DEFAULT_ROLE = self::ROLE_USER; public const DEFAULT_ROLE = self::ROLE_USER;
public const DEFAULT_LANGUAGE = Constants::DEFAULT_LOCALE; public const DEFAULT_LANGUAGE = Constants::DEFAULT_LOCALE;
@@ -370,6 +371,11 @@ class User extends BaseUser implements UserInterface
$this->setPreferenceValue(UserPreference::LOCALE, $language); $this->setPreferenceValue(UserPreference::LOCALE, $language);
} }
public function isFirstDayOfWeekSunday(): bool
{
return $this->getFirstDayOfWeek() === 'sunday';
}
public function getFirstDayOfWeek(): string public function getFirstDayOfWeek(): string
{ {
return $this->getPreferenceValue(UserPreference::FIRST_WEEKDAY, User::DEFAULT_FIRST_WEEKDAY); return $this->getPreferenceValue(UserPreference::FIRST_WEEKDAY, User::DEFAULT_FIRST_WEEKDAY);

View File

@@ -294,12 +294,14 @@ final class ServiceInvoice
private function getDateTimeFactory(InvoiceQuery $query): DateTimeFactory private function getDateTimeFactory(InvoiceQuery $query): DateTimeFactory
{ {
$timezone = date_default_timezone_get(); $timezone = date_default_timezone_get();
$sunday = false;
if (null !== $query->getCurrentUser()) { if (null !== ($user = $query->getCurrentUser())) {
$timezone = $query->getCurrentUser()->getTimezone(); $timezone = $user->getTimezone();
$sunday = $user->isFirstDayOfWeekSunday();
} }
return new DateTimeFactory(new \DateTimeZone($timezone)); return new DateTimeFactory(new \DateTimeZone($timezone), $sunday);
} }
/** /**

View File

@@ -19,7 +19,6 @@ use App\Repository\ProjectRepository;
use App\Repository\TimesheetRepository; use App\Repository\TimesheetRepository;
use App\Timesheet\DateTimeFactory; use App\Timesheet\DateTimeFactory;
use DateTime; use DateTime;
use DateTimeZone;
use Doctrine\DBAL\Types\Types; use Doctrine\DBAL\Types\Types;
final class ProjectStatisticService final class ProjectStatisticService
@@ -132,7 +131,7 @@ final class ProjectStatisticService
*/ */
public function getProjectView(User $user, array $projects, ?DateTime $today = null): array public function getProjectView(User $user, array $projects, ?DateTime $today = null): array
{ {
$factory = new DateTimeFactory(new DateTimeZone($user->getTimezone())); $factory = DateTimeFactory::createByUser($user);
if (null === $today) { if (null === $today) {
$today = $factory->createDateTime(); $today = $factory->createDateTime();
} }

View File

@@ -9,6 +9,7 @@
namespace App\Timesheet; namespace App\Timesheet;
use App\Entity\User;
use DateTime; use DateTime;
use DateTimeZone; use DateTimeZone;
@@ -18,16 +19,26 @@ class DateTimeFactory
* @var DateTimeZone * @var DateTimeZone
*/ */
private $timezone; private $timezone;
/**
* @var bool
*/
private $startOnSunday;
public function __construct(?DateTimeZone $timezone = null) public static function createByUser(User $user): self
{
return new DateTimeFactory(new \DateTimeZone($user->getTimezone()), $user->isFirstDayOfWeekSunday());
}
public function __construct(?DateTimeZone $timezone = null, bool $startOnSunday = false)
{ {
if (null === $timezone) { if (null === $timezone) {
$timezone = new \DateTimeZone(date_default_timezone_get()); $timezone = new \DateTimeZone(date_default_timezone_get());
} }
$this->setTimezone($timezone); $this->setTimezone($timezone);
$this->startOnSunday = $startOnSunday;
} }
public function setTimezone(DateTimeZone $timezone) protected function setTimezone(DateTimeZone $timezone)
{ {
$this->timezone = $timezone; $this->timezone = $timezone;
} }
@@ -51,7 +62,20 @@ class DateTimeFactory
$date = $this->createDateTime('now'); $date = $this->createDateTime('now');
} }
return $this->createWeekDateTime($date->format('o'), $date->format('W'), 1, 0, 0, 0); $from = clone $date;
$year = $from->format('o');
$week = $from->format('W');
$firstDay = 1;
if ($this->startOnSunday) {
$from->modify('-1 week');
$year = $from->format('o');
$week = $from->format('W');
$firstDay = 7;
}
return $this->createWeekDateTime($year, $week, $firstDay, 0, 0, 0);
} }
public function getEndOfWeek(?DateTime $date = null): DateTime public function getEndOfWeek(?DateTime $date = null): DateTime
@@ -60,7 +84,9 @@ class DateTimeFactory
$date = $this->createDateTime('now'); $date = $this->createDateTime('now');
} }
return $this->createWeekDateTime($date->format('o'), $date->format('W'), 7, 23, 59, 59); $lastDay = $this->startOnSunday ? 6 : 7;
return $this->createWeekDateTime($date->format('o'), $date->format('W'), $lastDay, 23, 59, 59);
} }
public function getEndOfMonth(): DateTime public function getEndOfMonth(): DateTime

View File

@@ -13,6 +13,7 @@ use App\Entity\Activity;
use App\Entity\Project; use App\Entity\Project;
use App\Entity\User; use App\Entity\User;
use App\Repository\TimesheetRepository; use App\Repository\TimesheetRepository;
use App\Timesheet\DateTimeFactory;
use DateTime; use DateTime;
class DailyWorkingTimeChart extends SimpleWidget implements UserWidget class DailyWorkingTimeChart extends SimpleWidget implements UserWidget
@@ -30,8 +31,8 @@ class DailyWorkingTimeChart extends SimpleWidget implements UserWidget
$this->setId('DailyWorkingTimeChart'); $this->setId('DailyWorkingTimeChart');
$this->setTitle('stats.yourWorkingHours'); $this->setTitle('stats.yourWorkingHours');
$this->setOptions([ $this->setOptions([
'begin' => 'monday this week 00:00:00', 'begin' => null,
'end' => 'sunday this week 23:59:59', 'end' => null,
'color' => '', 'color' => '',
'type' => self::DEFAULT_CHART, 'type' => self::DEFAULT_CHART,
'id' => '', 'id' => '',
@@ -67,12 +68,22 @@ class DailyWorkingTimeChart extends SimpleWidget implements UserWidget
throw new \InvalidArgumentException('Widget option "user" must be an instance of ' . User::class); throw new \InvalidArgumentException('Widget option "user" must be an instance of ' . User::class);
} }
$dateTimeFactory = DateTimeFactory::createByUser($user);
if ($options['begin'] === null) {
$options['begin'] = $dateTimeFactory->getStartOfWeek();
}
if ($options['begin'] instanceof DateTime) { if ($options['begin'] instanceof DateTime) {
$begin = $options['begin']; $begin = $options['begin'];
} else { } else {
$begin = new DateTime($options['begin'], new \DateTimeZone($user->getTimezone())); $begin = new DateTime($options['begin'], new \DateTimeZone($user->getTimezone()));
} }
if ($options['end'] === null) {
$options['end'] = $dateTimeFactory->getEndOfWeek($begin);
}
if ($options['end'] instanceof DateTime) { if ($options['end'] instanceof DateTime) {
$end = $options['end']; $end = $options['end'];
} else { } else {

View File

@@ -11,6 +11,7 @@ namespace App\Widget\Type;
use App\Entity\User; use App\Entity\User;
use App\Repository\TimesheetRepository; use App\Repository\TimesheetRepository;
use App\Timesheet\DateTimeFactory;
use DateTime; use DateTime;
final class PaginatedWorkingTimeChart extends SimpleWidget implements UserWidget final class PaginatedWorkingTimeChart extends SimpleWidget implements UserWidget
@@ -54,15 +55,6 @@ final class PaginatedWorkingTimeChart extends SimpleWidget implements UserWidget
return $options; return $options;
} }
private function getDate(\DateTimeZone $timezone, $year, $week, $day, $hour, $minute, $second)
{
$now = new DateTime('now', $timezone);
$now->setISODate($year, $week, $day);
$now->setTime($hour, $minute, $second);
return $now;
}
private function getLastWeekInYear($year): int private function getLastWeekInYear($year): int
{ {
$lastWeekInYear = new DateTime(); $lastWeekInYear = new DateTime();
@@ -80,17 +72,23 @@ final class PaginatedWorkingTimeChart extends SimpleWidget implements UserWidget
throw new \InvalidArgumentException('Widget option "user" must be an instance of ' . User::class); throw new \InvalidArgumentException('Widget option "user" must be an instance of ' . User::class);
} }
$timezone = new \DateTimeZone($user->getTimezone()); $dateTimeFactory = DateTimeFactory::createByUser($user);
$weekBegin = $this->getDate($timezone, $options['year'], $options['week'], 1, 0, 0, 0); $year = $options['year'];
$weekEnd = $this->getDate($timezone, $options['year'], $options['week'], 7, 23, 59, 59); $week = $options['week'];
$lastWeekInYear = $this->getLastWeekInYear($options['year']); $weekBegin = ($dateTimeFactory->createDateTime())->setISODate($year, $week, 1)->setTime(0, 0, 0);
$lastWeekInLastYear = $this->getLastWeekInYear($options['year'] - 1); $weekEnd = ($dateTimeFactory->createDateTime())->setISODate($year, $week, 7)->setTime(23, 59, 59);
$weekBegin = $dateTimeFactory->getStartOfWeek($weekBegin);
$weekEnd = $dateTimeFactory->getEndOfWeek($weekEnd);
$lastWeekInYear = $this->getLastWeekInYear($year);
$lastWeekInLastYear = $this->getLastWeekInYear($year - 1);
$thisMonth = clone $weekBegin; $thisMonth = clone $weekBegin;
if ((int) $options['week'] === 1) { if ((int) $week === 1) {
$thisMonth = (new DateTime('now', $timezone))->setISODate($options['year'], $options['week'], 7)->setTime(0, 0, 0); $thisMonth = ($dateTimeFactory->createDateTime())->setISODate($year, $week, 1)->setTime(0, 0, 0);
} }
return [ return [
@@ -102,8 +100,8 @@ final class PaginatedWorkingTimeChart extends SimpleWidget implements UserWidget
'lastWeekInLastYear' => $lastWeekInLastYear, 'lastWeekInLastYear' => $lastWeekInLastYear,
'day' => $this->repository->getStatistic( 'day' => $this->repository->getStatistic(
'duration', 'duration',
new DateTime('00:00:00', $timezone), $dateTimeFactory->createDateTime('00:00:00'),
new DateTime('23:59:59', $timezone), $dateTimeFactory->createDateTime('23:59:59'),
$user $user
), ),
'week' => $this->repository->getStatistic( 'week' => $this->repository->getStatistic(
@@ -114,14 +112,14 @@ final class PaginatedWorkingTimeChart extends SimpleWidget implements UserWidget
), ),
'month' => $this->repository->getStatistic( 'month' => $this->repository->getStatistic(
'duration', 'duration',
(clone $weekBegin)->setDate($weekBegin->format('Y'), $weekBegin->format('n'), 1)->setTime(0, 0, 0), (clone $weekBegin)->setDate((int) $weekBegin->format('Y'), (int) $weekBegin->format('n'), 1)->setTime(0, 0, 0),
(clone $weekBegin)->setDate($weekBegin->format('Y'), $weekBegin->format('n'), $weekBegin->format('t'))->setTime(23, 59, 59), (clone $weekBegin)->setDate((int) $weekBegin->format('Y'), (int) $weekBegin->format('n'), (int) $weekBegin->format('t'))->setTime(23, 59, 59),
$user $user
), ),
'year' => $this->repository->getStatistic( 'year' => $this->repository->getStatistic(
'duration', 'duration',
new DateTime(sprintf('01 january %s 00:00:00', $options['year']), $timezone), $dateTimeFactory->createDateTime(sprintf('01 january %s 00:00:00', $year)),
new DateTime(sprintf('31 december %s 23:59:59', $options['year']), $timezone), $dateTimeFactory->createDateTime(sprintf('31 december %s 23:59:59', $year)),
$user $user
), ),
]; ];

View File

@@ -440,6 +440,8 @@
{{ value|nl2br }} {{ value|nl2br }}
{% elseif '\\EmailType' in type %} {% elseif '\\EmailType' in type %}
<a href="mailto:{{ value }}">{{ value }}</a> <a href="mailto:{{ value }}">{{ value }}</a>
{% elseif '\\UrlType' in type %}
<a href="{{ value }}" target="_blank">{{ value }}</a>
{% else %} {% else %}
{{ value }} {{ value }}
{% endif %} {% endif %}

View File

@@ -21,13 +21,13 @@ class DateTimeFactoryTest extends TestCase
{ {
public const TEST_TIMEZONE = 'Europe/London'; public const TEST_TIMEZONE = 'Europe/London';
protected function createDateTimeFactory(?string $timezone = null): DateTimeFactory protected function createDateTimeFactory(?string $timezone = null, bool $sunday = false): DateTimeFactory
{ {
if (null === $timezone) { if (null === $timezone) {
return new DateTimeFactory(); return new DateTimeFactory(null, $sunday);
} }
return new DateTimeFactory(new DateTimeZone($timezone)); return new DateTimeFactory(new DateTimeZone($timezone), $sunday);
} }
public function testGetTimezone() public function testGetTimezone()
@@ -72,19 +72,28 @@ class DateTimeFactoryTest extends TestCase
$this->assertEquals(self::TEST_TIMEZONE, $dateTime->getTimezone()->getName()); $this->assertEquals(self::TEST_TIMEZONE, $dateTime->getTimezone()->getName());
} }
public function testGetStartOfWeek() public function getStartOfWeekData()
{
yield [$this->createDateTimeFactory(self::TEST_TIMEZONE), 'Monday', 23, 1];
yield [$this->createDateTimeFactory(self::TEST_TIMEZONE, false), 'Monday', 23, 1];
yield [$this->createDateTimeFactory(self::TEST_TIMEZONE, true), 'Sunday', 22, 7];
}
/**
* @dataProvider getStartOfWeekData
*/
public function testGetStartOfWeek(DateTimeFactory $sut, string $dayName, int $dayNum, int $day)
{ {
$expected = new DateTime('2018-07-26 16:47:31', new DateTimeZone(self::TEST_TIMEZONE)); $expected = new DateTime('2018-07-26 16:47:31', new DateTimeZone(self::TEST_TIMEZONE));
$sut = $this->createDateTimeFactory(self::TEST_TIMEZONE);
$dateTime = $sut->getStartOfWeek($expected); $dateTime = $sut->getStartOfWeek($expected);
$this->assertEquals(0, $dateTime->format('H')); $this->assertEquals(0, $dateTime->format('H'));
$this->assertEquals(0, $dateTime->format('i')); $this->assertEquals(0, $dateTime->format('i'));
$this->assertEquals(0, $dateTime->format('s')); $this->assertEquals(0, $dateTime->format('s'));
$this->assertEquals(23, $dateTime->format('d')); $this->assertEquals($dayNum, $dateTime->format('d'));
$this->assertEquals(1, $dateTime->format('N')); $this->assertEquals($day, $dateTime->format('N'));
$this->assertEquals('Monday', $dateTime->format('l')); $this->assertEquals($dayName, $dateTime->format('l'));
$this->assertEquals($expected->format('m'), $dateTime->format('m')); $this->assertEquals($expected->format('m'), $dateTime->format('m'));
$this->assertEquals($expected->format('Y'), $dateTime->format('Y')); $this->assertEquals($expected->format('Y'), $dateTime->format('Y'));
$this->assertEquals(self::TEST_TIMEZONE, $dateTime->getTimezone()->getName()); $this->assertEquals(self::TEST_TIMEZONE, $dateTime->getTimezone()->getName());
@@ -94,25 +103,34 @@ class DateTimeFactoryTest extends TestCase
$this->assertEquals(0, $dateTime->format('H')); $this->assertEquals(0, $dateTime->format('H'));
$this->assertEquals(0, $dateTime->format('i')); $this->assertEquals(0, $dateTime->format('i'));
$this->assertEquals(0, $dateTime->format('s')); $this->assertEquals(0, $dateTime->format('s'));
$this->assertEquals(1, $dateTime->format('N')); $this->assertEquals($day, $dateTime->format('N'));
$this->assertEquals('Monday', $dateTime->format('l')); $this->assertEquals($dayName, $dateTime->format('l'));
// month and year can be different when the week started at the end of the month // month and year can be different when the week started at the end of the month
$this->assertEquals(self::TEST_TIMEZONE, $dateTime->getTimezone()->getName()); $this->assertEquals(self::TEST_TIMEZONE, $dateTime->getTimezone()->getName());
} }
public function testGetEndOfWeek() public function getEndOfWeekData()
{
yield [$this->createDateTimeFactory(self::TEST_TIMEZONE), 'Sunday', 29, 7];
yield [$this->createDateTimeFactory(self::TEST_TIMEZONE, false), 'Sunday', 29, 7];
yield [$this->createDateTimeFactory(self::TEST_TIMEZONE, true), 'Saturday', 28, 6];
}
/**
* @dataProvider getEndOfWeekData
*/
public function testGetEndOfWeek(DateTimeFactory $sut, string $dayName, int $dayNum, int $day)
{ {
$expected = new DateTime('2018-07-26 16:47:31', new DateTimeZone(self::TEST_TIMEZONE)); $expected = new DateTime('2018-07-26 16:47:31', new DateTimeZone(self::TEST_TIMEZONE));
$sut = $this->createDateTimeFactory(self::TEST_TIMEZONE);
$dateTime = $sut->getEndOfWeek($expected); $dateTime = $sut->getEndOfWeek($expected);
$this->assertEquals(23, $dateTime->format('H')); $this->assertEquals(23, $dateTime->format('H'));
$this->assertEquals(59, $dateTime->format('i')); $this->assertEquals(59, $dateTime->format('i'));
$this->assertEquals(59, $dateTime->format('s')); $this->assertEquals(59, $dateTime->format('s'));
$this->assertEquals(29, $dateTime->format('d')); $this->assertEquals($dayNum, $dateTime->format('d'));
$this->assertEquals(7, $dateTime->format('N')); $this->assertEquals($day, $dateTime->format('N'));
$this->assertEquals('Sunday', $dateTime->format('l')); $this->assertEquals($dayName, $dateTime->format('l'));
$this->assertEquals('07', $dateTime->format('m')); $this->assertEquals('07', $dateTime->format('m'));
$this->assertEquals('2018', $dateTime->format('Y')); $this->assertEquals('2018', $dateTime->format('Y'));
$this->assertEquals(self::TEST_TIMEZONE, $dateTime->getTimezone()->getName()); $this->assertEquals(self::TEST_TIMEZONE, $dateTime->getTimezone()->getName());
@@ -122,8 +140,8 @@ class DateTimeFactoryTest extends TestCase
$this->assertEquals(23, $dateTime->format('H')); $this->assertEquals(23, $dateTime->format('H'));
$this->assertEquals(59, $dateTime->format('i')); $this->assertEquals(59, $dateTime->format('i'));
$this->assertEquals(59, $dateTime->format('s')); $this->assertEquals(59, $dateTime->format('s'));
$this->assertEquals(7, $dateTime->format('N')); $this->assertEquals($day, $dateTime->format('N'));
$this->assertEquals('Sunday', $dateTime->format('l')); $this->assertEquals($dayName, $dateTime->format('l'));
// month and year can be different when the week started at the end of the month // month and year can be different when the week started at the end of the month
$this->assertEquals(self::TEST_TIMEZONE, $dateTime->getTimezone()->getName()); $this->assertEquals(self::TEST_TIMEZONE, $dateTime->getTimezone()->getName());
} }

View File

@@ -49,8 +49,8 @@ class DailyWorkingTimeChartTest extends TestCase
self::assertInstanceOf(AbstractWidgetType::class, $sut); self::assertInstanceOf(AbstractWidgetType::class, $sut);
self::assertEquals('DailyWorkingTimeChart', $sut->getId()); self::assertEquals('DailyWorkingTimeChart', $sut->getId());
self::assertEquals('stats.yourWorkingHours', $sut->getTitle()); self::assertEquals('stats.yourWorkingHours', $sut->getTitle());
self::assertEquals('monday this week 00:00:00', $sut->getOption('begin', 'xxx')); self::assertNull($sut->getOption('begin', 'xxx'));
self::assertEquals('sunday this week 23:59:59', $sut->getOption('end', 'xxx')); self::assertNull($sut->getOption('end', 'xxx'));
self::assertEquals('', $sut->getOption('color', 'xxx')); self::assertEquals('', $sut->getOption('color', 'xxx'));
self::assertInstanceOf(User::class, $sut->getOption('user', 'xxx')); self::assertInstanceOf(User::class, $sut->getOption('user', 'xxx'));
self::assertEquals('bar', $sut->getOption('type', 'xxx')); self::assertEquals('bar', $sut->getOption('type', 'xxx'));