1use crate::dialect::DialectTypes;
15use crate::sql::{SQL, Token};
16use crate::traits::SQLParam;
17use crate::types::{DataType, Numeric, Temporal, Textual};
18use crate::{PostgresDialect, SQLiteDialect};
19use drizzle_types::postgres::types::{Timestamp as PgTimestamp, Timestamptz as PgTimestamptz};
20
21use super::{AggOr, Expr, NullOr, Nullability, SQLExpr, Scalar};
22
23#[diagnostic::on_unimplemented(
24 message = "this date/time function is not available for this dialect",
25 label = "use a dialect-specific alternative"
26)]
27pub trait SQLiteDateTimeSupport {}
28
29#[diagnostic::on_unimplemented(
30 message = "this date/time function is not available for this dialect",
31 label = "use a dialect-specific alternative"
32)]
33pub trait PostgresDateTimeSupport {}
34
35#[diagnostic::on_unimplemented(
36 message = "DATE_TRUNC output type is not defined for `{Self}` on this dialect",
37 label = "DATE_TRUNC accepts timestamp/timestamptz and preserves the timestamp flavor"
38)]
39pub trait DateTruncPolicy<D>: Temporal {
40 type Output: DataType;
41}
42
43impl SQLiteDateTimeSupport for SQLiteDialect {}
44impl PostgresDateTimeSupport for PostgresDialect {}
45
46impl DateTruncPolicy<PostgresDialect> for PgTimestamptz {
47 type Output = Self;
48}
49impl DateTruncPolicy<PostgresDialect> for PgTimestamp {
50 type Output = Self;
51}
52
53#[must_use]
72pub fn current_date<'a, V>()
73-> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Date, super::NonNull, Scalar>
74where
75 V: SQLParam + 'a,
76{
77 SQLExpr::new(SQL::raw("CURRENT_DATE"))
78}
79
80#[must_use]
95pub fn current_time<'a, V>()
96-> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Time, super::NonNull, Scalar>
97where
98 V: SQLParam + 'a,
99{
100 SQLExpr::new(SQL::raw("CURRENT_TIME"))
101}
102
103#[must_use]
121pub fn current_timestamp<'a, V>()
122-> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::TimestampTz, super::NonNull, Scalar>
123where
124 V: SQLParam + 'a,
125{
126 SQLExpr::new(SQL::raw("CURRENT_TIMESTAMP"))
127}
128
129pub fn date<'a, V, E>(
148 expr: E,
149) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Date, E::Nullable, E::Aggregate>
150where
151 V: SQLParam + 'a,
152 V::DialectMarker: SQLiteDateTimeSupport,
153 E: Expr<'a, V>,
154 E::SQLType: Temporal,
155{
156 SQLExpr::new(SQL::func("DATE", expr.into_sql()))
157}
158
159pub fn time<'a, V, E>(
174 expr: E,
175) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Time, E::Nullable, E::Aggregate>
176where
177 V: SQLParam + 'a,
178 V::DialectMarker: SQLiteDateTimeSupport,
179 E: Expr<'a, V>,
180 E::SQLType: Temporal,
181{
182 SQLExpr::new(SQL::func("TIME", expr.into_sql()))
183}
184
185pub fn datetime<'a, V, E>(
200 expr: E,
201) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Timestamp, E::Nullable, E::Aggregate>
202where
203 V: SQLParam + 'a,
204 V::DialectMarker: SQLiteDateTimeSupport,
205 E: Expr<'a, V>,
206 E::SQLType: Temporal,
207{
208 SQLExpr::new(SQL::func("DATETIME", expr.into_sql()))
209}
210
211#[allow(clippy::type_complexity)]
238pub fn strftime<'a, V, F, E>(
239 format: F,
240 expr: E,
241) -> SQLExpr<
242 'a,
243 V,
244 <V::DialectMarker as DialectTypes>::Text,
245 E::Nullable,
246 <F::Aggregate as AggOr<E::Aggregate>>::Output,
247>
248where
249 V: SQLParam + 'a,
250 V::DialectMarker: SQLiteDateTimeSupport,
251 F: Expr<'a, V>,
252 F::SQLType: Textual,
253 E: Expr<'a, V>,
254 E::SQLType: Temporal,
255 F::Aggregate: AggOr<E::Aggregate>,
256{
257 SQLExpr::new(SQL::func(
258 "STRFTIME",
259 format.into_sql().push(Token::COMMA).append(expr.into_sql()),
260 ))
261}
262
263pub fn julianday<'a, V, E>(
278 expr: E,
279) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Double, E::Nullable, E::Aggregate>
280where
281 V: SQLParam + 'a,
282 V::DialectMarker: SQLiteDateTimeSupport,
283 E: Expr<'a, V>,
284 E::SQLType: Temporal,
285{
286 SQLExpr::new(SQL::func("JULIANDAY", expr.into_sql()))
287}
288
289pub fn unixepoch<'a, V, E>(
304 expr: E,
305) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::BigInt, E::Nullable, E::Aggregate>
306where
307 V: SQLParam + 'a,
308 V::DialectMarker: SQLiteDateTimeSupport,
309 E: Expr<'a, V>,
310 E::SQLType: Temporal,
311{
312 SQLExpr::new(SQL::func("UNIXEPOCH", expr.into_sql()))
313}
314
315#[must_use]
332pub fn now<'a, V>()
333-> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::TimestampTz, super::NonNull, Scalar>
334where
335 V: SQLParam + 'a,
336 V::DialectMarker: PostgresDateTimeSupport,
337{
338 SQLExpr::new(SQL::raw("NOW()"))
339}
340
341#[allow(clippy::type_complexity)]
360pub fn date_trunc<'a, V, P, E>(
361 precision: P,
362 expr: E,
363) -> SQLExpr<
364 'a,
365 V,
366 <E::SQLType as DateTruncPolicy<V::DialectMarker>>::Output,
367 E::Nullable,
368 <P::Aggregate as AggOr<E::Aggregate>>::Output,
369>
370where
371 V: SQLParam + 'a,
372 V::DialectMarker: PostgresDateTimeSupport,
373 P: Expr<'a, V>,
374 P::SQLType: Textual,
375 E: Expr<'a, V>,
376 E::SQLType: DateTruncPolicy<V::DialectMarker>,
377 P::Aggregate: AggOr<E::Aggregate>,
378{
379 SQLExpr::new(SQL::func(
380 "DATE_TRUNC",
381 precision
382 .into_sql()
383 .push(Token::COMMA)
384 .append(expr.into_sql()),
385 ))
386}
387
388pub fn extract<'a, 'f, V, E>(
407 field: &'f str,
408 expr: E,
409) -> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Double, E::Nullable, E::Aggregate>
410where
411 'f: 'a,
412 V: SQLParam + 'a,
413 V::DialectMarker: PostgresDateTimeSupport,
414 E: Expr<'a, V>,
415 E::SQLType: Temporal,
416{
417 let extracted = SQL::raw("EXTRACT(")
420 .append(SQL::raw(field))
421 .append(SQL::raw(" FROM "))
422 .append(expr.into_sql())
423 .push(Token::RPAREN);
424 SQLExpr::new(SQL::func(
425 "CAST",
426 extracted
427 .push(Token::AS)
428 .append(SQL::raw("DOUBLE PRECISION")),
429 ))
430}
431
432#[allow(clippy::type_complexity)]
447pub fn age<'a, V, E1, E2>(
448 timestamp1: E1,
449 timestamp2: E2,
450) -> SQLExpr<
451 'a,
452 V,
453 drizzle_types::postgres::types::Interval,
454 <E1::Nullable as NullOr<E2::Nullable>>::Output,
455 <E1::Aggregate as AggOr<E2::Aggregate>>::Output,
456>
457where
458 V: SQLParam + 'a,
459 V::DialectMarker: PostgresDateTimeSupport,
460 E1: Expr<'a, V>,
461 E1::SQLType: Temporal,
462 E2: Expr<'a, V>,
463 E2::SQLType: Temporal,
464 E1::Nullable: NullOr<E2::Nullable>,
465 E2::Nullable: Nullability,
466 E1::Aggregate: AggOr<E2::Aggregate>,
467{
468 SQLExpr::new(SQL::func(
469 "AGE",
470 timestamp1
471 .into_sql()
472 .push(Token::COMMA)
473 .append(timestamp2.into_sql()),
474 ))
475}
476
477#[allow(clippy::type_complexity)]
503pub fn to_char<'a, V, E, F>(
504 expr: E,
505 format: F,
506) -> SQLExpr<
507 'a,
508 V,
509 <V::DialectMarker as DialectTypes>::Text,
510 E::Nullable,
511 <E::Aggregate as AggOr<F::Aggregate>>::Output,
512>
513where
514 V: SQLParam + 'a,
515 V::DialectMarker: PostgresDateTimeSupport,
516 E: Expr<'a, V>,
517 E::SQLType: Temporal,
518 F: Expr<'a, V>,
519 F::SQLType: Textual,
520 E::Aggregate: AggOr<F::Aggregate>,
521{
522 SQLExpr::new(SQL::func(
523 "TO_CHAR",
524 expr.into_sql().push(Token::COMMA).append(format.into_sql()),
525 ))
526}
527
528pub fn to_timestamp<'a, V, E>(expr: E) -> SQLExpr<'a, V, PgTimestamptz, E::Nullable, E::Aggregate>
543where
544 V: SQLParam + 'a,
545 V::DialectMarker: PostgresDateTimeSupport,
546 E: Expr<'a, V>,
547 E::SQLType: Numeric,
548{
549 SQLExpr::new(SQL::func("TO_TIMESTAMP", expr.into_sql()))
550}
551
552#[allow(clippy::type_complexity)]
571pub fn to_date<'a, V, E, F>(
572 expr: E,
573 format: F,
574) -> SQLExpr<
575 'a,
576 V,
577 <V::DialectMarker as DialectTypes>::Date,
578 E::Nullable,
579 <E::Aggregate as AggOr<F::Aggregate>>::Output,
580>
581where
582 V: SQLParam + 'a,
583 V::DialectMarker: PostgresDateTimeSupport,
584 E: Expr<'a, V>,
585 E::SQLType: Textual,
586 F: Expr<'a, V>,
587 F::SQLType: Textual,
588 E::Aggregate: AggOr<F::Aggregate>,
589{
590 SQLExpr::new(SQL::func(
591 "TO_DATE",
592 expr.into_sql().push(Token::COMMA).append(format.into_sql()),
593 ))
594}
595
596#[allow(clippy::type_complexity)]
611pub fn to_number<'a, V, E, F>(
612 expr: E,
613 format: F,
614) -> SQLExpr<
615 'a,
616 V,
617 drizzle_types::postgres::types::Numeric,
618 E::Nullable,
619 <E::Aggregate as AggOr<F::Aggregate>>::Output,
620>
621where
622 V: SQLParam + 'a,
623 V::DialectMarker: PostgresDateTimeSupport,
624 E: Expr<'a, V>,
625 E::SQLType: Textual,
626 F: Expr<'a, V>,
627 F::SQLType: Textual,
628 E::Aggregate: AggOr<F::Aggregate>,
629{
630 SQLExpr::new(SQL::func(
631 "TO_NUMBER",
632 expr.into_sql().push(Token::COMMA).append(format.into_sql()),
633 ))
634}
635
636#[allow(clippy::type_complexity)]
656pub fn date_bin<'a, V, S, E, O>(
657 stride: S,
658 source: E,
659 origin: O,
660) -> SQLExpr<
661 'a,
662 V,
663 E::SQLType,
664 <<S::Nullable as NullOr<E::Nullable>>::Output as NullOr<O::Nullable>>::Output,
665 <<S::Aggregate as AggOr<E::Aggregate>>::Output as AggOr<O::Aggregate>>::Output,
666>
667where
668 V: SQLParam + 'a,
669 V::DialectMarker: PostgresDateTimeSupport,
670 S: Expr<'a, V>,
671 E: Expr<'a, V>,
672 E::SQLType: Temporal,
673 O: Expr<'a, V>,
674 O::SQLType: Temporal,
675 S::Nullable: NullOr<E::Nullable>,
676 E::Nullable: Nullability,
677 <S::Nullable as NullOr<E::Nullable>>::Output: NullOr<O::Nullable>,
678 O::Nullable: Nullability,
679 S::Aggregate: AggOr<E::Aggregate>,
680 <S::Aggregate as AggOr<E::Aggregate>>::Output: AggOr<O::Aggregate>,
681 O::Aggregate: super::AggregateKind,
682{
683 SQLExpr::new(SQL::func(
686 "DATE_BIN",
687 super::math::pg_cast(stride.into_sql(), "INTERVAL")
688 .push(Token::COMMA)
689 .append(source.into_sql())
690 .push(Token::COMMA)
691 .append(origin.into_sql()),
692 ))
693}
694
695#[allow(clippy::type_complexity)]
712pub fn make_date<'a, V, Y, M, D>(
713 year: Y,
714 month: M,
715 day: D,
716) -> SQLExpr<
717 'a,
718 V,
719 <V::DialectMarker as DialectTypes>::Date,
720 <<Y::Nullable as NullOr<M::Nullable>>::Output as NullOr<D::Nullable>>::Output,
721 <<Y::Aggregate as AggOr<M::Aggregate>>::Output as AggOr<D::Aggregate>>::Output,
722>
723where
724 V: SQLParam + 'a,
725 V::DialectMarker: PostgresDateTimeSupport,
726 Y: Expr<'a, V>,
727 Y::SQLType: Numeric,
728 M: Expr<'a, V>,
729 M::SQLType: Numeric,
730 D: Expr<'a, V>,
731 D::SQLType: Numeric,
732 Y::Nullable: NullOr<M::Nullable>,
733 M::Nullable: Nullability,
734 <Y::Nullable as NullOr<M::Nullable>>::Output: NullOr<D::Nullable>,
735 D::Nullable: Nullability,
736 Y::Aggregate: AggOr<M::Aggregate>,
737 <Y::Aggregate as AggOr<M::Aggregate>>::Output: AggOr<D::Aggregate>,
738 D::Aggregate: super::AggregateKind,
739{
740 SQLExpr::new(SQL::func(
741 "MAKE_DATE",
742 year.into_sql()
743 .push(Token::COMMA)
744 .append(month.into_sql())
745 .push(Token::COMMA)
746 .append(day.into_sql()),
747 ))
748}
749
750#[allow(clippy::type_complexity)]
763pub fn make_timestamp<'a, V, Y, Mo, D, H, Mi, S>(
764 year: Y,
765 month: Mo,
766 day: D,
767 hour: H,
768 minute: Mi,
769 second: S,
770) -> SQLExpr<
771 'a,
772 V,
773 <V::DialectMarker as DialectTypes>::Timestamp,
774 <<<<Y::Nullable as NullOr<Mo::Nullable>>::Output as NullOr<D::Nullable>>::Output as NullOr<
775 H::Nullable,
776 >>::Output as NullOr<<Mi::Nullable as NullOr<S::Nullable>>::Output>>::Output,
777 <<<<Y::Aggregate as AggOr<Mo::Aggregate>>::Output as AggOr<D::Aggregate>>::Output as AggOr<
778 H::Aggregate,
779 >>::Output as AggOr<<Mi::Aggregate as AggOr<S::Aggregate>>::Output>>::Output,
780>
781where
782 V: SQLParam + 'a,
783 V::DialectMarker: PostgresDateTimeSupport,
784 Y: Expr<'a, V>,
785 Y::SQLType: Numeric,
786 Mo: Expr<'a, V>,
787 Mo::SQLType: Numeric,
788 D: Expr<'a, V>,
789 D::SQLType: Numeric,
790 H: Expr<'a, V>,
791 H::SQLType: Numeric,
792 Mi: Expr<'a, V>,
793 Mi::SQLType: Numeric,
794 S: Expr<'a, V>,
795 S::SQLType: Numeric,
796 Y::Nullable: NullOr<Mo::Nullable>,
797 <Y::Nullable as NullOr<Mo::Nullable>>::Output: NullOr<D::Nullable>,
798 <<Y::Nullable as NullOr<Mo::Nullable>>::Output as NullOr<D::Nullable>>::Output:
799 NullOr<H::Nullable>,
800 Mi::Nullable: NullOr<S::Nullable>,
801 <<<Y::Nullable as NullOr<Mo::Nullable>>::Output as NullOr<D::Nullable>>::Output as NullOr<
802 H::Nullable,
803 >>::Output: NullOr<<Mi::Nullable as NullOr<S::Nullable>>::Output>,
804 H::Nullable: Nullability,
805 D::Nullable: Nullability,
806 Mo::Nullable: Nullability,
807 S::Nullable: Nullability,
808 Y::Aggregate: AggOr<Mo::Aggregate>,
809 <Y::Aggregate as AggOr<Mo::Aggregate>>::Output: AggOr<D::Aggregate>,
810 <<Y::Aggregate as AggOr<Mo::Aggregate>>::Output as AggOr<D::Aggregate>>::Output:
811 AggOr<H::Aggregate>,
812 Mi::Aggregate: AggOr<S::Aggregate>,
813 <<<Y::Aggregate as AggOr<Mo::Aggregate>>::Output as AggOr<D::Aggregate>>::Output as AggOr<
814 H::Aggregate,
815 >>::Output: AggOr<<Mi::Aggregate as AggOr<S::Aggregate>>::Output>,
816 H::Aggregate: super::AggregateKind,
817 D::Aggregate: super::AggregateKind,
818 S::Aggregate: super::AggregateKind,
819{
820 SQLExpr::new(SQL::func(
821 "MAKE_TIMESTAMP",
822 year.into_sql()
823 .push(Token::COMMA)
824 .append(month.into_sql())
825 .push(Token::COMMA)
826 .append(day.into_sql())
827 .push(Token::COMMA)
828 .append(hour.into_sql())
829 .push(Token::COMMA)
830 .append(minute.into_sql())
831 .push(Token::COMMA)
832 .append(second.into_sql()),
833 ))
834}
835
836#[must_use]
853pub fn localtime<'a, V>()
854-> SQLExpr<'a, V, <V::DialectMarker as DialectTypes>::Time, super::NonNull, Scalar>
855where
856 V: SQLParam + 'a,
857 V::DialectMarker: PostgresDateTimeSupport,
858{
859 SQLExpr::new(SQL::raw("LOCALTIME"))
860}
861
862#[must_use]
875pub fn localtimestamp<'a, V>() -> SQLExpr<'a, V, PgTimestamp, super::NonNull, Scalar>
876where
877 V: SQLParam + 'a,
878 V::DialectMarker: PostgresDateTimeSupport,
879{
880 SQLExpr::new(SQL::raw("LOCALTIMESTAMP"))
881}
882
883#[must_use]
898pub fn clock_timestamp<'a, V>() -> SQLExpr<'a, V, PgTimestamptz, super::NonNull, Scalar>
899where
900 V: SQLParam + 'a,
901 V::DialectMarker: PostgresDateTimeSupport,
902{
903 SQLExpr::new(SQL::raw("CLOCK_TIMESTAMP()"))
904}
905
906#[allow(clippy::type_complexity)]
925pub fn timediff<'a, V, E1, E2>(
926 time1: E1,
927 time2: E2,
928) -> SQLExpr<
929 'a,
930 V,
931 <V::DialectMarker as DialectTypes>::Text,
932 <E1::Nullable as NullOr<E2::Nullable>>::Output,
933 <E1::Aggregate as AggOr<E2::Aggregate>>::Output,
934>
935where
936 V: SQLParam + 'a,
937 V::DialectMarker: SQLiteDateTimeSupport,
938 E1: Expr<'a, V>,
939 E1::SQLType: Temporal,
940 E2: Expr<'a, V>,
941 E2::SQLType: Temporal,
942 E1::Nullable: NullOr<E2::Nullable>,
943 E2::Nullable: Nullability,
944 E1::Aggregate: AggOr<E2::Aggregate>,
945{
946 SQLExpr::new(SQL::func(
947 "TIMEDIFF",
948 time1.into_sql().push(Token::COMMA).append(time2.into_sql()),
949 ))
950}