@@ -42,6 +42,55 @@ export class SnowflakeQuery extends BaseQuery {
4242 return `date_trunc('${ GRANULARITY_TO_INTERVAL [ granularity ] } ', ${ dimension } )` ;
4343 }
4444
45+ public dimensionTimeGroupedColumn ( dimension : string , interval : string , offset : string ) : string {
46+ if ( offset ) {
47+ offset = this . formatInterval ( offset ) ;
48+ }
49+
50+ if ( this . isGranularityNaturalAligned ( interval ) ) {
51+ return super . dimensionTimeGroupedColumn ( dimension , interval , offset ) ;
52+ }
53+
54+ // Formula:
55+ // SELECT DATEADD(second,
56+ // FLOOR(
57+ // DATEDIFF(seconds, DATE_TRUNC('year', dimension) + offset?, dimension) /
58+ // DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval))
59+ // ) * DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval)),
60+ // DATE_TRUNC('year', dimension) + offset?)
61+ //
62+ // The formula operates with seconds so it won't produce dates aligned with offset date parts, like:
63+ // if offset is "6 months 3 days" - the result won't always be the 3rd of July. It will add
64+ // exact number of seconds in the "6 months 3 days" without aligning with natural calendar.
65+
66+ let dtDate = this . timeGroupedColumn ( 'year' , dimension ) ;
67+ if ( offset ) {
68+ dtDate = this . addInterval ( dtDate , offset ) ;
69+ }
70+
71+ interval = this . formatInterval ( interval ) ;
72+
73+ return `DATEADD(second,
74+ FLOOR(
75+ DATEDIFF(seconds, ${ dtDate } , CURRENT_TIMESTAMP) /
76+ DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval '${ interval } '))
77+ ) * DATE_PART(epoch_seconds FROM (TIMESTAMP_FROM_PARTS(1970, 1, 1, 0, 0, 0) + interval '${ interval } ')),
78+ ${ dtDate } )` ;
79+ }
80+
81+ /**
82+ * The input interval in format "2 years 3 months 4 weeks 5 days...."
83+ * will be converted to Snowflake dialect "2 years, 3 months, 4 weeks, 5 days...."
84+ */
85+ private formatInterval ( interval : string ) : string {
86+ return interval . split ( ' ' ) . map ( ( word , index , arr ) => {
87+ if ( index % 2 !== 0 && index < arr . length - 1 ) {
88+ return `${ word } ,` ;
89+ }
90+ return word ;
91+ } ) . join ( ' ' ) ;
92+ }
93+
4594 public timeStampCast ( value ) {
4695 return `${ value } ::timestamp_tz` ;
4796 }
0 commit comments