Skip to content

Commit

Permalink
Merge pull request chronotope#1233 from pitdicker/merge_0.4.x
Browse files Browse the repository at this point in the history
Merge 0.4.x
  • Loading branch information
djc authored Aug 30, 2023
2 parents bc410bb + 178b1d1 commit 794e0b5
Show file tree
Hide file tree
Showing 30 changed files with 1,065 additions and 367 deletions.
5 changes: 5 additions & 0 deletions .github/codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
coverage:
status:
project:
default:
threshold: 1%
27 changes: 27 additions & 0 deletions .github/workflows/codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: codecov
on:
push:
branches: [main, 0.4.x]
pull_request:
jobs:
# Run code coverage using cargo-llvm-cov then upload to codecov.io
job_code_coverage:
name: llvm-cov
runs-on: ubuntu-latest
env:
CARGO_TERM_COLOR: always
steps:
- uses: actions/checkout@v3
- name: Install Rust
run: rustup update stable
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov
- name: Generate code coverage
run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
files: lcov.info
fail_ci_if_error: true
12 changes: 6 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,11 @@ jobs:
# which are run elsewhere
- run: |
cargo test --lib \
--features \
unstable-locales,wasmbind,clock,serde,windows-sys \
--features unstable-locales,wasmbind,clock,serde \
--color=always -- --color=always
- run: |
cargo test --doc \
--features \
unstable-locales,wasmbind,clock,serde,windows-sys \
--features unstable-locales,wasmbind,clock,serde \
--color=always -- --color=always
rust_versions:
Expand Down Expand Up @@ -88,11 +86,13 @@ jobs:
- uses: Swatinem/rust-cache@v2
- run: |
cargo hack check --feature-powerset --optional-deps serde,rkyv \
--skip default --skip __internal_bench --skip __doctest \
--skip iana-time-zone --skip pure-rust-locales
--skip __internal_bench,__doctest,iana-time-zone,pure-rust-locales,libc,winapi \
--all-targets
# run using `bash` on all platforms for consistent
# line-continuation marks
shell: bash
env:
RUSTFLAGS: "-D warnings"
- run: cargo test --no-default-features
- run: cargo test --no-default-features --features=alloc
- run: cargo test --no-default-features --features=unstable-locales
Expand Down
7 changes: 5 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ cff-version: 1.2.0
message: Please cite this crate using these information.

# Version information.
date-released: 2023-05-31
version: 0.4.26
date-released: 2023-08-29
version: 0.4.27

# Project information.
abstract: Date and time library for Rust
Expand All @@ -21,6 +21,9 @@ authors:
- alias: esheppa
family-names: Sheppard
given-names: Eric
- alias: pitdicker
family-names: Dicker
given-names: Paul
license:
- Apache-2.0
- MIT
Expand Down
10 changes: 7 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ name = "chrono"
default = ["clock", "std", "wasmbind"]
alloc = []
libc = []
winapi = ["windows-targets"]
std = []
clock = ["std", "windows-sys", "iana-time-zone"]
clock = ["std", "winapi", "iana-time-zone", "android-tzdata"]
wasmbind = ["wasm-bindgen", "js-sys"]
unstable-locales = ["pure-rust-locales", "alloc"]
__internal_bench = ["criterion"]
Expand All @@ -39,13 +40,16 @@ wasm-bindgen = { version = "0.2", optional = true }
js-sys = { version = "0.3", optional = true } # contains FFI bindings for the JS Date API

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.48.0", features = ["Win32_System_Time", "Win32_System_SystemInformation", "Win32_Foundation"], optional = true }
windows-targets = { version = "0.48", optional = true }

[target.'cfg(windows)'.dev-dependencies]
windows-bindgen = { version = "0.51" }

[target.'cfg(unix)'.dependencies]
iana-time-zone = { version = "0.1.45", optional = true, features = ["fallback"] }

[target.'cfg(target_os = "android")'.dependencies]
android-tzdata = "0.1.1"
android-tzdata = { version = "0.1.1", optional = true }

[dev-dependencies]
serde_json = { version = "1" }
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
[![Chrono on crates.io][cratesio-image]][cratesio]
[![Chrono on docs.rs][docsrs-image]][docsrs]
[![Join the chat at https://gitter.im/chrono-rs/chrono][gitter-image]][gitter]
[![codecov.io][codecov-img]][codecov-link]

[gh-image]: https://github.com/chronotope/chrono/actions/workflows/test.yml/badge.svg
[gh-checks]: https://github.com/chronotope/chrono/actions?query=workflow%3Atest
Expand All @@ -14,6 +15,8 @@
[docsrs]: https://docs.rs/chrono
[gitter-image]: https://badges.gitter.im/chrono-rs/chrono.svg
[gitter]: https://gitter.im/chrono-rs/chrono
[codecov-img]: https://img.shields.io/codecov/c/github/chronotope/chrono?logo=codecov
[codecov-link]: https://codecov.io/gh/chronotope/chrono

Chrono aims to provide all functionality needed to do correct operations on dates and times in the
[proleptic Gregorian calendar](https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar):
Expand Down
16 changes: 16 additions & 0 deletions benches/chrono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@ fn bench_datetime_to_rfc3339(c: &mut Criterion) {
c.bench_function("bench_datetime_to_rfc3339", |b| b.iter(|| black_box(dt).to_rfc3339()));
}

fn bench_datetime_to_rfc3339_opts(c: &mut Criterion) {
let pst = FixedOffset::east_opt(8 * 60 * 60).unwrap();
let dt = pst
.from_local_datetime(
&NaiveDate::from_ymd_opt(2018, 1, 11)
.unwrap()
.and_hms_nano_opt(10, 5, 13, 84_660_000)
.unwrap(),
)
.unwrap();
c.bench_function("bench_datetime_to_rfc3339_opts", |b| {
b.iter(|| black_box(dt).to_rfc3339_opts(SecondsFormat::Nanos, true))
});
}

fn bench_year_flags_from_year(c: &mut Criterion) {
c.bench_function("bench_year_flags_from_year", |b| {
b.iter(|| {
Expand Down Expand Up @@ -188,6 +203,7 @@ criterion_group!(
bench_datetime_from_str,
bench_datetime_to_rfc2822,
bench_datetime_to_rfc3339,
bench_datetime_to_rfc3339_opts,
bench_year_flags_from_year,
bench_num_days_from_ce,
bench_get_local_time,
Expand Down
114 changes: 64 additions & 50 deletions src/datetime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,24 @@
//! ISO 8601 date and time with time zone.
#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::string::{String, ToString};
use alloc::string::String;
use core::borrow::Borrow;
use core::cmp::Ordering;
use core::fmt::Write;
use core::ops::{Add, AddAssign, Sub, SubAssign};
use core::time::Duration;
use core::{fmt, hash, str};
#[cfg(feature = "std")]
use std::string::ToString;
#[cfg(feature = "std")]
use std::time::{SystemTime, UNIX_EPOCH};

#[cfg(any(feature = "alloc", feature = "std"))]
use crate::format::DelayedFormat;
#[cfg(feature = "unstable-locales")]
use crate::format::Locale;
use crate::format::{parse, parse_and_remainder, ParseError, ParseResult, Parsed, StrftimeItems};
use crate::format::{Fixed, Item};
use crate::format::{
parse, parse_and_remainder, parse_rfc3339, Fixed, Item, ParseError, ParseResult, Parsed,
StrftimeItems, TOO_LONG,
};
#[cfg(any(feature = "alloc", feature = "std"))]
use crate::format::{write_rfc3339, DelayedFormat};
use crate::naive::{Days, IsoWeek, NaiveDate, NaiveDateTime, NaiveTime};
#[cfg(feature = "clock")]
use crate::offset::Local;
Expand All @@ -45,6 +46,7 @@ mod tests;
/// See the `TimeZone::to_rfc3339_opts` function for usage.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
#[allow(clippy::manual_non_exhaustive)]
pub enum SecondsFormat {
/// Format whole seconds only, with no decimal point nor subseconds.
Secs,
Expand Down Expand Up @@ -501,8 +503,11 @@ impl<Tz: TimeZone> DateTime<Tz> {
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[must_use]
pub fn to_rfc3339(&self) -> String {
// For some reason a string with a capacity less than 32 is ca 20% slower when benchmarking.
let mut result = String::with_capacity(32);
crate::format::write_rfc3339(&mut result, self.naive_local(), self.offset.fix())
let naive = self.naive_local();
let offset = self.offset.fix();
write_rfc3339(&mut result, naive, offset, SecondsFormat::AutoSi, false)
.expect("writing rfc3339 datetime to string should never fail");
result
}
Expand Down Expand Up @@ -535,43 +540,10 @@ impl<Tz: TimeZone> DateTime<Tz> {
#[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))]
#[must_use]
pub fn to_rfc3339_opts(&self, secform: SecondsFormat, use_z: bool) -> String {
use crate::format::Numeric::*;
use crate::format::Pad::Zero;
use crate::SecondsFormat::*;

const PREFIX: &[Item<'static>] = &[
Item::Numeric(Year, Zero),
Item::Literal("-"),
Item::Numeric(Month, Zero),
Item::Literal("-"),
Item::Numeric(Day, Zero),
Item::Literal("T"),
Item::Numeric(Hour, Zero),
Item::Literal(":"),
Item::Numeric(Minute, Zero),
Item::Literal(":"),
Item::Numeric(Second, Zero),
];

let ssitem = match secform {
Secs => None,
Millis => Some(Item::Fixed(Fixed::Nanosecond3)),
Micros => Some(Item::Fixed(Fixed::Nanosecond6)),
Nanos => Some(Item::Fixed(Fixed::Nanosecond9)),
AutoSi => Some(Item::Fixed(Fixed::Nanosecond)),
};

let tzitem = Item::Fixed(if use_z {
Fixed::TimezoneOffsetColonZ
} else {
Fixed::TimezoneOffsetColon
});

let dt = self.fixed_offset();
match ssitem {
None => dt.format_with_items(PREFIX.iter().chain([tzitem].iter())).to_string(),
Some(s) => dt.format_with_items(PREFIX.iter().chain([s, tzitem].iter())).to_string(),
}
let mut result = String::with_capacity(38);
write_rfc3339(&mut result, self.naive_local(), self.offset.fix(), secform, use_z)
.expect("writing rfc3339 datetime to string should never fail");
result
}

/// The minimum possible `DateTime<Utc>`.
Expand Down Expand Up @@ -724,9 +696,11 @@ impl DateTime<FixedOffset> {
/// also simultaneously valid RFC 3339 values, but not all RFC 3339 values are valid ISO 8601
/// values (or the other way around).
pub fn parse_from_rfc3339(s: &str) -> ParseResult<DateTime<FixedOffset>> {
const ITEMS: &[Item<'static>] = &[Item::Fixed(Fixed::RFC3339)];
let mut parsed = Parsed::new();
parse(&mut parsed, s, ITEMS.iter())?;
let (s, _) = parse_rfc3339(&mut parsed, s)?;
if !s.is_empty() {
return Err(TOO_LONG);
}
parsed.to_datetime()
}

Expand Down Expand Up @@ -1214,6 +1188,17 @@ impl<Tz: TimeZone> Add<TimeDelta> for DateTime<Tz> {
}
}

impl<Tz: TimeZone> Add<Duration> for DateTime<Tz> {
type Output = DateTime<Tz>;

#[inline]
fn add(self, rhs: Duration) -> DateTime<Tz> {
let rhs = TimeDelta::from_std(rhs)
.expect("overflow converting from core::time::Duration to chrono::Duration");
self.checked_add_signed(rhs).expect("`DateTime + Duration` overflowed")
}
}

impl<Tz: TimeZone> AddAssign<TimeDelta> for DateTime<Tz> {
#[inline]
fn add_assign(&mut self, rhs: TimeDelta) {
Expand All @@ -1224,6 +1209,15 @@ impl<Tz: TimeZone> AddAssign<TimeDelta> for DateTime<Tz> {
}
}

impl<Tz: TimeZone> AddAssign<Duration> for DateTime<Tz> {
#[inline]
fn add_assign(&mut self, rhs: Duration) {
let rhs = TimeDelta::from_std(rhs)
.expect("overflow converting from core::time::Duration to chrono::Duration");
*self += rhs;
}
}

impl<Tz: TimeZone> Add<Months> for DateTime<Tz> {
type Output = DateTime<Tz>;

Expand All @@ -1241,6 +1235,17 @@ impl<Tz: TimeZone> Sub<TimeDelta> for DateTime<Tz> {
}
}

impl<Tz: TimeZone> Sub<Duration> for DateTime<Tz> {
type Output = DateTime<Tz>;

#[inline]
fn sub(self, rhs: Duration) -> DateTime<Tz> {
let rhs = TimeDelta::from_std(rhs)
.expect("overflow converting from core::time::Duration to chrono::Duration");
self.checked_sub_signed(rhs).expect("`DateTime - Duration` overflowed")
}
}

impl<Tz: TimeZone> SubAssign<TimeDelta> for DateTime<Tz> {
#[inline]
fn sub_assign(&mut self, rhs: TimeDelta) {
Expand All @@ -1251,6 +1256,15 @@ impl<Tz: TimeZone> SubAssign<TimeDelta> for DateTime<Tz> {
}
}

impl<Tz: TimeZone> SubAssign<Duration> for DateTime<Tz> {
#[inline]
fn sub_assign(&mut self, rhs: Duration) {
let rhs = TimeDelta::from_std(rhs)
.expect("overflow converting from core::time::Duration to chrono::Duration");
*self -= rhs;
}
}

impl<Tz: TimeZone> Sub<Months> for DateTime<Tz> {
type Output = DateTime<Tz>;

Expand Down Expand Up @@ -1388,8 +1402,6 @@ impl From<SystemTime> for DateTime<Local> {
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl<Tz: TimeZone> From<DateTime<Tz>> for SystemTime {
fn from(dt: DateTime<Tz>) -> SystemTime {
use std::time::Duration;

let sec = dt.timestamp();
let nsec = dt.timestamp_subsec_nanos();
if sec < 0 {
Expand Down Expand Up @@ -1501,7 +1513,9 @@ where
&FixedOffset::east_opt(3650).unwrap().with_ymd_and_hms(2014, 7, 24, 12, 34, 6).unwrap()
)
.ok(),
Some(r#""2014-07-24T12:34:06+01:00:50""#.into())
// An offset with seconds is not allowed by RFC 3339, so we round it to the nearest minute.
// In this case `+01:00:50` becomes `+01:01`
Some(r#""2014-07-24T12:34:06+01:01""#.into())
);
}

Expand Down
Loading

0 comments on commit 794e0b5

Please sign in to comment.