1use super::Dialect;
27use std::collections::HashMap;
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
31pub enum TimeFormatStyle {
32 Strftime,
34 Mysql,
36 Postgres,
38 Snowflake,
40 Java,
42 Tsql,
44 ClickHouse,
46}
47
48impl TimeFormatStyle {
49 #[must_use]
51 pub fn for_dialect(dialect: Dialect) -> Self {
52 match dialect {
53 Dialect::Ansi | Dialect::Sqlite | Dialect::BigQuery | Dialect::DuckDb => {
55 TimeFormatStyle::Strftime
56 }
57
58 Dialect::Mysql
60 | Dialect::Doris
61 | Dialect::SingleStore
62 | Dialect::StarRocks => TimeFormatStyle::Mysql,
63
64 Dialect::Postgres
66 | Dialect::Oracle
67 | Dialect::Redshift
68 | Dialect::Materialize
69 | Dialect::RisingWave
70 | Dialect::Exasol
71 | Dialect::Teradata => TimeFormatStyle::Postgres,
72
73 Dialect::Snowflake => TimeFormatStyle::Snowflake,
75
76 Dialect::Hive | Dialect::Spark | Dialect::Databricks => TimeFormatStyle::Java,
78
79 Dialect::Tsql | Dialect::Fabric => TimeFormatStyle::Tsql,
81
82 Dialect::Presto | Dialect::Trino | Dialect::Athena => TimeFormatStyle::Java,
84
85 Dialect::ClickHouse => TimeFormatStyle::ClickHouse,
87
88 Dialect::Dremio | Dialect::Drill | Dialect::Druid | Dialect::Tableau | Dialect::Prql => {
90 TimeFormatStyle::Strftime
91 }
92 }
93 }
94}
95
96#[derive(Debug, Clone)]
102struct FormatMapping {
103 strftime: &'static str,
104 mysql: &'static str,
105 postgres: &'static str,
106 snowflake: &'static str,
107 java: &'static str,
108 tsql: &'static str,
109 clickhouse: &'static str,
110}
111
112impl FormatMapping {
113 fn get(&self, style: TimeFormatStyle) -> &'static str {
115 match style {
116 TimeFormatStyle::Strftime => self.strftime,
117 TimeFormatStyle::Mysql => self.mysql,
118 TimeFormatStyle::Postgres => self.postgres,
119 TimeFormatStyle::Snowflake => self.snowflake,
120 TimeFormatStyle::Java => self.java,
121 TimeFormatStyle::Tsql => self.tsql,
122 TimeFormatStyle::ClickHouse => self.clickhouse,
123 }
124 }
125}
126
127fn build_format_mappings() -> Vec<FormatMapping> {
130 vec![
131 FormatMapping {
133 strftime: "%Y", mysql: "%Y",
135 postgres: "YYYY",
136 snowflake: "YYYY",
137 java: "yyyy",
138 tsql: "yyyy",
139 clickhouse: "%Y",
140 },
141 FormatMapping {
142 strftime: "%y", mysql: "%y",
144 postgres: "YY",
145 snowflake: "YY",
146 java: "yy",
147 tsql: "yy",
148 clickhouse: "%y",
149 },
150 FormatMapping {
152 strftime: "%m", mysql: "%m",
154 postgres: "MM",
155 snowflake: "MM",
156 java: "MM",
157 tsql: "MM",
158 clickhouse: "%m",
159 },
160 FormatMapping {
161 strftime: "%b", mysql: "%b",
163 postgres: "Mon",
164 snowflake: "MON",
165 java: "MMM",
166 tsql: "MMM",
167 clickhouse: "%b",
168 },
169 FormatMapping {
170 strftime: "%B", mysql: "%M",
172 postgres: "Month",
173 snowflake: "MMMM",
174 java: "MMMM",
175 tsql: "MMMM",
176 clickhouse: "%B",
177 },
178 FormatMapping {
180 strftime: "%d", mysql: "%d",
182 postgres: "DD",
183 snowflake: "DD",
184 java: "dd",
185 tsql: "dd",
186 clickhouse: "%d",
187 },
188 FormatMapping {
189 strftime: "%e", mysql: "%e",
191 postgres: "FMDD",
192 snowflake: "DD", java: "d",
194 tsql: "d",
195 clickhouse: "%e",
196 },
197 FormatMapping {
198 strftime: "%j", mysql: "%j",
200 postgres: "DDD",
201 snowflake: "DDD",
202 java: "DDD",
203 tsql: "", clickhouse: "%j",
205 },
206 FormatMapping {
208 strftime: "%a", mysql: "%a",
210 postgres: "Dy",
211 snowflake: "DY",
212 java: "EEE",
213 tsql: "ddd",
214 clickhouse: "%a",
215 },
216 FormatMapping {
217 strftime: "%A", mysql: "%W",
219 postgres: "Day",
220 snowflake: "DY", java: "EEEE",
222 tsql: "dddd",
223 clickhouse: "%A",
224 },
225 FormatMapping {
226 strftime: "%w", mysql: "%w",
228 postgres: "D",
229 snowflake: "D",
230 java: "e",
231 tsql: "",
232 clickhouse: "%w",
233 },
234 FormatMapping {
235 strftime: "%u", mysql: "%u",
237 postgres: "ID",
238 snowflake: "ID",
239 java: "u",
240 tsql: "",
241 clickhouse: "%u",
242 },
243 FormatMapping {
245 strftime: "%W", mysql: "%v", postgres: "IW",
248 snowflake: "WW",
249 java: "ww",
250 tsql: "ww",
251 clickhouse: "%V",
252 },
253 FormatMapping {
254 strftime: "%U", mysql: "%U",
256 postgres: "WW",
257 snowflake: "WW",
258 java: "ww",
259 tsql: "ww",
260 clickhouse: "%U",
261 },
262 FormatMapping {
264 strftime: "%H", mysql: "%H",
266 postgres: "HH24",
267 snowflake: "HH24",
268 java: "HH",
269 tsql: "HH",
270 clickhouse: "%H",
271 },
272 FormatMapping {
273 strftime: "%I", mysql: "%h",
275 postgres: "HH12",
276 snowflake: "HH12",
277 java: "hh",
278 tsql: "hh",
279 clickhouse: "%I",
280 },
281 FormatMapping {
283 strftime: "%M", mysql: "%i", postgres: "MI",
286 snowflake: "MI",
287 java: "mm",
288 tsql: "mm",
289 clickhouse: "%M",
290 },
291 FormatMapping {
293 strftime: "%S", mysql: "%s",
295 postgres: "SS",
296 snowflake: "SS",
297 java: "ss",
298 tsql: "ss",
299 clickhouse: "%S",
300 },
301 FormatMapping {
303 strftime: "%f", mysql: "%f",
305 postgres: "US", snowflake: "FF6",
307 java: "SSSSSS",
308 tsql: "ffffff",
309 clickhouse: "%f",
310 },
311 FormatMapping {
313 strftime: "%p", mysql: "%p",
315 postgres: "AM",
316 snowflake: "AM",
317 java: "a",
318 tsql: "tt",
319 clickhouse: "%p",
320 },
321 FormatMapping {
323 strftime: "%z", mysql: "", postgres: "OF",
326 snowflake: "TZH:TZM",
327 java: "Z",
328 tsql: "zzz",
329 clickhouse: "%z",
330 },
331 FormatMapping {
332 strftime: "%Z", mysql: "",
334 postgres: "TZ",
335 snowflake: "TZR",
336 java: "z",
337 tsql: "",
338 clickhouse: "%Z",
339 },
340 FormatMapping {
342 strftime: "%%", mysql: "%%",
344 postgres: "", snowflake: "",
346 java: "",
347 tsql: "",
348 clickhouse: "%%",
349 },
350 ]
351}
352
353fn get_format_mappings() -> &'static Vec<FormatMapping> {
355 use std::sync::OnceLock;
356 static MAPPINGS: OnceLock<Vec<FormatMapping>> = OnceLock::new();
357 MAPPINGS.get_or_init(build_format_mappings)
358}
359
360#[allow(dead_code)]
365fn build_style_lookup(style: TimeFormatStyle) -> HashMap<&'static str, usize> {
366 let mappings = get_format_mappings();
367 let mut lookup = HashMap::new();
368 for (i, mapping) in mappings.iter().enumerate() {
369 let spec = mapping.get(style);
370 if !spec.is_empty() {
371 lookup.insert(spec, i);
372 }
373 }
374 lookup
375}
376
377#[must_use]
403pub fn format_time(format_str: &str, source: TimeFormatStyle, target: TimeFormatStyle) -> String {
404 if source == target {
405 return format_str.to_string();
406 }
407
408 match source {
410 TimeFormatStyle::Strftime
411 | TimeFormatStyle::Mysql
412 | TimeFormatStyle::ClickHouse => convert_strftime_style(format_str, source, target),
413 TimeFormatStyle::Postgres => convert_postgres_style(format_str, target),
414 TimeFormatStyle::Snowflake => convert_snowflake_style(format_str, target),
415 TimeFormatStyle::Java | TimeFormatStyle::Tsql => convert_java_style(format_str, source, target),
416 }
417}
418
419fn convert_strftime_style(format_str: &str, source: TimeFormatStyle, target: TimeFormatStyle) -> String {
421 let mappings = get_format_mappings();
422 let mut result = String::with_capacity(format_str.len() * 2);
423 let mut chars = format_str.chars().peekable();
424
425 while let Some(ch) = chars.next() {
426 if ch == '%' {
427 if let Some(&next) = chars.peek() {
428 chars.next();
429 let spec = format!("%{}", next);
430
431 let mapped = mappings.iter().find(|m| m.get(source) == spec);
433
434 if let Some(mapping) = mapped {
435 let target_spec = mapping.get(target);
436 if target_spec.is_empty() {
437 result.push_str(&spec);
439 } else {
440 result.push_str(target_spec);
441 }
442 } else {
443 result.push_str(&spec);
445 }
446 } else {
447 result.push('%');
449 }
450 } else {
451 result.push(ch);
452 }
453 }
454
455 result
456}
457
458fn convert_postgres_style(format_str: &str, target: TimeFormatStyle) -> String {
460 let mappings = get_format_mappings();
461 let mut result = String::with_capacity(format_str.len() * 2);
462 let chars: Vec<char> = format_str.chars().collect();
463 let mut i = 0;
464
465 let pg_specifiers: &[&str] = &[
467 "YYYY", "MMMM", "Month", "Mon", "MM", "DDD", "DD", "Day", "Dy", "D",
468 "HH24", "HH12", "HH", "MI", "SS", "US", "AM", "PM", "TZH:TZM", "TZR",
469 "TZ", "OF", "IW", "WW", "YY", "ID", "FMDD",
470 ];
471
472 while i < chars.len() {
473 let remaining: String = chars[i..].iter().collect();
474 let mut matched = false;
475
476 for spec in pg_specifiers {
478 if remaining.starts_with(spec) || remaining.to_uppercase().starts_with(&spec.to_uppercase()) {
479 let mapping = mappings.iter().find(|m|
481 m.postgres.eq_ignore_ascii_case(spec)
482 );
483
484 if let Some(m) = mapping {
485 let target_spec = m.get(target);
486 if !target_spec.is_empty() {
487 result.push_str(target_spec);
488 } else {
489 result.push_str(spec);
490 }
491 } else {
492 result.push_str(spec);
493 }
494 i += spec.len();
495 matched = true;
496 break;
497 }
498 }
499
500 if !matched {
501 if chars[i] == '"' {
503 result.push(chars[i]);
504 i += 1;
505 while i < chars.len() && chars[i] != '"' {
506 result.push(chars[i]);
507 i += 1;
508 }
509 if i < chars.len() {
510 result.push(chars[i]); i += 1;
512 }
513 } else {
514 result.push(chars[i]);
515 i += 1;
516 }
517 }
518 }
519
520 result
521}
522
523fn convert_snowflake_style(format_str: &str, target: TimeFormatStyle) -> String {
525 let mappings = get_format_mappings();
526 let mut result = String::with_capacity(format_str.len() * 2);
527 let chars: Vec<char> = format_str.chars().collect();
528 let mut i = 0;
529
530 let sf_specifiers: &[&str] = &[
532 "YYYY", "MMMM", "MON", "MM", "DDD", "DD", "DY", "D",
533 "HH24", "HH12", "HH", "MI", "SS", "FF6", "FF3", "FF",
534 "AM", "PM", "TZH:TZM", "TZR", "WW", "YY", "ID",
535 ];
536
537 while i < chars.len() {
538 let remaining: String = chars[i..].iter().collect();
539 let mut matched = false;
540
541 for spec in sf_specifiers {
542 if remaining.starts_with(spec) || remaining.to_uppercase().starts_with(&spec.to_uppercase()) {
543 let mapping = mappings.iter().find(|m|
544 m.snowflake.eq_ignore_ascii_case(spec)
545 );
546
547 if let Some(m) = mapping {
548 let target_spec = m.get(target);
549 if !target_spec.is_empty() {
550 result.push_str(target_spec);
551 } else {
552 result.push_str(spec);
553 }
554 } else {
555 result.push_str(spec);
556 }
557 i += spec.len();
558 matched = true;
559 break;
560 }
561 }
562
563 if !matched {
564 if chars[i] == '"' {
566 result.push(chars[i]);
567 i += 1;
568 while i < chars.len() && chars[i] != '"' {
569 result.push(chars[i]);
570 i += 1;
571 }
572 if i < chars.len() {
573 result.push(chars[i]);
574 i += 1;
575 }
576 } else {
577 result.push(chars[i]);
578 i += 1;
579 }
580 }
581 }
582
583 result
584}
585
586fn convert_java_style(format_str: &str, source: TimeFormatStyle, target: TimeFormatStyle) -> String {
588 let mappings = get_format_mappings();
589 let mut result = String::with_capacity(format_str.len() * 2);
590 let chars: Vec<char> = format_str.chars().collect();
591 let mut i = 0;
592
593 let java_specifiers: &[&str] = &[
595 "yyyy", "YYYY", "yy", "YY",
596 "MMMM", "MMM", "MM", "M",
597 "dd", "d", "DDD",
598 "EEEE", "EEE", "e", "u",
599 "HH", "hh", "H", "h",
600 "mm", "m",
601 "ss", "s",
602 "SSSSSS", "SSS", "SS", "S",
603 "a", "Z", "z",
604 "ww",
605 ];
606
607 while i < chars.len() {
608 let remaining: String = chars[i..].iter().collect();
609 let mut matched = false;
610
611 if chars[i] == '\'' {
613 result.push(chars[i]);
614 i += 1;
615 while i < chars.len() && chars[i] != '\'' {
616 result.push(chars[i]);
617 i += 1;
618 }
619 if i < chars.len() {
620 result.push(chars[i]);
621 i += 1;
622 }
623 continue;
624 }
625
626 for spec in java_specifiers {
627 if remaining.starts_with(spec) {
628 let mapping = mappings.iter().find(|m| {
629 let src_spec = m.get(source);
630 src_spec == *spec
631 });
632
633 if let Some(m) = mapping {
634 let target_spec = m.get(target);
635 if !target_spec.is_empty() {
636 result.push_str(target_spec);
637 } else {
638 result.push_str(spec);
639 }
640 } else {
641 result.push_str(spec);
642 }
643 i += spec.len();
644 matched = true;
645 break;
646 }
647 }
648
649 if !matched {
650 result.push(chars[i]);
651 i += 1;
652 }
653 }
654
655 result
656}
657
658#[must_use]
687pub fn format_time_dialect(format_str: &str, source_dialect: Dialect, target_dialect: Dialect) -> String {
688 let source_style = TimeFormatStyle::for_dialect(source_dialect);
689 let target_style = TimeFormatStyle::for_dialect(target_dialect);
690 format_time(format_str, source_style, target_style)
691}
692
693#[derive(Debug, Clone, Copy, PartialEq, Eq)]
702pub enum TsqlStyleCode {
703 Default100 = 100,
705 UsaDate = 101,
707 AnsiDate = 102,
709 BritishDate = 103,
711 GermanDate = 104,
713 ItalianDate = 105,
715 DayMonYear = 106,
717 MonDayYear = 107,
719 TimeOnly = 108,
721 UsaDashes = 110,
723 JapanDate = 111,
725 IsoBasic = 112,
727 TimeWithMs = 114,
729 OdbcCanonical = 120,
731 OdbcWithMs = 121,
733 Iso8601 = 126,
735 Iso8601Tz = 127,
737}
738
739impl TsqlStyleCode {
740 #[must_use]
744 pub fn to_format_pattern(&self) -> &'static str {
745 match self {
746 TsqlStyleCode::Default100 => "%b %d %Y %I:%M%p",
747 TsqlStyleCode::UsaDate => "%m/%d/%Y",
748 TsqlStyleCode::AnsiDate => "%Y.%m.%d",
749 TsqlStyleCode::BritishDate => "%d/%m/%Y",
750 TsqlStyleCode::GermanDate => "%d.%m.%Y",
751 TsqlStyleCode::ItalianDate => "%d-%m-%Y",
752 TsqlStyleCode::DayMonYear => "%d %b %Y",
753 TsqlStyleCode::MonDayYear => "%b %d, %Y",
754 TsqlStyleCode::TimeOnly => "%H:%M:%S",
755 TsqlStyleCode::UsaDashes => "%m-%d-%Y",
756 TsqlStyleCode::JapanDate => "%Y/%m/%d",
757 TsqlStyleCode::IsoBasic => "%Y%m%d",
758 TsqlStyleCode::TimeWithMs => "%H:%M:%S:%f",
759 TsqlStyleCode::OdbcCanonical => "%Y-%m-%d %H:%M:%S",
760 TsqlStyleCode::OdbcWithMs => "%Y-%m-%d %H:%M:%S.%f",
761 TsqlStyleCode::Iso8601 => "%Y-%m-%dT%H:%M:%S.%f",
762 TsqlStyleCode::Iso8601Tz => "%Y-%m-%dT%H:%M:%S.%fZ",
763 }
764 }
765
766 pub fn from_code(code: i32) -> Option<Self> {
768 match code {
769 100 => Some(TsqlStyleCode::Default100),
770 101 => Some(TsqlStyleCode::UsaDate),
771 102 => Some(TsqlStyleCode::AnsiDate),
772 103 => Some(TsqlStyleCode::BritishDate),
773 104 => Some(TsqlStyleCode::GermanDate),
774 105 => Some(TsqlStyleCode::ItalianDate),
775 106 => Some(TsqlStyleCode::DayMonYear),
776 107 => Some(TsqlStyleCode::MonDayYear),
777 108 => Some(TsqlStyleCode::TimeOnly),
778 110 => Some(TsqlStyleCode::UsaDashes),
779 111 => Some(TsqlStyleCode::JapanDate),
780 112 => Some(TsqlStyleCode::IsoBasic),
781 114 => Some(TsqlStyleCode::TimeWithMs),
782 120 => Some(TsqlStyleCode::OdbcCanonical),
783 121 => Some(TsqlStyleCode::OdbcWithMs),
784 126 => Some(TsqlStyleCode::Iso8601),
785 127 => Some(TsqlStyleCode::Iso8601Tz),
786 _ => None,
787 }
788 }
789
790 pub fn code(&self) -> i32 {
792 *self as i32
793 }
794}
795
796#[derive(Debug, Clone)]
802pub struct FormatConversionResult {
803 pub format: String,
805 pub warnings: Vec<String>,
807}
808
809#[must_use]
814pub fn format_time_with_warnings(
815 format_str: &str,
816 source: TimeFormatStyle,
817 target: TimeFormatStyle,
818) -> FormatConversionResult {
819 let mut warnings = Vec::new();
820 let mappings = get_format_mappings();
821
822 match source {
824 TimeFormatStyle::Strftime | TimeFormatStyle::Mysql | TimeFormatStyle::ClickHouse => {
825 let mut chars = format_str.chars().peekable();
826 while let Some(ch) = chars.next() {
827 if ch == '%'
828 && let Some(&next) = chars.peek()
829 {
830 chars.next();
831 let spec = format!("%{}", next);
832 let mapping = mappings.iter().find(|m| m.get(source) == spec);
833 if let Some(m) = mapping
834 && m.get(target).is_empty()
835 {
836 warnings.push(format!(
837 "Format specifier '{}' has no equivalent in target format",
838 spec
839 ));
840 }
841 }
842 }
843 }
844 _ => {
845 }
848 }
849
850 let format = format_time(format_str, source, target);
851 FormatConversionResult { format, warnings }
852}
853
854#[cfg(test)]
855mod tests {
856 use super::*;
857
858 #[test]
859 fn test_strftime_to_postgres() {
860 assert_eq!(
861 format_time("%Y-%m-%d", TimeFormatStyle::Strftime, TimeFormatStyle::Postgres),
862 "YYYY-MM-DD"
863 );
864 assert_eq!(
865 format_time("%H:%M:%S", TimeFormatStyle::Strftime, TimeFormatStyle::Postgres),
866 "HH24:MI:SS"
867 );
868 assert_eq!(
869 format_time("%Y-%m-%d %H:%M:%S", TimeFormatStyle::Strftime, TimeFormatStyle::Postgres),
870 "YYYY-MM-DD HH24:MI:SS"
871 );
872 }
873
874 #[test]
875 fn test_mysql_to_postgres() {
876 assert_eq!(
878 format_time("%Y-%m-%d %H:%i:%s", TimeFormatStyle::Mysql, TimeFormatStyle::Postgres),
879 "YYYY-MM-DD HH24:MI:SS"
880 );
881 }
882
883 #[test]
884 fn test_postgres_to_mysql() {
885 assert_eq!(
886 format_time("YYYY-MM-DD HH24:MI:SS", TimeFormatStyle::Postgres, TimeFormatStyle::Mysql),
887 "%Y-%m-%d %H:%i:%s"
888 );
889 }
890
891 #[test]
892 fn test_postgres_to_strftime() {
893 assert_eq!(
894 format_time("YYYY-MM-DD", TimeFormatStyle::Postgres, TimeFormatStyle::Strftime),
895 "%Y-%m-%d"
896 );
897 }
898
899 #[test]
900 fn test_strftime_to_java() {
901 assert_eq!(
902 format_time("%Y-%m-%d", TimeFormatStyle::Strftime, TimeFormatStyle::Java),
903 "yyyy-MM-dd"
904 );
905 assert_eq!(
906 format_time("%H:%M:%S", TimeFormatStyle::Strftime, TimeFormatStyle::Java),
907 "HH:mm:ss"
908 );
909 }
910
911 #[test]
912 fn test_java_to_strftime() {
913 assert_eq!(
914 format_time("yyyy-MM-dd", TimeFormatStyle::Java, TimeFormatStyle::Strftime),
915 "%Y-%m-%d"
916 );
917 assert_eq!(
918 format_time("HH:mm:ss", TimeFormatStyle::Java, TimeFormatStyle::Strftime),
919 "%H:%M:%S"
920 );
921 }
922
923 #[test]
924 fn test_strftime_to_snowflake() {
925 assert_eq!(
926 format_time("%Y-%m-%d", TimeFormatStyle::Strftime, TimeFormatStyle::Snowflake),
927 "YYYY-MM-DD"
928 );
929 }
930
931 #[test]
932 fn test_same_style_noop() {
933 let format = "%Y-%m-%d %H:%M:%S";
934 assert_eq!(
935 format_time(format, TimeFormatStyle::Strftime, TimeFormatStyle::Strftime),
936 format
937 );
938 }
939
940 #[test]
941 fn test_dialect_conversion() {
942 assert_eq!(
943 format_time_dialect("%Y-%m-%d %H:%i:%s", Dialect::Mysql, Dialect::Postgres),
944 "YYYY-MM-DD HH24:MI:SS"
945 );
946 assert_eq!(
947 format_time_dialect("YYYY-MM-DD HH24:MI:SS", Dialect::Postgres, Dialect::Spark),
948 "yyyy-MM-dd HH:mm:ss"
949 );
950 }
951
952 #[test]
953 fn test_literal_preservation() {
954 assert_eq!(
956 format_time("%Y/%m/%d", TimeFormatStyle::Strftime, TimeFormatStyle::Postgres),
957 "YYYY/MM/DD"
958 );
959 assert_eq!(
960 format_time("%Y at %H:%M", TimeFormatStyle::Strftime, TimeFormatStyle::Postgres),
961 "YYYY at HH24:MI"
962 );
963 }
964
965 #[test]
966 fn test_tsql_style_codes() {
967 assert_eq!(TsqlStyleCode::OdbcCanonical.to_format_pattern(), "%Y-%m-%d %H:%M:%S");
968 assert_eq!(TsqlStyleCode::UsaDate.to_format_pattern(), "%m/%d/%Y");
969 assert_eq!(TsqlStyleCode::from_code(120), Some(TsqlStyleCode::OdbcCanonical));
970 assert_eq!(TsqlStyleCode::from_code(999), None);
971 }
972
973 #[test]
974 fn test_12hour_format() {
975 assert_eq!(
976 format_time("%I:%M %p", TimeFormatStyle::Strftime, TimeFormatStyle::Postgres),
977 "HH12:MI AM"
978 );
979 }
980
981 #[test]
982 fn test_month_names() {
983 assert_eq!(
984 format_time("%b %d, %Y", TimeFormatStyle::Strftime, TimeFormatStyle::Postgres),
985 "Mon DD, YYYY"
986 );
987 assert_eq!(
988 format_time("%B", TimeFormatStyle::Strftime, TimeFormatStyle::Mysql),
989 "%M"
990 );
991 }
992
993 #[test]
994 fn test_format_style_for_dialect() {
995 assert_eq!(TimeFormatStyle::for_dialect(Dialect::Mysql), TimeFormatStyle::Mysql);
996 assert_eq!(TimeFormatStyle::for_dialect(Dialect::Postgres), TimeFormatStyle::Postgres);
997 assert_eq!(TimeFormatStyle::for_dialect(Dialect::Spark), TimeFormatStyle::Java);
998 assert_eq!(TimeFormatStyle::for_dialect(Dialect::Snowflake), TimeFormatStyle::Snowflake);
999 assert_eq!(TimeFormatStyle::for_dialect(Dialect::BigQuery), TimeFormatStyle::Strftime);
1000 }
1001}