added support for Ante meridiem and Post meridiem format (#615)
This commit is contained in:
@@ -174,7 +174,7 @@ $(function() {
|
|||||||
$(this).daterangepicker({
|
$(this).daterangepicker({
|
||||||
singleDatePicker: true,
|
singleDatePicker: true,
|
||||||
timePicker: true,
|
timePicker: true,
|
||||||
timePicker24Hour: true,
|
timePicker24Hour: $.kimai.settings['twentyFourHours'],
|
||||||
showDropdowns: true,
|
showDropdowns: true,
|
||||||
autoUpdateInput: false,
|
autoUpdateInput: false,
|
||||||
locale: {
|
locale: {
|
||||||
@@ -324,7 +324,8 @@ $(function() {
|
|||||||
lastMonth: 'Last month',
|
lastMonth: 'Last month',
|
||||||
thisYear: 'This year',
|
thisYear: 'This year',
|
||||||
lastYear: 'Last year',
|
lastYear: 'Last year',
|
||||||
customRange: 'Custom range'
|
customRange: 'Custom range',
|
||||||
|
twentyFourHours: true
|
||||||
};
|
};
|
||||||
|
|
||||||
// once initialized, here are all values
|
// once initialized, here are all values
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"build/app.js": "/build/app.js?039b40a3efd42cd2846a",
|
"build/app.js": "/build/app.js?71d3a396b643a587cabb",
|
||||||
"build/app.css": "/build/app.css?82e5f119685e8c17fbafec374d7a01e0",
|
"build/app.css": "/build/app.css?e067eb7af0fed5e92ada44bb38e51cb5",
|
||||||
"build/images/blue@2x.png": "/build/images/blue@2x.png?2694acfd",
|
"build/images/blue@2x.png": "/build/images/blue@2x.png?2694acfd",
|
||||||
"build/images/blue.png": "/build/images/blue.png?96f8a905",
|
"build/images/blue.png": "/build/images/blue.png?96f8a905",
|
||||||
"build/fonts/fa-solid-900.woff2": "/build/fonts/fa-solid-900.woff2?e8a92a29",
|
"build/fonts/fa-solid-900.woff2": "/build/fonts/fa-solid-900.woff2?e8a92a29",
|
||||||
|
|||||||
@@ -960,6 +960,11 @@ class KimaiImporterCommand extends Command
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (empty($oldRecord['end']) || $oldRecord['end'] === 0) {
|
||||||
|
$io->error('Cannot import running timesheet record, skipping: ' . $oldRecord['timeEntryID']);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$duration = $oldRecord['end'] - $oldRecord['start'];
|
$duration = $oldRecord['end'] - $oldRecord['start'];
|
||||||
|
|
||||||
// ----------------------- unknown user, damned missing data integrity in Kimai v1 -----------------------
|
// ----------------------- unknown user, damned missing data integrity in Kimai v1 -----------------------
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ class Configuration implements ConfigurationInterface
|
|||||||
$node = $builder->root('languages');
|
$node = $builder->root('languages');
|
||||||
|
|
||||||
$node
|
$node
|
||||||
|
->useAttributeAsKey('name', false) // see https://github.com/symfony/symfony/issues/18988
|
||||||
->arrayPrototype()
|
->arrayPrototype()
|
||||||
->children()
|
->children()
|
||||||
->scalarNode('date_time_type')->defaultValue('yyyy-MM-dd HH:mm')->end() // for DateTimeType
|
->scalarNode('date_time_type')->defaultValue('yyyy-MM-dd HH:mm')->end() // for DateTimeType
|
||||||
@@ -204,6 +205,8 @@ class Configuration implements ConfigurationInterface
|
|||||||
->scalarNode('date')->defaultValue('Y-m-d')->end() // for display via twig
|
->scalarNode('date')->defaultValue('Y-m-d')->end() // for display via twig
|
||||||
->scalarNode('date_time')->defaultValue('m-d H:i')->end() // for display via twig
|
->scalarNode('date_time')->defaultValue('m-d H:i')->end() // for display via twig
|
||||||
->scalarNode('duration')->defaultValue('%%h:%%m h')->end() // for display via twig
|
->scalarNode('duration')->defaultValue('%%h:%%m h')->end() // for display via twig
|
||||||
|
->scalarNode('time')->defaultValue('H:i')->end() // for display via twig
|
||||||
|
->booleanNode('24_hours')->defaultTrue()->end() // for DateTimeType JS component
|
||||||
->end()
|
->end()
|
||||||
->end()
|
->end()
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -19,9 +19,25 @@ use Twig\TwigFilter;
|
|||||||
class DateExtensions extends \Twig_Extension
|
class DateExtensions extends \Twig_Extension
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var LocaleSettings
|
* @var LocaleSettings|null
|
||||||
*/
|
*/
|
||||||
protected $localeSettings;
|
protected $localeSettings = null;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $dateFormat = null;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $dateTimeFormat = null;
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $timeFormat = null;
|
||||||
|
/**
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
protected $isTwentyFourHour = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param LocaleSettings $localeSettings
|
* @param LocaleSettings $localeSettings
|
||||||
@@ -42,6 +58,7 @@ class DateExtensions extends \Twig_Extension
|
|||||||
new TwigFilter('date_time', [$this, 'dateTime']),
|
new TwigFilter('date_time', [$this, 'dateTime']),
|
||||||
new TwigFilter('date_format', [$this, 'dateFormat']),
|
new TwigFilter('date_format', [$this, 'dateFormat']),
|
||||||
new TwigFilter('time', [$this, 'time']),
|
new TwigFilter('time', [$this, 'time']),
|
||||||
|
new TwigFilter('hour24', [$this, 'hour24']),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,9 +68,11 @@ class DateExtensions extends \Twig_Extension
|
|||||||
*/
|
*/
|
||||||
public function dateShort(DateTime $date)
|
public function dateShort(DateTime $date)
|
||||||
{
|
{
|
||||||
$format = $this->localeSettings->getDateFormat();
|
if (null === $this->dateFormat) {
|
||||||
|
$this->dateFormat = $this->localeSettings->getDateFormat();
|
||||||
|
}
|
||||||
|
|
||||||
return date_format($date, $format);
|
return date_format($date, $this->dateFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,9 +81,11 @@ class DateExtensions extends \Twig_Extension
|
|||||||
*/
|
*/
|
||||||
public function dateTime(DateTime $date)
|
public function dateTime(DateTime $date)
|
||||||
{
|
{
|
||||||
$format = $this->localeSettings->getDateTimeFormat();
|
if (null === $this->dateTimeFormat) {
|
||||||
|
$this->dateTimeFormat = $this->localeSettings->getDateTimeFormat();
|
||||||
|
}
|
||||||
|
|
||||||
return date_format($date, $format);
|
return date_format($date, $this->dateTimeFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,7 +104,11 @@ class DateExtensions extends \Twig_Extension
|
|||||||
*/
|
*/
|
||||||
public function time(DateTime $date)
|
public function time(DateTime $date)
|
||||||
{
|
{
|
||||||
return date_format($date, 'H:i');
|
if (null === $this->timeFormat) {
|
||||||
|
$this->timeFormat = $this->localeSettings->getTimeFormat();
|
||||||
|
}
|
||||||
|
|
||||||
|
return date_format($date, $this->timeFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,4 +119,22 @@ class DateExtensions extends \Twig_Extension
|
|||||||
{
|
{
|
||||||
return 'month.' . $date->format('n');
|
return 'month.' . $date->format('n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $twentyFour
|
||||||
|
* @param mixed $twelveHour
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function hour24($twentyFour, $twelveHour)
|
||||||
|
{
|
||||||
|
if (null === $this->isTwentyFourHour) {
|
||||||
|
$this->isTwentyFourHour = $this->localeSettings->isTwentyFourHours();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true === $this->isTwentyFourHour) {
|
||||||
|
return $twentyFour;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $twelveHour;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,17 @@ class LocaleSettings
|
|||||||
return $this->getConfigByLocaleAndKey('date', $locale);
|
return $this->getConfigByLocaleAndKey('date', $locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the locale specific time format, which should be used in combination with the twig filter "|time".
|
||||||
|
*
|
||||||
|
* @param null|string $locale
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTimeFormat(?string $locale = null): string
|
||||||
|
{
|
||||||
|
return $this->getConfigByLocaleAndKey('time', $locale);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the locale specific datetime format, which should be used in combination with the twig filter "|date".
|
* Returns the locale specific datetime format, which should be used in combination with the twig filter "|date".
|
||||||
*
|
*
|
||||||
@@ -133,6 +144,17 @@ class LocaleSettings
|
|||||||
return $this->getConfigByLocaleAndKey('duration', $locale);
|
return $this->getConfigByLocaleAndKey('duration', $locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this locale uses the 24 hour format.
|
||||||
|
*
|
||||||
|
* @param null|string $locale
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isTwentyFourHours(?string $locale = null): bool
|
||||||
|
{
|
||||||
|
return $this->getConfigByLocaleAndKey('24_hours', $locale);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $key
|
* @param string $key
|
||||||
* @param null|string $locale
|
* @param null|string $locale
|
||||||
|
|||||||
@@ -52,17 +52,17 @@
|
|||||||
<td class="text-nowrap {{ tables.data_table_column_class(tableName, columns, 'date') }}">{{ entry.begin|date_short }}</td>
|
<td class="text-nowrap {{ tables.data_table_column_class(tableName, columns, 'date') }}">{{ entry.begin|date_short }}</td>
|
||||||
|
|
||||||
{% if not duration_only %}
|
{% if not duration_only %}
|
||||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'starttime') }}">{{ entry.begin|time }}</td>
|
<td class="text-nowrap {{ tables.data_table_column_class(tableName, columns, 'starttime') }}">{{ entry.begin|time }}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if entry.end %}
|
{% if entry.end %}
|
||||||
{% if not duration_only %}
|
{% if not duration_only %}
|
||||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'endtime') }}">{{ entry.end|time }}</td>
|
<td class="text-nowrap {{ tables.data_table_column_class(tableName, columns, 'endtime') }}">{{ entry.end|time }}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td class="text-nowrap {{ tables.data_table_column_class(tableName, columns, 'duration') }}">{{ entry.duration|duration }}</td>
|
<td class="text-nowrap {{ tables.data_table_column_class(tableName, columns, 'duration') }}">{{ entry.duration|duration }}</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if not duration_only %}
|
{% if not duration_only %}
|
||||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'endtime') }}">‐</td>
|
<td class="text-nowrap {{ tables.data_table_column_class(tableName, columns, 'endtime') }}">‐</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td class="text-nowrap {{ tables.data_table_column_class(tableName, columns, 'duration') }}"><i>{{ entry|duration }}</i></td>
|
<td class="text-nowrap {{ tables.data_table_column_class(tableName, columns, 'duration') }}"><i>{{ entry|duration }}</i></td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
@@ -122,7 +122,8 @@
|
|||||||
thisMonth: '{{ 'daterangepicker.thisMonth'|trans({}, 'daterangepicker') }}',
|
thisMonth: '{{ 'daterangepicker.thisMonth'|trans({}, 'daterangepicker') }}',
|
||||||
lastYear: '{{ 'daterangepicker.lastYear'|trans({}, 'daterangepicker') }}',
|
lastYear: '{{ 'daterangepicker.lastYear'|trans({}, 'daterangepicker') }}',
|
||||||
thisYear: '{{ 'daterangepicker.thisYear'|trans({}, 'daterangepicker') }}',
|
thisYear: '{{ 'daterangepicker.thisYear'|trans({}, 'daterangepicker') }}',
|
||||||
customRange: '{{ 'daterangepicker.customRange'|trans({}, 'daterangepicker') }}'
|
customRange: '{{ 'daterangepicker.customRange'|trans({}, 'daterangepicker') }}',
|
||||||
|
twentyFourHours: {{ 'true'|hour24('false') }}
|
||||||
});
|
});
|
||||||
{# $.kimai.pauseRecord('li.messages-menu ul.menu li'); #}
|
{# $.kimai.pauseRecord('li.messages-menu ul.menu li'); #}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -91,12 +91,12 @@
|
|||||||
<td class="text-nowrap {{ tables.data_table_column_class(tableName, columns, 'date') }}">{{ entry.begin|date_short }}</td>
|
<td class="text-nowrap {{ tables.data_table_column_class(tableName, columns, 'date') }}">{{ entry.begin|date_short }}</td>
|
||||||
|
|
||||||
{% if not duration_only %}
|
{% if not duration_only %}
|
||||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'starttime') }}">{{ entry.begin|time }}</td>
|
<td class="text-nowrap {{ tables.data_table_column_class(tableName, columns, 'starttime') }}">{{ entry.begin|time }}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if entry.end %}
|
{% if entry.end %}
|
||||||
{% if not duration_only %}
|
{% if not duration_only %}
|
||||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'endtime') }}">{{ entry.end|time }}</td>
|
<td class="text-nowrap {{ tables.data_table_column_class(tableName, columns, 'endtime') }}">{{ entry.end|time }}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td class="text-nowrap {{ tables.data_table_column_class(tableName, columns, 'duration') }}">{{ entry.duration|duration }}</td>
|
<td class="text-nowrap {{ tables.data_table_column_class(tableName, columns, 'duration') }}">{{ entry.duration|duration }}</td>
|
||||||
{% if is_granted('view_rate', entry) %}
|
{% if is_granted('view_rate', entry) %}
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if not duration_only %}
|
{% if not duration_only %}
|
||||||
<td class="{{ tables.data_table_column_class(tableName, columns, 'endtime') }}">‐</td>
|
<td class="text-nowrap {{ tables.data_table_column_class(tableName, columns, 'endtime') }}">‐</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td class="text-nowrap {{ tables.data_table_column_class(tableName, columns, 'duration') }}"><i>{{ entry|duration }}</i></td>
|
<td class="text-nowrap {{ tables.data_table_column_class(tableName, columns, 'duration') }}"><i>{{ entry|duration }}</i></td>
|
||||||
{% if is_granted('view_rate', entry) %}
|
{% if is_granted('view_rate', entry) %}
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ abstract class AbstractRendererTest extends KernelTestCase
|
|||||||
$languages = [
|
$languages = [
|
||||||
'en' => [
|
'en' => [
|
||||||
'date' => 'Y.m.d',
|
'date' => 'Y.m.d',
|
||||||
'duration' => '%h:%m h'
|
'duration' => '%h:%m h',
|
||||||
|
'time' => 'H:i',
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,8 @@ abstract class AbstractRendererTest extends KernelTestCase
|
|||||||
$languages = [
|
$languages = [
|
||||||
'en' => [
|
'en' => [
|
||||||
'date' => 'Y.m.d',
|
'date' => 'Y.m.d',
|
||||||
'duration' => '%h:%m h'
|
'duration' => '%h:%m h',
|
||||||
|
'time' => 'H:i',
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class DateExtensionsTest extends TestCase
|
|||||||
|
|
||||||
public function testGetFilters()
|
public function testGetFilters()
|
||||||
{
|
{
|
||||||
$filters = ['month_name', 'date_short', 'date_time', 'date_format', 'time'];
|
$filters = ['month_name', 'date_short', 'date_time', 'date_format', 'time', 'hour24'];
|
||||||
$sut = $this->getSut('de', []);
|
$sut = $this->getSut('de', []);
|
||||||
$twigFilters = $sut->getFilters();
|
$twigFilters = $sut->getFilters();
|
||||||
$this->assertCount(count($filters), $twigFilters);
|
$this->assertCount(count($filters), $twigFilters);
|
||||||
@@ -132,7 +132,20 @@ class DateExtensionsTest extends TestCase
|
|||||||
$time = new \DateTime('2016-06-23');
|
$time = new \DateTime('2016-06-23');
|
||||||
$time->setTime(17, 53, 23);
|
$time->setTime(17, 53, 23);
|
||||||
|
|
||||||
$sut = $this->getSut('en', []);
|
$sut = $this->getSut('en', ['en' => ['time' => 'H:i']]);
|
||||||
$this->assertEquals('17:53', $sut->time($time));
|
$this->assertEquals('17:53', $sut->time($time));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testHour24()
|
||||||
|
{
|
||||||
|
$sut = $this->getSut('en', [
|
||||||
|
'en' => ['24_hours' => false],
|
||||||
|
]);
|
||||||
|
$this->assertEquals('bar', $sut->hour24('foo', 'bar'));
|
||||||
|
|
||||||
|
$sut = $this->getSut('de', [
|
||||||
|
'de' => ['24_hours' => true],
|
||||||
|
]);
|
||||||
|
$this->assertEquals('foo', $sut->hour24('foo', 'bar'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user