DTZ rules: Clarify error messages and docs (#10621)

- Clearly state in the documentation that passing `tz=None` is just as bad as not passing a `tz=` argument, from the perspective of these rules.
- Clearly state in the error messages exactly what the user is doing wrong, if the user is passing `tz=None` rather than failing to pass a `tz=` argument at all.
- Make error messages more concise, and separate out the suggested remedy from the thing that the user is identified as doing wrong.

Co-authored-by: Christian Clauss <cclauss@me.com>
This commit is contained in:
Alex Waygood 2024-03-27 19:42:13 +00:00 committed by GitHub
parent f9d0c6d9ae
commit abbefae6f1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 255 additions and 219 deletions

View file

@ -18,7 +18,7 @@ use crate::checkers::ast::Checker;
/// always use timezone-aware objects. /// always use timezone-aware objects.
/// ///
/// `datetime.date.fromtimestamp(ts)` returns a naive datetime object. /// `datetime.date.fromtimestamp(ts)` returns a naive datetime object.
/// Instead, use `datetime.datetime.fromtimestamp(ts, tz=)` to return a /// Instead, use `datetime.datetime.fromtimestamp(ts, tz=...)` to create a
/// timezone-aware object. /// timezone-aware object.
/// ///
/// ## Example /// ## Example
@ -50,10 +50,11 @@ pub struct CallDateFromtimestamp;
impl Violation for CallDateFromtimestamp { impl Violation for CallDateFromtimestamp {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
format!( format!("`datetime.date.fromtimestamp()` used")
"The use of `datetime.date.fromtimestamp()` is not allowed, use \ }
`datetime.datetime.fromtimestamp(ts, tz=).date()` instead"
) fn fix_title(&self) -> Option<String> {
Some("Use `datetime.datetime.fromtimestamp(ts, tz=...).date()` instead".to_string())
} }
} }

View file

@ -18,7 +18,7 @@ use crate::checkers::ast::Checker;
/// always use timezone-aware objects. /// always use timezone-aware objects.
/// ///
/// `datetime.date.today` returns a naive datetime object. Instead, use /// `datetime.date.today` returns a naive datetime object. Instead, use
/// `datetime.datetime.now(tz=).date()` to return a timezone-aware object. /// `datetime.datetime.now(tz=...).date()` to create a timezone-aware object.
/// ///
/// ## Example /// ## Example
/// ```python /// ```python
@ -49,10 +49,11 @@ pub struct CallDateToday;
impl Violation for CallDateToday { impl Violation for CallDateToday {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
format!( format!("`datetime.date.today()` used")
"The use of `datetime.date.today()` is not allowed, use \ }
`datetime.datetime.now(tz=).date()` instead"
) fn fix_title(&self) -> Option<String> {
Some("Use `datetime.datetime.now(tz=...).date()` instead".to_string())
} }
} }

View file

@ -3,16 +3,14 @@ use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast}; use ruff_python_ast::{self as ast};
use ruff_python_semantic::Modules; use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::rules::flake8_datetimez::rules::helpers::has_non_none_keyword;
use super::helpers; use super::helpers::{self, DatetimeModuleAntipattern};
/// ## What it does /// ## What it does
/// Checks for usage of `datetime.datetime.fromtimestamp()` without a `tz` /// Checks for usage of `datetime.datetime.fromtimestamp()` that do not specify
/// argument. /// a timezone.
/// ///
/// ## Why is this bad? /// ## Why is this bad?
/// Python datetime objects can be naive or timezone-aware. While an aware /// Python datetime objects can be naive or timezone-aware. While an aware
@ -21,9 +19,10 @@ use super::helpers;
/// datetime objects. Since this can lead to errors, it is recommended to /// datetime objects. Since this can lead to errors, it is recommended to
/// always use timezone-aware objects. /// always use timezone-aware objects.
/// ///
/// `datetime.datetime.fromtimestamp(ts)` returns a naive datetime object. /// `datetime.datetime.fromtimestamp(ts)` or
/// Instead, use `datetime.datetime.fromtimestamp(ts, tz=)` to return a /// `datetime.datetime.fromtimestampe(ts, tz=None)` returns a naive datetime
/// timezone-aware object. /// object. Instead, use `datetime.datetime.fromtimestamp(ts, tz=<timezone>)`
/// to create a timezone-aware object.
/// ///
/// ## Example /// ## Example
/// ```python /// ```python
@ -39,7 +38,7 @@ use super::helpers;
/// datetime.datetime.fromtimestamp(946684800, tz=datetime.timezone.utc) /// datetime.datetime.fromtimestamp(946684800, tz=datetime.timezone.utc)
/// ``` /// ```
/// ///
/// Or, for Python 3.11 and later: /// Or, on Python 3.11 and later:
/// ```python /// ```python
/// import datetime /// import datetime
/// ///
@ -49,14 +48,24 @@ use super::helpers;
/// ## References /// ## References
/// - [Python documentation: Aware and Naive Objects](https://docs.python.org/3/library/datetime.html#aware-and-naive-objects) /// - [Python documentation: Aware and Naive Objects](https://docs.python.org/3/library/datetime.html#aware-and-naive-objects)
#[violation] #[violation]
pub struct CallDatetimeFromtimestamp; pub struct CallDatetimeFromtimestamp(DatetimeModuleAntipattern);
impl Violation for CallDatetimeFromtimestamp { impl Violation for CallDatetimeFromtimestamp {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
format!( let CallDatetimeFromtimestamp(antipattern) = self;
"The use of `datetime.datetime.fromtimestamp()` without `tz` argument is not allowed" match antipattern {
) DatetimeModuleAntipattern::NoTzArgumentPassed => {
format!("`datetime.datetime.fromtimestamp()` called without a `tz` argument")
}
DatetimeModuleAntipattern::NonePassedToTzArgument => {
format!("`tz=None` passed to `datetime.datetime.fromtimestamp()`")
}
}
}
fn fix_title(&self) -> Option<String> {
Some("Pass a `datetime.timezone` object to the `tz` parameter".to_string())
} }
} }
@ -82,26 +91,14 @@ pub(crate) fn call_datetime_fromtimestamp(checker: &mut Checker, call: &ast::Exp
return; return;
} }
// no args / no args unqualified let antipattern = match call.arguments.find_argument("tz", 1) {
if call.arguments.args.len() < 2 && call.arguments.keywords.is_empty() { Some(ast::Expr::NoneLiteral(_)) => DatetimeModuleAntipattern::NonePassedToTzArgument,
checker Some(_) => return,
.diagnostics None => DatetimeModuleAntipattern::NoTzArgumentPassed,
.push(Diagnostic::new(CallDatetimeFromtimestamp, call.range())); };
return;
}
// none args checker.diagnostics.push(Diagnostic::new(
if call.arguments.args.len() > 1 && call.arguments.args[1].is_none_literal_expr() { CallDatetimeFromtimestamp(antipattern),
checker call.range,
.diagnostics ));
.push(Diagnostic::new(CallDatetimeFromtimestamp, call.range()));
return;
}
// wrong keywords / none keyword
if !call.arguments.keywords.is_empty() && !has_non_none_keyword(&call.arguments, "tz") {
checker
.diagnostics
.push(Diagnostic::new(CallDatetimeFromtimestamp, call.range()));
}
} }

View file

@ -1,17 +1,15 @@
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, Expr}; use ruff_python_ast as ast;
use ruff_python_semantic::Modules; use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::rules::flake8_datetimez::rules::helpers::has_non_none_keyword;
use super::helpers; use super::helpers::{self, DatetimeModuleAntipattern};
/// ## What it does /// ## What it does
/// Checks for usage of `datetime.datetime.now()` without a `tz` argument. /// Checks for usages of `datetime.datetime.now()` that do not specify a timezone.
/// ///
/// ## Why is this bad? /// ## Why is this bad?
/// Python datetime objects can be naive or timezone-aware. While an aware /// Python datetime objects can be naive or timezone-aware. While an aware
@ -20,8 +18,9 @@ use super::helpers;
/// datetime objects. Since this can lead to errors, it is recommended to /// datetime objects. Since this can lead to errors, it is recommended to
/// always use timezone-aware objects. /// always use timezone-aware objects.
/// ///
/// `datetime.datetime.now()` returns a naive datetime object. Instead, use /// `datetime.datetime.now()` or `datetime.datetime.now(tz=None)` returns a naive
/// `datetime.datetime.now(tz=)` to return a timezone-aware object. /// datetime object. Instead, use `datetime.datetime.now(tz=<timezone>)` to create
/// a timezone-aware object.
/// ///
/// ## Example /// ## Example
/// ```python /// ```python
@ -47,12 +46,24 @@ use super::helpers;
/// ## References /// ## References
/// - [Python documentation: Aware and Naive Objects](https://docs.python.org/3/library/datetime.html#aware-and-naive-objects) /// - [Python documentation: Aware and Naive Objects](https://docs.python.org/3/library/datetime.html#aware-and-naive-objects)
#[violation] #[violation]
pub struct CallDatetimeNowWithoutTzinfo; pub struct CallDatetimeNowWithoutTzinfo(DatetimeModuleAntipattern);
impl Violation for CallDatetimeNowWithoutTzinfo { impl Violation for CallDatetimeNowWithoutTzinfo {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
format!("The use of `datetime.datetime.now()` without `tz` argument is not allowed") let CallDatetimeNowWithoutTzinfo(antipattern) = self;
match antipattern {
DatetimeModuleAntipattern::NoTzArgumentPassed => {
format!("`datetime.datetime.now()` called without a `tz` argument")
}
DatetimeModuleAntipattern::NonePassedToTzArgument => {
format!("`tz=None` passed to `datetime.datetime.now()`")
}
}
}
fn fix_title(&self) -> Option<String> {
Some("Pass a `datetime.timezone` object to the `tz` parameter".to_string())
} }
} }
@ -75,31 +86,14 @@ pub(crate) fn call_datetime_now_without_tzinfo(checker: &mut Checker, call: &ast
return; return;
} }
// no args / no args unqualified let antipattern = match call.arguments.find_argument("tz", 0) {
if call.arguments.args.is_empty() && call.arguments.keywords.is_empty() { Some(ast::Expr::NoneLiteral(_)) => DatetimeModuleAntipattern::NonePassedToTzArgument,
checker Some(_) => return,
.diagnostics None => DatetimeModuleAntipattern::NoTzArgumentPassed,
.push(Diagnostic::new(CallDatetimeNowWithoutTzinfo, call.range())); };
return;
}
// none args checker.diagnostics.push(Diagnostic::new(
if call CallDatetimeNowWithoutTzinfo(antipattern),
.arguments call.range,
.args ));
.first()
.is_some_and(Expr::is_none_literal_expr)
{
checker
.diagnostics
.push(Diagnostic::new(CallDatetimeNowWithoutTzinfo, call.range()));
return;
}
// wrong keywords / none keyword
if !call.arguments.keywords.is_empty() && !has_non_none_keyword(&call.arguments, "tz") {
checker
.diagnostics
.push(Diagnostic::new(CallDatetimeNowWithoutTzinfo, call.range()));
}
} }

View file

@ -2,10 +2,10 @@ use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, Expr}; use ruff_python_ast::{self as ast, Expr};
use ruff_python_semantic::Modules; use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::rules::flake8_datetimez::rules::helpers::has_non_none_keyword;
use super::helpers::DatetimeModuleAntipattern;
/// ## What it does /// ## What it does
/// Checks for uses of `datetime.datetime.strptime()` that lead to naive /// Checks for uses of `datetime.datetime.strptime()` that lead to naive
@ -19,7 +19,7 @@ use crate::rules::flake8_datetimez::rules::helpers::has_non_none_keyword;
/// always use timezone-aware objects. /// always use timezone-aware objects.
/// ///
/// `datetime.datetime.strptime()` without `%z` returns a naive datetime /// `datetime.datetime.strptime()` without `%z` returns a naive datetime
/// object. Follow it with `.replace(tzinfo=)` or `.astimezone()`. /// object. Follow it with `.replace(tzinfo=<timezone>)` or `.astimezone()`.
/// ///
/// ## Example /// ## Example
/// ```python /// ```python
@ -28,7 +28,7 @@ use crate::rules::flake8_datetimez::rules::helpers::has_non_none_keyword;
/// datetime.datetime.strptime("2022/01/31", "%Y/%m/%d") /// datetime.datetime.strptime("2022/01/31", "%Y/%m/%d")
/// ``` /// ```
/// ///
/// Instead, use `.replace(tzinfo=)`: /// Instead, use `.replace(tzinfo=<timezone>)`:
/// ```python /// ```python
/// import datetime /// import datetime
/// ///
@ -51,15 +51,34 @@ use crate::rules::flake8_datetimez::rules::helpers::has_non_none_keyword;
/// - [Python documentation: Aware and Naive Objects](https://docs.python.org/3/library/datetime.html#aware-and-naive-objects) /// - [Python documentation: Aware and Naive Objects](https://docs.python.org/3/library/datetime.html#aware-and-naive-objects)
/// - [Python documentation: `strftime()` and `strptime()` Behavior](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior) /// - [Python documentation: `strftime()` and `strptime()` Behavior](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior)
#[violation] #[violation]
pub struct CallDatetimeStrptimeWithoutZone; pub struct CallDatetimeStrptimeWithoutZone(DatetimeModuleAntipattern);
impl Violation for CallDatetimeStrptimeWithoutZone { impl Violation for CallDatetimeStrptimeWithoutZone {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
format!( let CallDatetimeStrptimeWithoutZone(antipattern) = self;
"The use of `datetime.datetime.strptime()` without %z must be followed by \ match antipattern {
`.replace(tzinfo=)` or `.astimezone()`" DatetimeModuleAntipattern::NoTzArgumentPassed => format!(
) "Naive datetime constructed using `datetime.datetime.strptime()` without %z"
),
DatetimeModuleAntipattern::NonePassedToTzArgument => {
format!("`datetime.datetime.strptime(...).replace(tz=None)` used")
}
}
}
fn fix_title(&self) -> Option<String> {
let CallDatetimeStrptimeWithoutZone(antipattern) = self;
match antipattern {
DatetimeModuleAntipattern::NoTzArgumentPassed => Some(
"Call `.replace(tzinfo=<timezone>)` or `.astimezone()` \
to convert to an aware datetime"
.to_string(),
),
DatetimeModuleAntipattern::NonePassedToTzArgument => {
Some("Pass a `datetime.timezone` object to the `tzinfo` parameter".to_string())
}
}
} }
} }
@ -91,36 +110,44 @@ pub(crate) fn call_datetime_strptime_without_zone(checker: &mut Checker, call: &
} }
}; };
let (Some(grandparent), Some(parent)) = ( let semantic = checker.semantic();
checker.semantic().current_expression_grandparent(), if let Some(antipattern) = find_antipattern(
checker.semantic().current_expression_parent(), semantic.current_expression_grandparent(),
) else { semantic.current_expression_parent(),
) {
checker.diagnostics.push(Diagnostic::new( checker.diagnostics.push(Diagnostic::new(
CallDatetimeStrptimeWithoutZone, CallDatetimeStrptimeWithoutZone(antipattern),
call.range(), call.range,
)); ));
return; }
}; }
if let Expr::Call(ast::ExprCall { arguments, .. }) = grandparent { fn find_antipattern(
if let Expr::Attribute(ast::ExprAttribute { attr, .. }) = parent { grandparent: Option<&Expr>,
let attr = attr.as_str(); parent: Option<&Expr>,
) -> Option<DatetimeModuleAntipattern> {
let Some(Expr::Call(ast::ExprCall { arguments, .. })) = grandparent else {
return Some(DatetimeModuleAntipattern::NoTzArgumentPassed);
};
let Some(Expr::Attribute(ast::ExprAttribute { attr, .. })) = parent else {
return Some(DatetimeModuleAntipattern::NoTzArgumentPassed);
};
// Ex) `datetime.strptime(...).astimezone()` // Ex) `datetime.strptime(...).astimezone()`
if attr == "astimezone" { if attr == "astimezone" {
return; return None;
} }
if attr != "replace" {
// Ex) `datetime.strptime(...).replace(tzinfo=UTC)` return Some(DatetimeModuleAntipattern::NoTzArgumentPassed);
if attr == "replace" { }
if has_non_none_keyword(arguments, "tzinfo") { match arguments.find_keyword("tzinfo") {
return; // Ex) `datetime.strptime(...).replace(tzinfo=None)`
Some(ast::Keyword {
value: Expr::NoneLiteral(_),
..
}) => Some(DatetimeModuleAntipattern::NonePassedToTzArgument),
// Ex) `datetime.strptime(...).replace(tzinfo=...)`
Some(_) => None,
// Ex) `datetime.strptime(...).replace(...)` with no `tzinfo` argument
None => Some(DatetimeModuleAntipattern::NoTzArgumentPassed),
} }
} }
}
}
checker.diagnostics.push(Diagnostic::new(
CallDatetimeStrptimeWithoutZone,
call.range(),
));
}

View file

@ -20,7 +20,7 @@ use super::helpers;
/// time, unlike "naive" objects. /// time, unlike "naive" objects.
/// ///
/// `datetime.datetime.today()` creates a "naive" object; instead, use /// `datetime.datetime.today()` creates a "naive" object; instead, use
/// `datetime.datetime.now(tz=)` to create a timezone-aware object. /// `datetime.datetime.now(tz=...)` to create a timezone-aware object.
/// ///
/// ## Example /// ## Example
/// ```python /// ```python
@ -48,10 +48,11 @@ pub struct CallDatetimeToday;
impl Violation for CallDatetimeToday { impl Violation for CallDatetimeToday {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
format!( format!("`datetime.datetime.today()` used")
"The use of `datetime.datetime.today()` is not allowed, use \ }
`datetime.datetime.now(tz=)` instead"
) fn fix_title(&self) -> Option<String> {
Some("Use `datetime.datetime.now(tz=...)` instead".to_string())
} }
} }

View file

@ -19,15 +19,15 @@ use super::helpers;
/// datetime objects. Since this can lead to errors, it is recommended to /// datetime objects. Since this can lead to errors, it is recommended to
/// always use timezone-aware objects. /// always use timezone-aware objects.
/// ///
/// `datetime.datetime.utcfromtimestamp()` returns a naive datetime object; /// `datetime.datetime.utcfromtimestamp()` returns a naive datetime
/// instead, use `datetime.datetime.fromtimestamp(ts, tz=)` to return a /// object; instead, use `datetime.datetime.fromtimestamp(ts, tz=...)`
/// timezone-aware object. /// to create a timezone-aware object.
/// ///
/// ## Example /// ## Example
/// ```python /// ```python
/// import datetime /// import datetime
/// ///
/// datetime.datetime.utcfromtimestamp() /// datetime.datetime.utcfromtimestamp(946684800)
/// ``` /// ```
/// ///
/// Use instead: /// Use instead:
@ -37,7 +37,7 @@ use super::helpers;
/// datetime.datetime.fromtimestamp(946684800, tz=datetime.timezone.utc) /// datetime.datetime.fromtimestamp(946684800, tz=datetime.timezone.utc)
/// ``` /// ```
/// ///
/// Or, for Python 3.11 and later: /// Or, on Python 3.11 and later:
/// ```python /// ```python
/// import datetime /// import datetime
/// ///
@ -52,10 +52,11 @@ pub struct CallDatetimeUtcfromtimestamp;
impl Violation for CallDatetimeUtcfromtimestamp { impl Violation for CallDatetimeUtcfromtimestamp {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
format!( format!("`datetime.datetime.utcfromtimestamp()` used")
"The use of `datetime.datetime.utcfromtimestamp()` is not allowed, use \ }
`datetime.datetime.fromtimestamp(ts, tz=)` instead"
) fn fix_title(&self) -> Option<String> {
Some("Use `datetime.datetime.fromtimestamp(ts, tz=...)` instead".to_string())
} }
} }

View file

@ -20,7 +20,7 @@ use super::helpers;
/// always use timezone-aware objects. /// always use timezone-aware objects.
/// ///
/// `datetime.datetime.utcnow()` returns a naive datetime object; instead, use /// `datetime.datetime.utcnow()` returns a naive datetime object; instead, use
/// `datetime.datetime.now(tz=)` to return a timezone-aware object. /// `datetime.datetime.now(tz=...)` to create a timezone-aware object.
/// ///
/// ## Example /// ## Example
/// ```python /// ```python
@ -51,10 +51,11 @@ pub struct CallDatetimeUtcnow;
impl Violation for CallDatetimeUtcnow { impl Violation for CallDatetimeUtcnow {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
format!( format!("`datetime.datetime.utcnow()` used")
"The use of `datetime.datetime.utcnow()` is not allowed, use \ }
`datetime.datetime.now(tz=)` instead"
) fn fix_title(&self) -> Option<String> {
Some("Use `datetime.datetime.now(tz=...)` instead".to_string())
} }
} }

View file

@ -1,17 +1,15 @@
use ruff_diagnostics::{Diagnostic, Violation}; use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation}; use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast, Expr}; use ruff_python_ast as ast;
use ruff_python_semantic::Modules; use ruff_python_semantic::Modules;
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
use crate::rules::flake8_datetimez::rules::helpers::has_non_none_keyword;
use super::helpers; use super::helpers::{self, DatetimeModuleAntipattern};
/// ## What it does /// ## What it does
/// Checks for `datetime` instantiations that lack a `tzinfo` argument. /// Checks for `datetime` instantiations that do not specify a timezone.
/// ///
/// ## Why is this bad? /// ## Why is this bad?
/// `datetime` objects are "naive" by default, in that they do not include /// `datetime` objects are "naive" by default, in that they do not include
@ -20,7 +18,8 @@ use super::helpers;
/// `datetime` objects are preferred, as they represent a specific moment in /// `datetime` objects are preferred, as they represent a specific moment in
/// time, unlike "naive" objects. /// time, unlike "naive" objects.
/// ///
/// By providing a `tzinfo` value, a `datetime` can be made timezone-aware. /// By providing a non-`None` value for `tzinfo`, a `datetime` can be made
/// timezone-aware.
/// ///
/// ## Example /// ## Example
/// ```python /// ```python
@ -36,19 +35,31 @@ use super::helpers;
/// datetime.datetime(2000, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc) /// datetime.datetime(2000, 1, 1, 0, 0, 0, tzinfo=datetime.timezone.utc)
/// ``` /// ```
/// ///
/// Or, for Python 3.11 and later: /// Or, on Python 3.11 and later:
/// ```python /// ```python
/// import datetime /// import datetime
/// ///
/// datetime.datetime(2000, 1, 1, 0, 0, 0, tzinfo=datetime.UTC) /// datetime.datetime(2000, 1, 1, 0, 0, 0, tzinfo=datetime.UTC)
/// ``` /// ```
#[violation] #[violation]
pub struct CallDatetimeWithoutTzinfo; pub struct CallDatetimeWithoutTzinfo(DatetimeModuleAntipattern);
impl Violation for CallDatetimeWithoutTzinfo { impl Violation for CallDatetimeWithoutTzinfo {
#[derive_message_formats] #[derive_message_formats]
fn message(&self) -> String { fn message(&self) -> String {
format!("The use of `datetime.datetime()` without `tzinfo` argument is not allowed") let CallDatetimeWithoutTzinfo(antipattern) = self;
match antipattern {
DatetimeModuleAntipattern::NoTzArgumentPassed => {
format!("`datetime.datetime()` called without a `tzinfo` argument")
}
DatetimeModuleAntipattern::NonePassedToTzArgument => {
format!("`tzinfo=None` passed to `datetime.datetime()`")
}
}
}
fn fix_title(&self) -> Option<String> {
Some("Pass a `datetime.timezone` object to the `tzinfo` parameter".to_string())
} }
} }
@ -69,23 +80,14 @@ pub(crate) fn call_datetime_without_tzinfo(checker: &mut Checker, call: &ast::Ex
return; return;
} }
// No positional arg: keyword is missing or constant None. let antipattern = match call.arguments.find_argument("tzinfo", 7) {
if call.arguments.args.len() < 8 && !has_non_none_keyword(&call.arguments, "tzinfo") { Some(ast::Expr::NoneLiteral(_)) => DatetimeModuleAntipattern::NonePassedToTzArgument,
checker Some(_) => return,
.diagnostics None => DatetimeModuleAntipattern::NoTzArgumentPassed,
.push(Diagnostic::new(CallDatetimeWithoutTzinfo, call.range())); };
return;
}
// Positional arg: is constant None. checker.diagnostics.push(Diagnostic::new(
if call CallDatetimeWithoutTzinfo(antipattern),
.arguments call.range,
.args ));
.get(7)
.is_some_and(Expr::is_none_literal_expr)
{
checker
.diagnostics
.push(Diagnostic::new(CallDatetimeWithoutTzinfo, call.range()));
}
} }

View file

@ -1,7 +1,13 @@
use ruff_python_ast::{Arguments, Expr, ExprAttribute}; use ruff_python_ast::{Expr, ExprAttribute};
use crate::checkers::ast::Checker; use crate::checkers::ast::Checker;
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(super) enum DatetimeModuleAntipattern {
NoTzArgumentPassed,
NonePassedToTzArgument,
}
/// Check if the parent expression is a call to `astimezone`. This assumes that /// Check if the parent expression is a call to `astimezone`. This assumes that
/// the current expression is a `datetime.datetime` object. /// the current expression is a `datetime.datetime` object.
pub(super) fn parent_expr_is_astimezone(checker: &Checker) -> bool { pub(super) fn parent_expr_is_astimezone(checker: &Checker) -> bool {
@ -9,10 +15,3 @@ pub(super) fn parent_expr_is_astimezone(checker: &Checker) -> bool {
matches!(parent, Expr::Attribute(ExprAttribute { attr, .. }) if attr.as_str() == "astimezone") matches!(parent, Expr::Attribute(ExprAttribute { attr, .. }) if attr.as_str() == "astimezone")
}) })
} }
/// Return `true` if a keyword argument is present with a non-`None` value.
pub(super) fn has_non_none_keyword(arguments: &Arguments, keyword: &str) -> bool {
arguments
.find_keyword(keyword)
.is_some_and(|keyword| !keyword.value.is_none_literal_expr())
}

View file

@ -1,7 +1,7 @@
--- ---
source: crates/ruff_linter/src/rules/flake8_datetimez/mod.rs source: crates/ruff_linter/src/rules/flake8_datetimez/mod.rs
--- ---
DTZ001.py:4:1: DTZ001 The use of `datetime.datetime()` without `tzinfo` argument is not allowed DTZ001.py:4:1: DTZ001 `datetime.datetime()` called without a `tzinfo` argument
| |
3 | # no args 3 | # no args
4 | datetime.datetime(2000, 1, 1, 0, 0, 0) 4 | datetime.datetime(2000, 1, 1, 0, 0, 0)
@ -9,8 +9,9 @@ DTZ001.py:4:1: DTZ001 The use of `datetime.datetime()` without `tzinfo` argument
5 | 5 |
6 | # none args 6 | # none args
| |
= help: Pass a `datetime.timezone` object to the `tzinfo` parameter
DTZ001.py:7:1: DTZ001 The use of `datetime.datetime()` without `tzinfo` argument is not allowed DTZ001.py:7:1: DTZ001 `tzinfo=None` passed to `datetime.datetime()`
| |
6 | # none args 6 | # none args
7 | datetime.datetime(2000, 1, 1, 0, 0, 0, 0, None) 7 | datetime.datetime(2000, 1, 1, 0, 0, 0, 0, None)
@ -18,8 +19,9 @@ DTZ001.py:7:1: DTZ001 The use of `datetime.datetime()` without `tzinfo` argument
8 | 8 |
9 | # not none arg 9 | # not none arg
| |
= help: Pass a `datetime.timezone` object to the `tzinfo` parameter
DTZ001.py:13:1: DTZ001 The use of `datetime.datetime()` without `tzinfo` argument is not allowed DTZ001.py:13:1: DTZ001 `datetime.datetime()` called without a `tzinfo` argument
| |
12 | # no kwargs 12 | # no kwargs
13 | datetime.datetime(2000, 1, 1, fold=1) 13 | datetime.datetime(2000, 1, 1, fold=1)
@ -27,8 +29,9 @@ DTZ001.py:13:1: DTZ001 The use of `datetime.datetime()` without `tzinfo` argumen
14 | 14 |
15 | # none kwargs 15 | # none kwargs
| |
= help: Pass a `datetime.timezone` object to the `tzinfo` parameter
DTZ001.py:16:1: DTZ001 The use of `datetime.datetime()` without `tzinfo` argument is not allowed DTZ001.py:16:1: DTZ001 `tzinfo=None` passed to `datetime.datetime()`
| |
15 | # none kwargs 15 | # none kwargs
16 | datetime.datetime(2000, 1, 1, tzinfo=None) 16 | datetime.datetime(2000, 1, 1, tzinfo=None)
@ -36,8 +39,9 @@ DTZ001.py:16:1: DTZ001 The use of `datetime.datetime()` without `tzinfo` argumen
17 | 17 |
18 | from datetime import datetime 18 | from datetime import datetime
| |
= help: Pass a `datetime.timezone` object to the `tzinfo` parameter
DTZ001.py:21:1: DTZ001 The use of `datetime.datetime()` without `tzinfo` argument is not allowed DTZ001.py:21:1: DTZ001 `datetime.datetime()` called without a `tzinfo` argument
| |
20 | # no args unqualified 20 | # no args unqualified
21 | datetime(2000, 1, 1, 0, 0, 0) 21 | datetime(2000, 1, 1, 0, 0, 0)
@ -45,5 +49,4 @@ DTZ001.py:21:1: DTZ001 The use of `datetime.datetime()` without `tzinfo` argumen
22 | 22 |
23 | # uses `astimezone` method 23 | # uses `astimezone` method
| |
= help: Pass a `datetime.timezone` object to the `tzinfo` parameter

View file

@ -1,7 +1,7 @@
--- ---
source: crates/ruff_linter/src/rules/flake8_datetimez/mod.rs source: crates/ruff_linter/src/rules/flake8_datetimez/mod.rs
--- ---
DTZ002.py:4:1: DTZ002 The use of `datetime.datetime.today()` is not allowed, use `datetime.datetime.now(tz=)` instead DTZ002.py:4:1: DTZ002 `datetime.datetime.today()` used
| |
3 | # qualified 3 | # qualified
4 | datetime.datetime.today() 4 | datetime.datetime.today()
@ -9,8 +9,9 @@ DTZ002.py:4:1: DTZ002 The use of `datetime.datetime.today()` is not allowed, use
5 | 5 |
6 | from datetime import datetime 6 | from datetime import datetime
| |
= help: Use `datetime.datetime.now(tz=...)` instead
DTZ002.py:9:1: DTZ002 The use of `datetime.datetime.today()` is not allowed, use `datetime.datetime.now(tz=)` instead DTZ002.py:9:1: DTZ002 `datetime.datetime.today()` used
| |
8 | # unqualified 8 | # unqualified
9 | datetime.today() 9 | datetime.today()
@ -18,5 +19,4 @@ DTZ002.py:9:1: DTZ002 The use of `datetime.datetime.today()` is not allowed, use
10 | 10 |
11 | # uses `astimezone` method 11 | # uses `astimezone` method
| |
= help: Use `datetime.datetime.now(tz=...)` instead

View file

@ -1,7 +1,7 @@
--- ---
source: crates/ruff_linter/src/rules/flake8_datetimez/mod.rs source: crates/ruff_linter/src/rules/flake8_datetimez/mod.rs
--- ---
DTZ003.py:4:1: DTZ003 The use of `datetime.datetime.utcnow()` is not allowed, use `datetime.datetime.now(tz=)` instead DTZ003.py:4:1: DTZ003 `datetime.datetime.utcnow()` used
| |
3 | # qualified 3 | # qualified
4 | datetime.datetime.utcnow() 4 | datetime.datetime.utcnow()
@ -9,8 +9,9 @@ DTZ003.py:4:1: DTZ003 The use of `datetime.datetime.utcnow()` is not allowed, us
5 | 5 |
6 | from datetime import datetime 6 | from datetime import datetime
| |
= help: Use `datetime.datetime.now(tz=...)` instead
DTZ003.py:9:1: DTZ003 The use of `datetime.datetime.utcnow()` is not allowed, use `datetime.datetime.now(tz=)` instead DTZ003.py:9:1: DTZ003 `datetime.datetime.utcnow()` used
| |
8 | # unqualified 8 | # unqualified
9 | datetime.utcnow() 9 | datetime.utcnow()
@ -18,5 +19,4 @@ DTZ003.py:9:1: DTZ003 The use of `datetime.datetime.utcnow()` is not allowed, us
10 | 10 |
11 | # uses `astimezone` method 11 | # uses `astimezone` method
| |
= help: Use `datetime.datetime.now(tz=...)` instead

View file

@ -1,7 +1,7 @@
--- ---
source: crates/ruff_linter/src/rules/flake8_datetimez/mod.rs source: crates/ruff_linter/src/rules/flake8_datetimez/mod.rs
--- ---
DTZ004.py:4:1: DTZ004 The use of `datetime.datetime.utcfromtimestamp()` is not allowed, use `datetime.datetime.fromtimestamp(ts, tz=)` instead DTZ004.py:4:1: DTZ004 `datetime.datetime.utcfromtimestamp()` used
| |
3 | # qualified 3 | # qualified
4 | datetime.datetime.utcfromtimestamp(1234) 4 | datetime.datetime.utcfromtimestamp(1234)
@ -9,8 +9,9 @@ DTZ004.py:4:1: DTZ004 The use of `datetime.datetime.utcfromtimestamp()` is not a
5 | 5 |
6 | from datetime import datetime 6 | from datetime import datetime
| |
= help: Use `datetime.datetime.fromtimestamp(ts, tz=...)` instead
DTZ004.py:9:1: DTZ004 The use of `datetime.datetime.utcfromtimestamp()` is not allowed, use `datetime.datetime.fromtimestamp(ts, tz=)` instead DTZ004.py:9:1: DTZ004 `datetime.datetime.utcfromtimestamp()` used
| |
8 | # unqualified 8 | # unqualified
9 | datetime.utcfromtimestamp(1234) 9 | datetime.utcfromtimestamp(1234)
@ -18,5 +19,4 @@ DTZ004.py:9:1: DTZ004 The use of `datetime.datetime.utcfromtimestamp()` is not a
10 | 10 |
11 | # uses `astimezone` method 11 | # uses `astimezone` method
| |
= help: Use `datetime.datetime.fromtimestamp(ts, tz=...)` instead

View file

@ -1,7 +1,7 @@
--- ---
source: crates/ruff_linter/src/rules/flake8_datetimez/mod.rs source: crates/ruff_linter/src/rules/flake8_datetimez/mod.rs
--- ---
DTZ005.py:4:1: DTZ005 The use of `datetime.datetime.now()` without `tz` argument is not allowed DTZ005.py:4:1: DTZ005 `datetime.datetime.now()` called without a `tz` argument
| |
3 | # no args 3 | # no args
4 | datetime.datetime.now() 4 | datetime.datetime.now()
@ -9,8 +9,9 @@ DTZ005.py:4:1: DTZ005 The use of `datetime.datetime.now()` without `tz` argument
5 | 5 |
6 | # wrong keywords 6 | # wrong keywords
| |
= help: Pass a `datetime.timezone` object to the `tz` parameter
DTZ005.py:7:1: DTZ005 The use of `datetime.datetime.now()` without `tz` argument is not allowed DTZ005.py:7:1: DTZ005 `datetime.datetime.now()` called without a `tz` argument
| |
6 | # wrong keywords 6 | # wrong keywords
7 | datetime.datetime.now(bad=datetime.timezone.utc) 7 | datetime.datetime.now(bad=datetime.timezone.utc)
@ -18,8 +19,9 @@ DTZ005.py:7:1: DTZ005 The use of `datetime.datetime.now()` without `tz` argument
8 | 8 |
9 | # none args 9 | # none args
| |
= help: Pass a `datetime.timezone` object to the `tz` parameter
DTZ005.py:10:1: DTZ005 The use of `datetime.datetime.now()` without `tz` argument is not allowed DTZ005.py:10:1: DTZ005 `tz=None` passed to `datetime.datetime.now()`
| |
9 | # none args 9 | # none args
10 | datetime.datetime.now(None) 10 | datetime.datetime.now(None)
@ -27,8 +29,9 @@ DTZ005.py:10:1: DTZ005 The use of `datetime.datetime.now()` without `tz` argumen
11 | 11 |
12 | # none keywords 12 | # none keywords
| |
= help: Pass a `datetime.timezone` object to the `tz` parameter
DTZ005.py:13:1: DTZ005 The use of `datetime.datetime.now()` without `tz` argument is not allowed DTZ005.py:13:1: DTZ005 `tz=None` passed to `datetime.datetime.now()`
| |
12 | # none keywords 12 | # none keywords
13 | datetime.datetime.now(tz=None) 13 | datetime.datetime.now(tz=None)
@ -36,8 +39,9 @@ DTZ005.py:13:1: DTZ005 The use of `datetime.datetime.now()` without `tz` argumen
14 | 14 |
15 | from datetime import datetime 15 | from datetime import datetime
| |
= help: Pass a `datetime.timezone` object to the `tz` parameter
DTZ005.py:18:1: DTZ005 The use of `datetime.datetime.now()` without `tz` argument is not allowed DTZ005.py:18:1: DTZ005 `datetime.datetime.now()` called without a `tz` argument
| |
17 | # no args unqualified 17 | # no args unqualified
18 | datetime.now() 18 | datetime.now()
@ -45,5 +49,4 @@ DTZ005.py:18:1: DTZ005 The use of `datetime.datetime.now()` without `tz` argumen
19 | 19 |
20 | # uses `astimezone` method 20 | # uses `astimezone` method
| |
= help: Pass a `datetime.timezone` object to the `tz` parameter

View file

@ -1,7 +1,7 @@
--- ---
source: crates/ruff_linter/src/rules/flake8_datetimez/mod.rs source: crates/ruff_linter/src/rules/flake8_datetimez/mod.rs
--- ---
DTZ006.py:4:1: DTZ006 The use of `datetime.datetime.fromtimestamp()` without `tz` argument is not allowed DTZ006.py:4:1: DTZ006 `datetime.datetime.fromtimestamp()` called without a `tz` argument
| |
3 | # no args 3 | # no args
4 | datetime.datetime.fromtimestamp(1234) 4 | datetime.datetime.fromtimestamp(1234)
@ -9,8 +9,9 @@ DTZ006.py:4:1: DTZ006 The use of `datetime.datetime.fromtimestamp()` without `tz
5 | 5 |
6 | # wrong keywords 6 | # wrong keywords
| |
= help: Pass a `datetime.timezone` object to the `tz` parameter
DTZ006.py:7:1: DTZ006 The use of `datetime.datetime.fromtimestamp()` without `tz` argument is not allowed DTZ006.py:7:1: DTZ006 `datetime.datetime.fromtimestamp()` called without a `tz` argument
| |
6 | # wrong keywords 6 | # wrong keywords
7 | datetime.datetime.fromtimestamp(1234, bad=datetime.timezone.utc) 7 | datetime.datetime.fromtimestamp(1234, bad=datetime.timezone.utc)
@ -18,8 +19,9 @@ DTZ006.py:7:1: DTZ006 The use of `datetime.datetime.fromtimestamp()` without `tz
8 | 8 |
9 | # none args 9 | # none args
| |
= help: Pass a `datetime.timezone` object to the `tz` parameter
DTZ006.py:10:1: DTZ006 The use of `datetime.datetime.fromtimestamp()` without `tz` argument is not allowed DTZ006.py:10:1: DTZ006 `tz=None` passed to `datetime.datetime.fromtimestamp()`
| |
9 | # none args 9 | # none args
10 | datetime.datetime.fromtimestamp(1234, None) 10 | datetime.datetime.fromtimestamp(1234, None)
@ -27,8 +29,9 @@ DTZ006.py:10:1: DTZ006 The use of `datetime.datetime.fromtimestamp()` without `t
11 | 11 |
12 | # none keywords 12 | # none keywords
| |
= help: Pass a `datetime.timezone` object to the `tz` parameter
DTZ006.py:13:1: DTZ006 The use of `datetime.datetime.fromtimestamp()` without `tz` argument is not allowed DTZ006.py:13:1: DTZ006 `tz=None` passed to `datetime.datetime.fromtimestamp()`
| |
12 | # none keywords 12 | # none keywords
13 | datetime.datetime.fromtimestamp(1234, tz=None) 13 | datetime.datetime.fromtimestamp(1234, tz=None)
@ -36,8 +39,9 @@ DTZ006.py:13:1: DTZ006 The use of `datetime.datetime.fromtimestamp()` without `t
14 | 14 |
15 | from datetime import datetime 15 | from datetime import datetime
| |
= help: Pass a `datetime.timezone` object to the `tz` parameter
DTZ006.py:18:1: DTZ006 The use of `datetime.datetime.fromtimestamp()` without `tz` argument is not allowed DTZ006.py:18:1: DTZ006 `datetime.datetime.fromtimestamp()` called without a `tz` argument
| |
17 | # no args unqualified 17 | # no args unqualified
18 | datetime.fromtimestamp(1234) 18 | datetime.fromtimestamp(1234)
@ -45,5 +49,4 @@ DTZ006.py:18:1: DTZ006 The use of `datetime.datetime.fromtimestamp()` without `t
19 | 19 |
20 | # uses `astimezone` method 20 | # uses `astimezone` method
| |
= help: Pass a `datetime.timezone` object to the `tz` parameter

View file

@ -1,7 +1,7 @@
--- ---
source: crates/ruff_linter/src/rules/flake8_datetimez/mod.rs source: crates/ruff_linter/src/rules/flake8_datetimez/mod.rs
--- ---
DTZ007.py:4:1: DTZ007 The use of `datetime.datetime.strptime()` without %z must be followed by `.replace(tzinfo=)` or `.astimezone()` DTZ007.py:4:1: DTZ007 Naive datetime constructed using `datetime.datetime.strptime()` without %z
| |
3 | # bad format 3 | # bad format
4 | datetime.datetime.strptime("something", "%H:%M:%S%Z") 4 | datetime.datetime.strptime("something", "%H:%M:%S%Z")
@ -9,8 +9,9 @@ DTZ007.py:4:1: DTZ007 The use of `datetime.datetime.strptime()` without %z must
5 | 5 |
6 | # no replace or astimezone 6 | # no replace or astimezone
| |
= help: Call `.replace(tzinfo=<timezone>)` or `.astimezone()` to convert to an aware datetime
DTZ007.py:7:1: DTZ007 The use of `datetime.datetime.strptime()` without %z must be followed by `.replace(tzinfo=)` or `.astimezone()` DTZ007.py:7:1: DTZ007 Naive datetime constructed using `datetime.datetime.strptime()` without %z
| |
6 | # no replace or astimezone 6 | # no replace or astimezone
7 | datetime.datetime.strptime("something", "something") 7 | datetime.datetime.strptime("something", "something")
@ -18,8 +19,9 @@ DTZ007.py:7:1: DTZ007 The use of `datetime.datetime.strptime()` without %z must
8 | 8 |
9 | # wrong replace 9 | # wrong replace
| |
= help: Call `.replace(tzinfo=<timezone>)` or `.astimezone()` to convert to an aware datetime
DTZ007.py:10:1: DTZ007 The use of `datetime.datetime.strptime()` without %z must be followed by `.replace(tzinfo=)` or `.astimezone()` DTZ007.py:10:1: DTZ007 Naive datetime constructed using `datetime.datetime.strptime()` without %z
| |
9 | # wrong replace 9 | # wrong replace
10 | datetime.datetime.strptime("something", "something").replace(hour=1) 10 | datetime.datetime.strptime("something", "something").replace(hour=1)
@ -27,8 +29,9 @@ DTZ007.py:10:1: DTZ007 The use of `datetime.datetime.strptime()` without %z must
11 | 11 |
12 | # none replace 12 | # none replace
| |
= help: Call `.replace(tzinfo=<timezone>)` or `.astimezone()` to convert to an aware datetime
DTZ007.py:13:1: DTZ007 The use of `datetime.datetime.strptime()` without %z must be followed by `.replace(tzinfo=)` or `.astimezone()` DTZ007.py:13:1: DTZ007 `datetime.datetime.strptime(...).replace(tz=None)` used
| |
12 | # none replace 12 | # none replace
13 | datetime.datetime.strptime("something", "something").replace(tzinfo=None) 13 | datetime.datetime.strptime("something", "something").replace(tzinfo=None)
@ -36,12 +39,12 @@ DTZ007.py:13:1: DTZ007 The use of `datetime.datetime.strptime()` without %z must
14 | 14 |
15 | # OK 15 | # OK
| |
= help: Pass a `datetime.timezone` object to the `tzinfo` parameter
DTZ007.py:35:1: DTZ007 The use of `datetime.datetime.strptime()` without %z must be followed by `.replace(tzinfo=)` or `.astimezone()` DTZ007.py:35:1: DTZ007 Naive datetime constructed using `datetime.datetime.strptime()` without %z
| |
34 | # no replace orastimezone unqualified 34 | # no replace orastimezone unqualified
35 | datetime.strptime("something", "something") 35 | datetime.strptime("something", "something")
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DTZ007 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ DTZ007
| |
= help: Call `.replace(tzinfo=<timezone>)` or `.astimezone()` to convert to an aware datetime

View file

@ -1,7 +1,7 @@
--- ---
source: crates/ruff_linter/src/rules/flake8_datetimez/mod.rs source: crates/ruff_linter/src/rules/flake8_datetimez/mod.rs
--- ---
DTZ011.py:4:1: DTZ011 The use of `datetime.date.today()` is not allowed, use `datetime.datetime.now(tz=).date()` instead DTZ011.py:4:1: DTZ011 `datetime.date.today()` used
| |
3 | # qualified 3 | # qualified
4 | datetime.date.today() 4 | datetime.date.today()
@ -9,12 +9,12 @@ DTZ011.py:4:1: DTZ011 The use of `datetime.date.today()` is not allowed, use `da
5 | 5 |
6 | from datetime import date 6 | from datetime import date
| |
= help: Use `datetime.datetime.now(tz=...).date()` instead
DTZ011.py:9:1: DTZ011 The use of `datetime.date.today()` is not allowed, use `datetime.datetime.now(tz=).date()` instead DTZ011.py:9:1: DTZ011 `datetime.date.today()` used
| |
8 | # unqualified 8 | # unqualified
9 | date.today() 9 | date.today()
| ^^^^^^^^^^^^ DTZ011 | ^^^^^^^^^^^^ DTZ011
| |
= help: Use `datetime.datetime.now(tz=...).date()` instead

View file

@ -1,7 +1,7 @@
--- ---
source: crates/ruff_linter/src/rules/flake8_datetimez/mod.rs source: crates/ruff_linter/src/rules/flake8_datetimez/mod.rs
--- ---
DTZ012.py:4:1: DTZ012 The use of `datetime.date.fromtimestamp()` is not allowed, use `datetime.datetime.fromtimestamp(ts, tz=).date()` instead DTZ012.py:4:1: DTZ012 `datetime.date.fromtimestamp()` used
| |
3 | # qualified 3 | # qualified
4 | datetime.date.fromtimestamp(1234) 4 | datetime.date.fromtimestamp(1234)
@ -9,12 +9,12 @@ DTZ012.py:4:1: DTZ012 The use of `datetime.date.fromtimestamp()` is not allowed,
5 | 5 |
6 | from datetime import date 6 | from datetime import date
| |
= help: Use `datetime.datetime.fromtimestamp(ts, tz=...).date()` instead
DTZ012.py:9:1: DTZ012 The use of `datetime.date.fromtimestamp()` is not allowed, use `datetime.datetime.fromtimestamp(ts, tz=).date()` instead DTZ012.py:9:1: DTZ012 `datetime.date.fromtimestamp()` used
| |
8 | # unqualified 8 | # unqualified
9 | date.fromtimestamp(1234) 9 | date.fromtimestamp(1234)
| ^^^^^^^^^^^^^^^^^^^^^^^^ DTZ012 | ^^^^^^^^^^^^^^^^^^^^^^^^ DTZ012
| |
= help: Use `datetime.datetime.fromtimestamp(ts, tz=...).date()` instead