@@ -78,31 +78,17 @@ export function subtractInterval(date: moment.Moment, interval: ParsedInterval):
7878 */
7979export const alignToOrigin = ( startDate : moment . Moment , interval : ParsedInterval , origin : moment . Moment ) : moment . Moment => {
8080 let alignedDate = startDate . clone ( ) ;
81- let intervalOp ;
82- let isIntervalNegative = false ;
83-
84- let offsetDate = addInterval ( origin , interval ) ;
85-
86- // The easiest way to check the interval sign
87- if ( offsetDate . isBefore ( origin ) ) {
88- isIntervalNegative = true ;
89- }
90-
91- offsetDate = origin . clone ( ) ;
81+ let offsetDate = origin . clone ( ) ;
9282
9383 if ( startDate . isBefore ( origin ) ) {
94- intervalOp = isIntervalNegative ? addInterval : subtractInterval ;
95-
9684 while ( offsetDate . isAfter ( startDate ) ) {
97- offsetDate = intervalOp ( offsetDate , interval ) ;
85+ offsetDate = subtractInterval ( offsetDate , interval ) ;
9886 }
9987 alignedDate = offsetDate ;
10088 } else {
101- intervalOp = isIntervalNegative ? subtractInterval : addInterval ;
102-
10389 while ( offsetDate . isBefore ( startDate ) ) {
10490 alignedDate = offsetDate . clone ( ) ;
105- offsetDate = intervalOp ( offsetDate , interval ) ;
91+ offsetDate = addInterval ( offsetDate , interval ) ;
10692 }
10793
10894 if ( offsetDate . isSame ( startDate ) ) {
@@ -192,7 +178,13 @@ export const BUILD_RANGE_START_LOCAL = '__BUILD_RANGE_START_LOCAL';
192178
193179export const BUILD_RANGE_END_LOCAL = '__BUILD_RANGE_END_LOCAL' ;
194180
195- export const inDbTimeZone = ( timezone : string , timestampFormat : string , timestamp : string ) : string => {
181+ /**
182+ * Takes timestamp, treat it as time in provided timezone and returns the corresponding timestamp in UTC
183+ */
184+ export const localTimestampToUtc = ( timezone : string , timestampFormat : string , timestamp ?: string ) : string | null => {
185+ if ( ! timestamp ) {
186+ return null ;
187+ }
196188 if ( timestamp . length === 23 || timestamp . length === 26 ) {
197189 const zone = moment . tz . zone ( timezone ) ;
198190 if ( ! zone ) {
@@ -217,8 +209,14 @@ export const inDbTimeZone = (timezone: string, timestampFormat: string, timestam
217209 } else if ( timestampFormat === 'YYYY-MM-DDTHH:mm:ss.SSS' ) {
218210 return inDbTimeZoneDate . toJSON ( ) . replace ( 'Z' , '' ) ;
219211 } else if ( timestampFormat === 'YYYY-MM-DDTHH:mm:ss.SSSSSS' ) {
212+ const value = inDbTimeZoneDate . toJSON ( ) ;
213+ if ( value . endsWith ( '999Z' ) ) {
214+ // emulate microseconds
215+ return value . replace ( 'Z' , '999' ) ;
216+ }
217+
220218 // emulate microseconds
221- return inDbTimeZoneDate . toJSON ( ) . replace ( 'Z' , '000' ) ;
219+ return value . replace ( 'Z' , '000' ) ;
222220 }
223221 }
224222
@@ -227,7 +225,13 @@ export const inDbTimeZone = (timezone: string, timestampFormat: string, timestam
227225 return moment . tz ( timestamp , timezone ) . utc ( ) . format ( timestampFormat ) ;
228226} ;
229227
230- export const utcToLocalTimeZone = ( timezone : string , timestampFormat : string , timestamp : string ) : string => {
228+ /**
229+ * Takes timestamp in UTC, shift it into provided timezone and returns the corresponding timestamp in UTC
230+ */
231+ export const utcToLocalTimeZone = ( timezone : string , timestampFormat : string , timestamp ?: string ) : string | null => {
232+ if ( ! timestamp ) {
233+ return null ;
234+ }
231235 if ( timestamp . length === 23 ) {
232236 const zone = moment . tz . zone ( timezone ) ;
233237 if ( ! zone ) {
@@ -247,16 +251,45 @@ export const utcToLocalTimeZone = (timezone: string, timestampFormat: string, ti
247251 return moment . tz ( timestamp , 'UTC' ) . tz ( timezone ) . format ( timestampFormat ) ;
248252} ;
249253
250- export const extractDate = ( data : any ) : string | null => {
254+ export const parseLocalDate = ( data : any , timezone : string , timestampFormat : string = 'YYYY-MM-DDTHH:mm:ss.SSS' ) : string | null => {
251255 if ( ! data ) {
252256 return null ;
253257 }
254258 data = JSON . parse ( JSON . stringify ( data ) ) ;
255259 const value = data [ 0 ] && data [ 0 ] [ Object . keys ( data [ 0 ] ) [ 0 ] ] ;
256260 if ( ! value ) {
257- return value ;
261+ return null ;
262+ }
263+
264+ const zone = moment . tz . zone ( timezone ) ;
265+ if ( ! zone ) {
266+ throw new Error ( `Unknown timezone: ${ timezone } ` ) ;
267+ }
268+
269+ // Most common formats
270+ const formats = [
271+ moment . ISO_8601 ,
272+ 'YYYY-MM-DD HH:mm:ss' ,
273+ 'YYYY-MM-DD HH:mm:ss.SSS' ,
274+ 'YYYY-MM-DDTHH:mm:ss.SSS' ,
275+ 'YYYY-MM-DDTHH:mm:ss'
276+ ] ;
277+
278+ let parsedMoment ;
279+
280+ if ( value . includes ( 'Z' ) || / ( [ + - ] \d { 2 } : ? \d { 2 } ) $ / . test ( value . trim ( ) ) ) {
281+ // We have timezone info
282+ parsedMoment = moment ( value , formats , true ) ;
283+ } else {
284+ // If no tz info - use provided timezone
285+ parsedMoment = moment . tz ( value , formats , true , timezone ) ;
258286 }
259- return moment . tz ( value , 'UTC' ) . utc ( ) . format ( moment . HTML5_FMT . DATETIME_LOCAL_MS ) ;
287+
288+ if ( ! parsedMoment . isValid ( ) ) {
289+ return null ;
290+ }
291+
292+ return parsedMoment . tz ( timezone ) . format ( timestampFormat ) ;
260293} ;
261294
262295export const addSecondsToLocalTimestamp = ( timestamp : string , timezone : string , seconds : number ) : Date => {
0 commit comments