Skip to content

Commit 56e68f2

Browse files
pdanpdanrstoenescu
andauthored
fix(date utils): Correctly extract dates with timezoneOffset different than the current one; keep original timezone in QDate and QTime quasarframework#6615 (quasarframework#6635)
* fix(date utils): Correctly extract dates with timezoneOffset different than the current one; keep original timezone in QDate and QTime quasarframework#6615 * Update date.js Co-authored-by: Razvan Stoenescu <razvan.stoenescu@gmail.com>
1 parent 902762b commit 56e68f2

File tree

5 files changed

+134
-11
lines changed

5 files changed

+134
-11
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<template>
2+
<div class="q-layout-padding column q-gutter-y-md">
3+
<q-input filled v-model="date1" label="YYYY-MM-DD HH:mm:ssZ">
4+
<template v-slot:prepend>
5+
<q-icon name="event" class="cursor-pointer">
6+
<q-popup-proxy transition-show="scale" transition-hide="scale">
7+
<q-date v-model="date1" mask="YYYY-MM-DD HH:mm:ssZ" />
8+
</q-popup-proxy>
9+
</q-icon>
10+
</template>
11+
12+
<template v-slot:append>
13+
<q-icon name="access_time" class="cursor-pointer">
14+
<q-popup-proxy transition-show="scale" transition-hide="scale">
15+
<q-time v-model="date1" mask="YYYY-MM-DD HH:mm:ssZ" format24h with-seconds />
16+
</q-popup-proxy>
17+
</q-icon>
18+
</template>
19+
</q-input>
20+
21+
<q-input filled v-model="date2" label="YYYY-MM-DD HH:mm ZZ">
22+
<template v-slot:prepend>
23+
<q-icon name="event" class="cursor-pointer">
24+
<q-popup-proxy transition-show="scale" transition-hide="scale">
25+
<q-date v-model="date2" mask="YYYY-MM-DD HH:mm ZZ" />
26+
</q-popup-proxy>
27+
</q-icon>
28+
</template>
29+
30+
<template v-slot:append>
31+
<q-icon name="access_time" class="cursor-pointer">
32+
<q-popup-proxy transition-show="scale" transition-hide="scale">
33+
<q-time v-model="date2" mask="YYYY-MM-DD HH:mm ZZ" format24h />
34+
</q-popup-proxy>
35+
</q-icon>
36+
</template>
37+
</q-input>
38+
39+
<q-input filled v-model="date3" label="YYYY-MM-DD HH:mm">
40+
<template v-slot:prepend>
41+
<q-icon name="event" class="cursor-pointer">
42+
<q-popup-proxy transition-show="scale" transition-hide="scale">
43+
<q-date v-model="date3" mask="YYYY-MM-DD HH:mm" />
44+
</q-popup-proxy>
45+
</q-icon>
46+
</template>
47+
48+
<template v-slot:append>
49+
<q-icon name="access_time" class="cursor-pointer">
50+
<q-popup-proxy transition-show="scale" transition-hide="scale">
51+
<q-time v-model="date3" mask="YYYY-MM-DD HH:mm" format24h />
52+
</q-popup-proxy>
53+
</q-icon>
54+
</template>
55+
</q-input>
56+
</div>
57+
</template>
58+
59+
<script>
60+
import { date as qDate } from 'quasar'
61+
62+
export default {
63+
data () {
64+
return {
65+
date1: '2020-03-18 17:40:45+05:00',
66+
date2: '2020-03-18 17:40 -0730',
67+
date3: '2020-03-18 17:40'
68+
}
69+
},
70+
methods: {
71+
test (dateString, formatMask) {
72+
const date = new Date(dateString)
73+
console.log('date [' + dateString + ']: ', date)
74+
console.log('extr [' + dateString + ']: ', qDate.extractDate(dateString, formatMask))
75+
}
76+
},
77+
mounted () {
78+
this.test('2020-03-14 10:44:05+05:00', 'YYYY-MM-DD HH:mm:ssZ')
79+
this.test('2020-03-14 10:44:05+02:00', 'YYYY-MM-DD HH:mm:ssZ')
80+
this.test('2020-03-14 10:44:05+05:30', 'YYYY-MM-DD HH:mm:ssZ')
81+
this.test('2020-03-14 10:44:05-05:30', 'YYYY-MM-DD HH:mm:ssZ')
82+
83+
this.test('2020-03-14 10:44:05+0500', 'YYYY-MM-DD HH:mm:ssZZ')
84+
this.test('2020-03-14 10:44:05+0200', 'YYYY-MM-DD HH:mm:ssZZ')
85+
this.test('2020-03-14 10:44:05+0530', 'YYYY-MM-DD HH:mm:ssZZ')
86+
this.test('2020-03-14 10:44:05-0530', 'YYYY-MM-DD HH:mm:ssZZ')
87+
88+
this.test('2020-03-14 10:44:05', 'YYYY-MM-DD HH:mm:ss')
89+
this.test('2020-03-14 10:44:05', 'YYYY-MM-DD HH:mm:ss')
90+
this.test('2020-03-14 10:44:05', 'YYYY-MM-DD HH:mm:ss')
91+
this.test('2020-03-14 10:44:05', 'YYYY-MM-DD HH:mm:ss')
92+
}
93+
}
94+
</script>
95+
96+
<style lang="stylus">
97+
</style>

ui/src/components/date/QDate.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,8 @@ export default Vue.extend({
716716
),
717717
this.mask,
718718
this.computedLocale,
719-
date.year
719+
date.year,
720+
this.extModel.timezoneOffset
720721
)
721722

722723
date.changed = val !== this.value

ui/src/components/time/QTime.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -688,7 +688,8 @@ export default Vue.extend({
688688
),
689689
this.computedMask,
690690
this.computedLocale,
691-
date.year
691+
date.year,
692+
date.timezoneOffset
692693
)
693694

694695
date.changed = val !== this.value

ui/src/utils/date.js

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,10 @@ function getRegexData (mask, dateLocale) {
123123
return '(\\d{2})'
124124

125125
case 'Z': // to split: (?:(Z)()()|([+-])?(\\d{2}):?(\\d{2}))
126+
map.Z = index
126127
return '(Z|[+-]\\d{2}:\\d{2})'
127128
case 'ZZ':
129+
map.ZZ = index
128130
return '(Z|[+-]\\d{2}\\d{2})'
129131

130132
case 'X':
@@ -152,7 +154,7 @@ function getRegexData (mask, dateLocale) {
152154
export function extractDate (str, mask, dateLocale) {
153155
const d = __splitDate(str, mask, dateLocale)
154156

155-
return new Date(
157+
const date = new Date(
156158
d.year,
157159
d.month === null ? null : d.month - 1,
158160
d.day,
@@ -161,6 +163,12 @@ export function extractDate (str, mask, dateLocale) {
161163
d.second,
162164
d.millisecond
163165
)
166+
167+
const tzOffset = date.getTimezoneOffset()
168+
169+
return d.timezoneOffset === null || d.timezoneOffset === tzOffset
170+
? date
171+
: getChange(date, { minutes: d.timezoneOffset - tzOffset }, true)
164172
}
165173

166174
export function __splitDate (str, mask, dateLocale, calendar, defaultModel) {
@@ -172,6 +180,7 @@ export function __splitDate (str, mask, dateLocale, calendar, defaultModel) {
172180
minute: null,
173181
second: null,
174182
millisecond: null,
183+
timezoneOffset: null,
175184
dateHash: null,
176185
timeHash: null
177186
}, defaultModel)
@@ -202,6 +211,8 @@ export function __splitDate (str, mask, dateLocale, calendar, defaultModel) {
202211
return date
203212
}
204213

214+
let tzString = ''
215+
205216
if (map.X !== void 0 || map.x !== void 0) {
206217
const stamp = parseInt(match[map.X !== void 0 ? map.X : map.x], 10)
207218

@@ -283,10 +294,15 @@ export function __splitDate (str, mask, dateLocale, calendar, defaultModel) {
283294
if (map.S !== void 0) {
284295
date.millisecond = parseInt(match[map.S], 10) * 10 ** (3 - match[map.S].length)
285296
}
297+
298+
if (map.Z !== void 0 || map.ZZ !== void 0) {
299+
tzString = (map.Z !== void 0 ? match[map.Z].replace(':', '') : match[map.ZZ])
300+
date.timezoneOffset = (tzString[0] === '+' ? -1 : 1) * (60 * tzString.slice(1, 3) + 1 * tzString.slice(3, 5))
301+
}
286302
}
287303

288304
date.dateHash = date.year + '/' + pad(date.month) + '/' + pad(date.day)
289-
date.timeHash = pad(date.hour) + ':' + pad(date.minute) + ':' + pad(date.second)
305+
date.timeHash = pad(date.hour) + ':' + pad(date.minute) + ':' + pad(date.second) + tzString
290306

291307
return date
292308
}
@@ -772,13 +788,21 @@ const formatter = {
772788
},
773789

774790
// Timezone: -01:00, +00:00, ... +12:00
775-
Z (date) {
776-
return formatTimezone(date.getTimezoneOffset(), ':')
791+
Z (date, dateLocale, forcedYear, forcedTimezoneOffset) {
792+
const tzOffset = forcedTimezoneOffset === void 0 || forcedTimezoneOffset === null
793+
? date.getTimezoneOffset()
794+
: forcedTimezoneOffset
795+
796+
return formatTimezone(tzOffset, ':')
777797
},
778798

779799
// Timezone: -0100, +0000, ... +1200
780-
ZZ (date) {
781-
return formatTimezone(date.getTimezoneOffset())
800+
ZZ (date, dateLocale, forcedYear, forcedTimezoneOffset) {
801+
const tzOffset = forcedTimezoneOffset === void 0 || forcedTimezoneOffset === null
802+
? date.getTimezoneOffset()
803+
: forcedTimezoneOffset
804+
805+
return formatTimezone(tzOffset)
782806
},
783807

784808
// Seconds timestamp: 512969520
@@ -792,7 +816,7 @@ const formatter = {
792816
}
793817
}
794818

795-
export function formatDate (val, mask, dateLocale, __forcedYear) {
819+
export function formatDate (val, mask, dateLocale, __forcedYear, __forcedTimezoneOffset) {
796820
if (
797821
(val !== 0 && !val) ||
798822
val === Infinity ||
@@ -818,7 +842,7 @@ export function formatDate (val, mask, dateLocale, __forcedYear) {
818842
return mask.replace(
819843
token,
820844
(match, text) => match in formatter
821-
? formatter[match](date, locale, __forcedYear)
845+
? formatter[match](date, locale, __forcedYear, __forcedTimezoneOffset)
822846
: (text === void 0 ? match : text.split('\\]').join(']'))
823847
)
824848
}

ui/types/utils/date.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,6 @@ export namespace date {
4343
function getDateBetween(date: Date | number | string, min?: Date | number | string, max?: Date | number | string): Date;
4444
function isSameDate(date: Date | number | string, date2: Date | number | string, unit?: DateUnitOptions): boolean;
4545
function daysInMonth(date: Date | number | string): number;
46-
function formatDate(date: Date | number | string | undefined, format?: string, locale?: DateLocale, __forcedYear?: number): string;
46+
function formatDate(date: Date | number | string | undefined, format?: string, locale?: DateLocale, __forcedYear?: number, __forcedTimezoneOffset?: number): string;
4747
function clone<D extends Date | number | string>(date: D): D;
4848
}

0 commit comments

Comments
 (0)