Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
58fde93
improve partitionTableName() in PreAggregationPartitionRangeLoader
KSDaemon Feb 26, 2025
685383f
make loadBuildRange() timezone-aware + align tests
KSDaemon Feb 26, 2025
2a1152e
fix loadBuildRange() to use inDbTimeZone()
KSDaemon Feb 27, 2025
641d6cf
return back utcToLocalTimeZone
KSDaemon Feb 28, 2025
36b836a
return back utcToLocalTimeZone in replaceQueryBuildRangeParams
KSDaemon Feb 28, 2025
df32aab
rename inDbTimeZone to localTimestampToUtc
KSDaemon Feb 28, 2025
81bab83
small improvement in BaseFilter format*Date
KSDaemon Feb 28, 2025
a73ccff
remove unneeded
KSDaemon Feb 28, 2025
bfad841
another round in loadBuildRange()
KSDaemon Feb 28, 2025
6a5b563
optimize to avoid doble ts convertion, add ts format to loadBuildRange()
KSDaemon Feb 28, 2025
d11412c
fix/specify some types
KSDaemon Feb 28, 2025
937aef6
fix invalidateKeyQueries not to be in the future
KSDaemon Feb 28, 2025
f0e06d5
fix correct extractDate parsing
KSDaemon Feb 28, 2025
4aa2b41
fix localTimestampToUtc()
KSDaemon Feb 28, 2025
0037104
tests for all time functions
KSDaemon Feb 28, 2025
d7e05db
revert change for sealAt
KSDaemon Feb 28, 2025
3199316
fix tests
KSDaemon Feb 28, 2025
0440afc
fix extractDate and rename it to parseLocalDate
KSDaemon Feb 28, 2025
1500406
fix tests for parseLocalDate()
KSDaemon Feb 28, 2025
fc5d7eb
align PreAggregationPartitionRangeLoader with changes
KSDaemon Feb 28, 2025
37375d3
fix BaseDbRunner
KSDaemon Feb 28, 2025
e425e84
fix date formatting in replacePartitionSqlAndParams
KSDaemon Feb 28, 2025
fe38b11
fix tests
KSDaemon Feb 28, 2025
d0440f7
spelling
KSDaemon Mar 3, 2025
deb0155
add more tests for timeSeries()
KSDaemon Mar 3, 2025
e5bbd27
add tests for reformatUtcTimestamp()
KSDaemon Mar 3, 2025
95c3b40
refactor alignToOrigin()
KSDaemon Mar 3, 2025
8a7bf70
convert to utc only in replacePartitionSqlAndParams()
KSDaemon Mar 4, 2025
8ba8f7b
remove reformatUtcTimestamp() as oblsolete
KSDaemon Mar 4, 2025
9ad3a79
code polish
KSDaemon Mar 4, 2025
048290d
remove unused redis-related utils
KSDaemon Mar 4, 2025
ea32c3d
move widely used ts format literal to const
KSDaemon Mar 4, 2025
7dc6919
more tests for PreAggregationPartitionRangeLoader.intersectDateRanges()
KSDaemon Mar 4, 2025
e628a11
some improvements in tests
KSDaemon Mar 4, 2025
8911dc8
some tests for PreAggregationPartitionRangeLoader
KSDaemon Mar 4, 2025
4105a5d
some test polishment
KSDaemon Mar 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 56 additions & 23 deletions packages/cubejs-backend-shared/src/time.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,31 +78,17 @@ export function subtractInterval(date: moment.Moment, interval: ParsedInterval):
*/
export const alignToOrigin = (startDate: moment.Moment, interval: ParsedInterval, origin: moment.Moment): moment.Moment => {
let alignedDate = startDate.clone();
let intervalOp;
let isIntervalNegative = false;

let offsetDate = addInterval(origin, interval);

// The easiest way to check the interval sign
if (offsetDate.isBefore(origin)) {
isIntervalNegative = true;
}

offsetDate = origin.clone();
let offsetDate = origin.clone();

if (startDate.isBefore(origin)) {
intervalOp = isIntervalNegative ? addInterval : subtractInterval;

while (offsetDate.isAfter(startDate)) {
offsetDate = intervalOp(offsetDate, interval);
offsetDate = subtractInterval(offsetDate, interval);
}
alignedDate = offsetDate;
} else {
intervalOp = isIntervalNegative ? subtractInterval : addInterval;

while (offsetDate.isBefore(startDate)) {
alignedDate = offsetDate.clone();
offsetDate = intervalOp(offsetDate, interval);
offsetDate = addInterval(offsetDate, interval);
}

if (offsetDate.isSame(startDate)) {
Expand Down Expand Up @@ -192,7 +178,13 @@ export const BUILD_RANGE_START_LOCAL = '__BUILD_RANGE_START_LOCAL';

export const BUILD_RANGE_END_LOCAL = '__BUILD_RANGE_END_LOCAL';

export const inDbTimeZone = (timezone: string, timestampFormat: string, timestamp: string): string => {
/**
* Takes timestamp, treat it as time in provided timezone and returns the corresponding timestamp in UTC
*/
export const localTimestampToUtc = (timezone: string, timestampFormat: string, timestamp?: string): string | null => {
if (!timestamp) {
return null;
}
if (timestamp.length === 23 || timestamp.length === 26) {
const zone = moment.tz.zone(timezone);
if (!zone) {
Expand All @@ -217,8 +209,14 @@ export const inDbTimeZone = (timezone: string, timestampFormat: string, timestam
} else if (timestampFormat === 'YYYY-MM-DDTHH:mm:ss.SSS') {
return inDbTimeZoneDate.toJSON().replace('Z', '');
} else if (timestampFormat === 'YYYY-MM-DDTHH:mm:ss.SSSSSS') {
const value = inDbTimeZoneDate.toJSON();
if (value.endsWith('999Z')) {
// emulate microseconds
return value.replace('Z', '999');
}

// emulate microseconds
return inDbTimeZoneDate.toJSON().replace('Z', '000');
return value.replace('Z', '000');
}
}

Expand All @@ -227,7 +225,13 @@ export const inDbTimeZone = (timezone: string, timestampFormat: string, timestam
return moment.tz(timestamp, timezone).utc().format(timestampFormat);
};

export const utcToLocalTimeZone = (timezone: string, timestampFormat: string, timestamp: string): string => {
/**
* Takes timestamp in UTC, shift it into provided timezone and returns the corresponding timestamp in UTC
*/
export const utcToLocalTimeZone = (timezone: string, timestampFormat: string, timestamp?: string): string | null => {
if (!timestamp) {
return null;
}
if (timestamp.length === 23) {
const zone = moment.tz.zone(timezone);
if (!zone) {
Expand All @@ -247,16 +251,45 @@ export const utcToLocalTimeZone = (timezone: string, timestampFormat: string, ti
return moment.tz(timestamp, 'UTC').tz(timezone).format(timestampFormat);
};

export const extractDate = (data: any): string | null => {
export const parseLocalDate = (data: any, timezone: string, timestampFormat: string = 'YYYY-MM-DDTHH:mm:ss.SSS'): string | null => {
if (!data) {
return null;
}
data = JSON.parse(JSON.stringify(data));
const value = data[0] && data[0][Object.keys(data[0])[0]];
if (!value) {
return value;
return null;
}

const zone = moment.tz.zone(timezone);
if (!zone) {
throw new Error(`Unknown timezone: ${timezone}`);
}

// Most common formats
const formats = [
moment.ISO_8601,
'YYYY-MM-DD HH:mm:ss',
'YYYY-MM-DD HH:mm:ss.SSS',
'YYYY-MM-DDTHH:mm:ss.SSS',
'YYYY-MM-DDTHH:mm:ss'
];

let parsedMoment;

if (value.includes('Z') || /([+-]\d{2}:?\d{2})$/.test(value.trim())) {
// We have timezone info
parsedMoment = moment(value, formats, true);
} else {
// If no tz info - use provided timezone
parsedMoment = moment.tz(value, formats, true, timezone);
}
return moment.tz(value, 'UTC').utc().format(moment.HTML5_FMT.DATETIME_LOCAL_MS);

if (!parsedMoment.isValid()) {
return null;
}

return parsedMoment.tz(timezone).format(timestampFormat);
};

export const addSecondsToLocalTimestamp = (timestamp: string, timezone: string, seconds: number): Date => {
Expand Down
Loading
Loading