mirror of
https://github.com/nushell/nushell.git
synced 2025-02-23 05:51:28 +01:00
Handle int input in into datetime
(#5484)
This commit is contained in:
parent
ccfa35289b
commit
4052a99ff5
@ -24,13 +24,13 @@ enum Zone {
|
|||||||
Local,
|
Local,
|
||||||
East(u8),
|
East(u8),
|
||||||
West(u8),
|
West(u8),
|
||||||
Error, // we want the nullshell to cast it instead of rust
|
Error, // we want Nushell to cast it instead of Rust
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Zone {
|
impl Zone {
|
||||||
fn new(i: i64) -> Self {
|
fn new(i: i64) -> Self {
|
||||||
if i.abs() <= 12 {
|
if i.abs() <= 12 {
|
||||||
// guanranteed here
|
// guaranteed here
|
||||||
if i >= 0 {
|
if i >= 0 {
|
||||||
Self::East(i as u8) // won't go out of range
|
Self::East(i as u8) // won't go out of range
|
||||||
} else {
|
} else {
|
||||||
@ -209,58 +209,73 @@ fn action(
|
|||||||
dateformat: &Option<DatetimeFormat>,
|
dateformat: &Option<DatetimeFormat>,
|
||||||
head: Span,
|
head: Span,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
match input {
|
// if timezone is specified, first check if the input is a timestamp.
|
||||||
Value::String { val: s, span } => {
|
if let Some(tz) = timezone {
|
||||||
let ts = s.parse::<i64>();
|
const TIMESTAMP_BOUND: i64 = 8.2e+12 as i64;
|
||||||
// if timezone if specified, first check if the input is a timestamp.
|
|
||||||
if let Some(tz) = timezone {
|
let ts = match input {
|
||||||
const TIMESTAMP_BOUND: i64 = 8.2e+12 as i64;
|
Value::Int { val, .. } => Ok(*val),
|
||||||
// Since the timestamp method of chrono itself don't throw an error (it just panicked)
|
Value::String { val, .. } => val.parse::<i64>(),
|
||||||
// We have to manually guard it.
|
other => {
|
||||||
if let Ok(t) = ts {
|
return Value::Error {
|
||||||
if t.abs() > TIMESTAMP_BOUND {
|
error: ShellError::UnsupportedInput(
|
||||||
return Value::Error{error: ShellError::UnsupportedInput(
|
format!("Expected string or int, got {} instead", other.get_type()),
|
||||||
"Given timestamp is out of range, it should between -8e+12 and 8e+12".to_string(),
|
head,
|
||||||
head,
|
),
|
||||||
)};
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Ok(t) = ts {
|
||||||
|
if t.abs() > TIMESTAMP_BOUND {
|
||||||
|
return Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
"Given timestamp is out of range, it should between -8e+12 and 8e+12"
|
||||||
|
.to_string(),
|
||||||
|
head,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const HOUR: i32 = 3600;
|
||||||
|
let stampout = match tz.item {
|
||||||
|
Zone::Utc => Value::Date {
|
||||||
|
val: Utc.timestamp(t, 0).into(),
|
||||||
|
span: head,
|
||||||
|
},
|
||||||
|
Zone::Local => Value::Date {
|
||||||
|
val: Local.timestamp(t, 0).into(),
|
||||||
|
span: head,
|
||||||
|
},
|
||||||
|
Zone::East(i) => {
|
||||||
|
let eastoffset = FixedOffset::east((i as i32) * HOUR);
|
||||||
|
Value::Date {
|
||||||
|
val: eastoffset.timestamp(t, 0),
|
||||||
|
span: head,
|
||||||
}
|
}
|
||||||
const HOUR: i32 = 3600;
|
|
||||||
let stampout = match tz.item {
|
|
||||||
Zone::Utc => Value::Date {
|
|
||||||
val: Utc.timestamp(t, 0).into(),
|
|
||||||
span: head,
|
|
||||||
},
|
|
||||||
Zone::Local => Value::Date {
|
|
||||||
val: Local.timestamp(t, 0).into(),
|
|
||||||
span: head,
|
|
||||||
},
|
|
||||||
Zone::East(i) => {
|
|
||||||
let eastoffset = FixedOffset::east((i as i32) * HOUR);
|
|
||||||
Value::Date {
|
|
||||||
val: eastoffset.timestamp(t, 0),
|
|
||||||
span: head,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Zone::West(i) => {
|
|
||||||
let westoffset = FixedOffset::west((i as i32) * HOUR);
|
|
||||||
Value::Date {
|
|
||||||
val: westoffset.timestamp(t, 0),
|
|
||||||
span: head,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Zone::Error => Value::Error {
|
|
||||||
error: ShellError::UnsupportedInput(
|
|
||||||
"Cannot convert given timezone or offset to timestamp".to_string(),
|
|
||||||
tz.span,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return stampout;
|
|
||||||
}
|
}
|
||||||
|
Zone::West(i) => {
|
||||||
|
let westoffset = FixedOffset::west((i as i32) * HOUR);
|
||||||
|
Value::Date {
|
||||||
|
val: westoffset.timestamp(t, 0),
|
||||||
|
span: head,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Zone::Error => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
"Cannot convert given timezone or offset to timestamp".to_string(),
|
||||||
|
tz.span,
|
||||||
|
),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
// if it's not, continue and default to the system's local timezone.
|
return stampout;
|
||||||
let out = match dateformat {
|
}
|
||||||
Some(dt) => match DateTime::parse_from_str(s, &dt.0) {
|
};
|
||||||
|
|
||||||
|
// if it's not, continue and default to the system's local timezone.
|
||||||
|
match input {
|
||||||
|
Value::String { val, span } => {
|
||||||
|
match dateformat {
|
||||||
|
Some(dt) => match DateTime::parse_from_str(val, &dt.0) {
|
||||||
Ok(d) => Value::Date { val: d, span: head },
|
Ok(d) => Value::Date { val: d, span: head },
|
||||||
Err(reason) => {
|
Err(reason) => {
|
||||||
return Value::Error {
|
return Value::Error {
|
||||||
@ -276,23 +291,27 @@ fn action(
|
|||||||
// Tries to automatically parse the date
|
// Tries to automatically parse the date
|
||||||
// (i.e. without a format string)
|
// (i.e. without a format string)
|
||||||
// and assumes the system's local timezone if none is specified
|
// and assumes the system's local timezone if none is specified
|
||||||
None => match parse_date_from_string(s, *span) {
|
None => match parse_date_from_string(val, *span) {
|
||||||
Ok(date) => Value::Date {
|
Ok(date) => Value::Date {
|
||||||
val: date,
|
val: date,
|
||||||
span: *span,
|
span: *span,
|
||||||
},
|
},
|
||||||
Err(err) => err,
|
Err(err) => err,
|
||||||
},
|
},
|
||||||
};
|
|
||||||
|
|
||||||
out
|
|
||||||
}
|
|
||||||
other => {
|
|
||||||
let got = format!("Expected string, got {} instead", other.get_type());
|
|
||||||
Value::Error {
|
|
||||||
error: ShellError::UnsupportedInput(got, head),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Value::Int { .. } => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
"Received integer input but timezone not specified. Did you forget to specify a timezone?".to_string(),
|
||||||
|
head,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
other => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
format!("Expected string or int, got {} instead", other.get_type()),
|
||||||
|
head,
|
||||||
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,6 +370,23 @@ mod tests {
|
|||||||
assert_eq!(actual, expected)
|
assert_eq!(actual, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn takes_timestamp_offset_as_int() {
|
||||||
|
let date_int = Value::test_int(1614434140);
|
||||||
|
let timezone_option = Some(Spanned {
|
||||||
|
item: Zone::East(8),
|
||||||
|
span: Span::test_data(),
|
||||||
|
});
|
||||||
|
let actual = action(&date_int, &timezone_option, &None, Span::test_data());
|
||||||
|
let expected = Value::Date {
|
||||||
|
val: DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z")
|
||||||
|
.unwrap(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn takes_timestamp() {
|
fn takes_timestamp() {
|
||||||
let date_str = Value::test_string("1614434140");
|
let date_str = Value::test_string("1614434140");
|
||||||
|
Loading…
Reference in New Issue
Block a user