22 changed files with 780 additions and 31 deletions
@ -0,0 +1,22 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2023 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<mat-form-field appearance="outline" subscriptSizing="dynamic" style="width: 100%;"> |
|||
<mat-select [formControl]="cssUnitFormControl"> |
|||
<mat-option *ngFor="let cssUnit of cssUnitsList" [value]="cssUnit">{{ cssUnit }}</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
@ -0,0 +1,82 @@ |
|||
///
|
|||
/// Copyright © 2016-2023 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { Component, forwardRef, Input, OnInit } from '@angular/core'; |
|||
import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; |
|||
import { cssUnit, cssUnits } from '@home/components/widget/config/widget-settings.models'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-css-unit-select', |
|||
templateUrl: './css-unit-select.component.html', |
|||
styleUrls: [], |
|||
providers: [ |
|||
{ |
|||
provide: NG_VALUE_ACCESSOR, |
|||
useExisting: forwardRef(() => CssUnitSelectComponent), |
|||
multi: true |
|||
} |
|||
] |
|||
}) |
|||
export class CssUnitSelectComponent implements OnInit, ControlValueAccessor { |
|||
|
|||
@Input() |
|||
disabled: boolean; |
|||
|
|||
cssUnitsList = cssUnits; |
|||
|
|||
cssUnitFormControl: UntypedFormControl; |
|||
|
|||
modelValue: cssUnit; |
|||
|
|||
private propagateChange = null; |
|||
|
|||
constructor() {} |
|||
|
|||
ngOnInit(): void { |
|||
this.cssUnitFormControl = new UntypedFormControl(); |
|||
this.cssUnitFormControl.valueChanges.subscribe((value: cssUnit) => { |
|||
this.updateModel(value); |
|||
}); |
|||
} |
|||
|
|||
registerOnChange(fn: any): void { |
|||
this.propagateChange = fn; |
|||
} |
|||
|
|||
registerOnTouched(fn: any): void { |
|||
} |
|||
|
|||
setDisabledState(isDisabled: boolean): void { |
|||
this.disabled = isDisabled; |
|||
if (this.disabled) { |
|||
this.cssUnitFormControl.disable(); |
|||
} else { |
|||
this.cssUnitFormControl.enable(); |
|||
} |
|||
} |
|||
|
|||
writeValue(value: cssUnit): void { |
|||
this.modelValue = value; |
|||
this.cssUnitFormControl.patchValue(this.modelValue, {emitEvent: false}); |
|||
} |
|||
|
|||
updateModel(value: cssUnit): void { |
|||
if (this.modelValue !== value) { |
|||
this.modelValue = value; |
|||
this.propagateChange(this.modelValue); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,26 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2023 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<mat-form-field appearance="outline" subscriptSizing="dynamic" style="width: 100%;"> |
|||
<mat-select [formControl]="dateFormatFormControl" [compareWith]="dateFormatsCompare" > |
|||
<mat-option *ngFor="let dateFormat of dateFormatList" [value]="dateFormat">{{ dateFormatDisplayValue(dateFormat) }}</mat-option> |
|||
</mat-select> |
|||
<button #customFormatButton |
|||
*ngIf="dateFormatFormControl.value?.custom" matSuffix mat-icon-button (click)="openDateFormatSettingsPopup($event, customFormatButton)"> |
|||
<tb-icon>edit</tb-icon> |
|||
</button> |
|||
</mat-form-field> |
|||
@ -0,0 +1,148 @@ |
|||
///
|
|||
/// Copyright © 2016-2023 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { Component, forwardRef, Input, OnInit, Renderer2, ViewChild, ViewContainerRef } from '@angular/core'; |
|||
import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormControl } from '@angular/forms'; |
|||
import { |
|||
compareDateFormats, |
|||
dateFormats, |
|||
DateFormatSettings |
|||
} from '@home/components/widget/config/widget-settings.models'; |
|||
import { TranslateService } from '@ngx-translate/core'; |
|||
import { DatePipe } from '@angular/common'; |
|||
import { MatButton } from '@angular/material/button'; |
|||
import { TbPopoverService } from '@shared/components/popover.service'; |
|||
import { deepClone } from '@core/utils'; |
|||
import { |
|||
DateFormatSettingsPanelComponent |
|||
} from '@home/components/widget/lib/settings/common/date-format-settings-panel.component'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-date-format-select', |
|||
templateUrl: './date-format-select.component.html', |
|||
styleUrls: [], |
|||
providers: [ |
|||
{ |
|||
provide: NG_VALUE_ACCESSOR, |
|||
useExisting: forwardRef(() => DateFormatSelectComponent), |
|||
multi: true |
|||
} |
|||
] |
|||
}) |
|||
export class DateFormatSelectComponent implements OnInit, ControlValueAccessor { |
|||
|
|||
@ViewChild('customFormatButton', {static: false}) |
|||
customFormatButton: MatButton; |
|||
|
|||
@Input() |
|||
disabled: boolean; |
|||
|
|||
dateFormatList = dateFormats; |
|||
|
|||
dateFormatsCompare = compareDateFormats; |
|||
|
|||
dateFormatFormControl: UntypedFormControl; |
|||
|
|||
modelValue: DateFormatSettings; |
|||
|
|||
private propagateChange = null; |
|||
|
|||
private formatCache: {[format: string]: string} = {}; |
|||
|
|||
constructor(private translate: TranslateService, |
|||
private date: DatePipe, |
|||
private popoverService: TbPopoverService, |
|||
private renderer: Renderer2, |
|||
private viewContainerRef: ViewContainerRef) {} |
|||
|
|||
ngOnInit(): void { |
|||
this.dateFormatFormControl = new UntypedFormControl(); |
|||
this.dateFormatFormControl.valueChanges.subscribe((value: DateFormatSettings) => { |
|||
this.updateModel(value); |
|||
if (value?.custom) { |
|||
setTimeout(() => { |
|||
this.openDateFormatSettingsPopup(null, this.customFormatButton); |
|||
}, 0); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
registerOnChange(fn: any): void { |
|||
this.propagateChange = fn; |
|||
} |
|||
|
|||
registerOnTouched(fn: any): void { |
|||
} |
|||
|
|||
setDisabledState(isDisabled: boolean): void { |
|||
this.disabled = isDisabled; |
|||
if (this.disabled) { |
|||
this.dateFormatFormControl.disable(); |
|||
} else { |
|||
this.dateFormatFormControl.enable(); |
|||
} |
|||
} |
|||
|
|||
writeValue(value: DateFormatSettings): void { |
|||
this.modelValue = value; |
|||
this.dateFormatFormControl.patchValue(this.modelValue, {emitEvent: false}); |
|||
} |
|||
|
|||
updateModel(value: DateFormatSettings): void { |
|||
if (!compareDateFormats(this.modelValue, value)) { |
|||
this.modelValue = value; |
|||
this.propagateChange(this.modelValue); |
|||
} |
|||
} |
|||
|
|||
dateFormatDisplayValue(value: DateFormatSettings): string { |
|||
if (value.custom) { |
|||
return this.translate.instant('date.custom-date'); |
|||
} else if (value.lastUpdateAgo) { |
|||
return this.translate.instant('date.last-update-n-ago'); |
|||
} else { |
|||
if (!this.formatCache[value.format]) { |
|||
this.formatCache[value.format] = this.date.transform(Date.now(), value.format); |
|||
} |
|||
return this.formatCache[value.format]; |
|||
} |
|||
} |
|||
|
|||
openDateFormatSettingsPopup($event: Event, matButton: MatButton) { |
|||
if ($event) { |
|||
$event.stopPropagation(); |
|||
} |
|||
const trigger = matButton._elementRef.nativeElement; |
|||
if (this.popoverService.hasPopover(trigger)) { |
|||
this.popoverService.hidePopover(trigger); |
|||
} else { |
|||
const ctx: any = { |
|||
dateFormat: deepClone(this.modelValue) |
|||
}; |
|||
const dateFormatSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, |
|||
this.viewContainerRef, DateFormatSettingsPanelComponent, 'top', true, null, |
|||
ctx, |
|||
{}, |
|||
{}, {}, true); |
|||
dateFormatSettingsPanelPopover.tbComponentRef.instance.popover = dateFormatSettingsPanelPopover; |
|||
dateFormatSettingsPanelPopover.tbComponentRef.instance.dateFormatApplied.subscribe((dateFormat) => { |
|||
dateFormatSettingsPanelPopover.hide(); |
|||
this.modelValue = dateFormat; |
|||
this.propagateChange(this.modelValue); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2023 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<div class="tb-date-format-settings-panel"> |
|||
<div class="tb-date-format-settings-title" translate>date.custom-date</div> |
|||
<div class="tb-form-row no-border no-padding"> |
|||
<div class="fixed-title-width" translate>date.format</div> |
|||
<mat-form-field class="tb-date-format-input" required fxFlex appearance="outline" subscriptSizing="dynamic"> |
|||
<input matInput [formControl]="dateFormatFormControl" placeholder="{{ 'widget-config.set' | translate }}"> |
|||
<div matSuffix tb-help-popup="date/date-format" [tb-help-popup-style]="{width: '800px'}"></div> |
|||
</mat-form-field> |
|||
</div> |
|||
<mat-divider></mat-divider> |
|||
<div class="tb-form-row no-border no-padding date-format-preview"> |
|||
<div class="fixed-title-width" translate>date.preview</div> |
|||
<div class="preview-text" fxFlex>{{ previewText }}</div> |
|||
</div> |
|||
<div class="tb-date-format-settings-panel-buttons"> |
|||
<button mat-button |
|||
color="primary" |
|||
type="button" |
|||
(click)="cancel()"> |
|||
{{ 'action.cancel' | translate }} |
|||
</button> |
|||
<button mat-raised-button |
|||
color="primary" |
|||
type="button" |
|||
(click)="applyDateFormat()" |
|||
[disabled]="dateFormatFormControl.invalid || !dateFormatFormControl.dirty"> |
|||
{{ 'action.apply' | translate }} |
|||
</button> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,67 @@ |
|||
/** |
|||
* Copyright © 2016-2023 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
|
|||
@import '../../../../../../../../scss/constants'; |
|||
|
|||
.tb-date-format-settings-panel { |
|||
width: 500px; |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 16px; |
|||
@media #{$mat-xs} { |
|||
width: 90vw; |
|||
} |
|||
.tb-date-format-settings-title { |
|||
font-size: 16px; |
|||
font-weight: 500; |
|||
line-height: 24px; |
|||
letter-spacing: 0.25px; |
|||
color: rgba(0, 0, 0, 0.87); |
|||
} |
|||
.tb-form-row { |
|||
.fixed-title-width { |
|||
min-width: 120px; |
|||
} |
|||
&.date-format-preview { |
|||
align-items: flex-start; |
|||
.preview-text { |
|||
font-size: 14px; |
|||
font-style: normal; |
|||
font-weight: 400; |
|||
line-height: 20px; |
|||
letter-spacing: 0.2px; |
|||
color: rgba(0, 0, 0, 0.38); |
|||
} |
|||
} |
|||
.mat-mdc-form-field.tb-date-format-input { |
|||
.mat-mdc-text-field-wrapper.mdc-text-field--outlined { |
|||
.mat-mdc-form-field-icon-suffix { |
|||
display: flex; |
|||
align-items: center; |
|||
line-height: normal; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
.tb-date-format-settings-panel-buttons { |
|||
height: 60px; |
|||
display: flex; |
|||
flex-direction: row; |
|||
gap: 16px; |
|||
justify-content: flex-end; |
|||
align-items: flex-end; |
|||
} |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
///
|
|||
/// Copyright © 2016-2023 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; |
|||
import { PageComponent } from '@shared/components/page.component'; |
|||
import { DateFormatSettings } from '@home/components/widget/config/widget-settings.models'; |
|||
import { TbPopoverComponent } from '@shared/components/popover.component'; |
|||
import { UntypedFormControl, Validators } from '@angular/forms'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { DatePipe } from '@angular/common'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-date-format-settings-panel', |
|||
templateUrl: './date-format-settings-panel.component.html', |
|||
providers: [], |
|||
styleUrls: ['./date-format-settings-panel.component.scss'], |
|||
encapsulation: ViewEncapsulation.None |
|||
}) |
|||
export class DateFormatSettingsPanelComponent extends PageComponent implements OnInit { |
|||
|
|||
@Input() |
|||
dateFormat: DateFormatSettings; |
|||
|
|||
@Input() |
|||
popover: TbPopoverComponent<DateFormatSettingsPanelComponent>; |
|||
|
|||
@Output() |
|||
dateFormatApplied = new EventEmitter<DateFormatSettings>(); |
|||
|
|||
dateFormatFormControl: UntypedFormControl; |
|||
|
|||
previewText = ''; |
|||
|
|||
constructor(private date: DatePipe, |
|||
protected store: Store<AppState>) { |
|||
super(store); |
|||
} |
|||
|
|||
ngOnInit(): void { |
|||
this.dateFormatFormControl = new UntypedFormControl(this.dateFormat.format, [Validators.required]); |
|||
this.dateFormatFormControl.valueChanges.subscribe((value: string) => { |
|||
this.previewText = this.date.transform(Date.now(), value); |
|||
}); |
|||
this.previewText = this.date.transform(Date.now(), this.dateFormat.format); |
|||
} |
|||
|
|||
cancel() { |
|||
this.popover?.hide(); |
|||
} |
|||
|
|||
applyDateFormat() { |
|||
this.dateFormat.format = this.dateFormatFormControl.value; |
|||
this.dateFormatApplied.emit(this.dateFormat); |
|||
} |
|||
|
|||
} |
|||
@ -0,0 +1,88 @@ |
|||
#### Pre-defined format options |
|||
|
|||
| Option | Equivalent to | Examples (given in `en-US` locale) | |
|||
|------------|---------------------------------|-----------------------------------------------| |
|||
| short | M/d/yy, h:mm a | 6/15/15, 9:03 AM | |
|||
| medium | MMM d, y, h:mm:ss a | Jun 15, 2015, 9:03:01 AM | |
|||
| long | MMMM d, y, h:mm:ss a z | June 15, 2015 at 9:03:01 AM GMT+1 | |
|||
| full | EEEE, MMMM d, y, h:mm:ss a zzzz | Monday, June 15, 2015 at 9:03:01 AM GMT+01:00 | |
|||
| shortDate | M/d/yy | 6/15/15 | |
|||
| mediumDate | MMM d, y | Jun 15, 2015 | |
|||
| longDate | MMMM d, y | June 15, 2015 | |
|||
| fullDate | EEEE, MMMM d, y | Monday, June 15, 2015 | |
|||
| shortTime | h:mm a | 9:03 AM | |
|||
| mediumTime | h:mm:ss a | 9:03:01 AM | |
|||
| longTime | h:mm:ss a z | 9:03:01 AM GMT+1 | |
|||
| fullTime | h:mm:ss a zzzz | 9:03:01 AM GMT+01:00 | |
|||
|
|||
#### Custom format options |
|||
|
|||
You can construct a format string using symbols to specify the components |
|||
of a date-time value, as described in the following table. |
|||
Format details depend on the locale. |
|||
Fields marked with (*) are only available in the extra data set for the given locale. |
|||
|
|||
| Field type | Format | Description | Example Value | |
|||
|---------------------|-------------|--------------------------------------------------------------|------------------------------------------------------------| |
|||
| Era | G, GG & GGG | Abbreviated | AD | |
|||
| | GGGG | Wide | Anno Domini | |
|||
| | GGGGG | Narrow | A | |
|||
| Year | y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | |
|||
| | yy | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | |
|||
| | yyy | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | |
|||
| | yyyy | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | |
|||
| Week-numbering year | Y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 | |
|||
| | YY | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 | |
|||
| | YYY | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 | |
|||
| | YYYY | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 | |
|||
| Month | M | Numeric: 1 digit | 9, 12 | |
|||
| | MM | Numeric: 2 digits + zero padded | 09, 12 | |
|||
| | MMM | Abbreviated | Sep | |
|||
| | MMMM | Wide | September | |
|||
| | MMMMM | Narrow | S | |
|||
| Month standalone | L | Numeric: 1 digit | 9, 12 | |
|||
| | LL | Numeric: 2 digits + zero padded | 09, 12 | |
|||
| | LLL | Abbreviated | Sep | |
|||
| | LLLL | Wide | September | |
|||
| | LLLLL | Narrow | S | |
|||
| Week of year | w | Numeric: minimum digits | 1... 53 | |
|||
| | ww | Numeric: 2 digits + zero padded | 01... 53 | |
|||
| Week of month | W | Numeric: 1 digit | 1... 5 | |
|||
| Day of month | d | Numeric: minimum digits | 1 | |
|||
| | dd | Numeric: 2 digits + zero padded | 01 | |
|||
| Week day | E, EE & EEE | Abbreviated | Tue | |
|||
| | EEEE | Wide | Tuesday | |
|||
| | EEEEE | Narrow | T | |
|||
| | EEEEEE | Short | Tu | |
|||
| Week day standalone | c, cc | Numeric: 1 digit | 2 | |
|||
| | ccc | Abbreviated | Tue | |
|||
| | cccc | Wide | Tuesday | |
|||
| | ccccc | Narrow | T | |
|||
| | cccccc | Short | Tu | |
|||
| Period | a, aa & aaa | Abbreviated | am/pm or AM/PM | |
|||
| | aaaa | Wide (fallback to `a` when missing) | ante meridiem/post meridiem | |
|||
| | aaaaa | Narrow | a/p | |
|||
| Period* | B, BB & BBB | Abbreviated | mid. | |
|||
| | BBBB | Wide | am, pm, midnight, noon, morning, afternoon, evening, night | |
|||
| | BBBBB | Narrow | md | |
|||
| Period standalone* | b, bb & bbb | Abbreviated | mid. | |
|||
| | bbbb | Wide | am, pm, midnight, noon, morning, afternoon, evening, night | |
|||
| | bbbbb | Narrow | md | |
|||
| Hour 1-12 | h | Numeric: minimum digits | 1, 12 | |
|||
| | hh | Numeric: 2 digits + zero padded | 01, 12 | |
|||
| Hour 0-23 | H | Numeric: minimum digits | 0, 23 | |
|||
| | HH | Numeric: 2 digits + zero padded | 00, 23 | |
|||
| Minute | m | Numeric: minimum digits | 8, 59 | |
|||
| | mm | Numeric: 2 digits + zero padded | 08, 59 | |
|||
| Second | s | Numeric: minimum digits | 0... 59 | |
|||
| | ss | Numeric: 2 digits + zero padded | 00... 59 | |
|||
| Fractional seconds | S | Numeric: 1 digit | 0... 9 | |
|||
| | SS | Numeric: 2 digits + zero padded | 00... 99 | |
|||
| | SSS | Numeric: 3 digits + zero padded (= milliseconds) | 000... 999 | |
|||
| Zone | z, zz & zzz | Short specific non location format (fallback to O) | GMT-8 | |
|||
| | zzzz | Long specific non location format (fallback to OOOO) | GMT-08:00 | |
|||
| | Z, ZZ & ZZZ | ISO8601 basic format | -0800 | |
|||
| | ZZZZ | Long localized GMT format | GMT-8:00 | |
|||
| | ZZZZZ | ISO8601 extended format + Z indicator for offset 0 (= XXXXX) | -08:00 | |
|||
| | O, OO & OOO | Short localized GMT format | GMT-8 | |
|||
| | OOOO | Long localized GMT format | GMT-08:00 | |
|||
Loading…
Reference in new issue