diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 02deb5a95d..1b9dcdd593 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,3 +1,2 @@
github: jhpratt
-patreon: jhpratt
custom: ["paypal.me/jhpratt"]
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 643283a3b6..e6124367a3 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -6,6 +6,25 @@ concurrency:
env:
CARGO_INCREMENTAL: 0
+ TYPE_CHECK_TARGETS: '{
+ "no_std": [
+ "thumbv7em-none-eabihf"
+ ],
+ "std_no_offset": [
+ "x86_64-unknown-netbsd",
+ "x86_64-unknown-illumos",
+ "wasm32-wasi"
+ ],
+ "std_with_offset": [
+ "x86_64-unknown-linux-gnu",
+ "x86_64-apple-darwin",
+ "x86_64-pc-windows-gnu"
+ ]
+ }'
+
+defaults:
+ run:
+ shell: bash
on:
push:
@@ -26,142 +45,112 @@ on:
- logo.svg
jobs:
- check-other-targets:
- name: Type checking (${{ matrix.target.name }}, ${{ matrix.rust.name }})
+ check-targets:
+ name: Type checking (${{ matrix.rust.name }}, ${{ matrix.kind.name }})
runs-on: ubuntu-20.04
- if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) || github.event_name == 'push' }}
+ if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) || github.event_name == 'push'
strategy:
matrix:
rust:
- - { version: "1.59", name: MSRV }
+ - { version: "1.59.0", name: MSRV }
- { version: stable, name: stable }
- target:
- - name: NetBSD
- triple: x86_64-unknown-netbsd
- has_std: true
- has_local_offset: false
- - name: Illumos
- triple: x86_64-unknown-illumos
- has_std: true
- has_local_offset: false
- - name: wasi
- triple: wasm32-wasi
- has_std: true
- has_local_offset: false
- - name: embedded
- triple: thumbv7em-none-eabihf
- has_std: false
- has_local_offset: false
+ kind:
+ - name: no_std
+ query: .no_std + .std_no_offset + .std_with_offset
+ exclude-features:
+ - std
+ - formatting
+ - serde-human-readable
+ - serde-well-known
+ - local-offset
+ - quickcheck
+ group-features: []
+ - name: std_no_offset
+ query: .std_no_offset + .std_with_offset
+ exclude-features: [local-offset]
+ enable-features: [std]
+ group-features:
+ - [formatting, parsing]
+ - [serde-human-readable, serde-well-known]
+ - name: std_with_offset
+ query: .std_with_offset
+ enable-features: [std, local-offset]
+ group-features:
+ - [formatting, parsing]
+ - [serde-human-readable, serde-well-known]
steps:
- name: Checkout sources
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
+
+ - name: Generate target list
+ run: jq -r 'env.TYPE_CHECK_TARGETS | ${{ matrix.kind.query }} | join(",") | "TARGETS=" + .' >> $GITHUB_ENV
- name: Install toolchain
- uses: actions-rs/toolchain@v1
+ uses: dtolnay/rust-toolchain@master
with:
- profile: minimal
toolchain: ${{ matrix.rust.version }}
- target: ${{ matrix.target.triple }}
- override: true
+ targets: ${{ env.TARGETS }}
- name: Install cargo-hack
shell: bash
run: |
- curl -LsSf https://github.com/taiki-e/cargo-hack/releases/latest/download/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xzf - -C ~/.cargo/bin
+ curl -LsSf https://github.com/taiki-e/cargo-hack/releases/latest/download/cargo-hack-x86_64-unknown-linux-gnu.tar.gz \
+ | tar xzf - -C ~/.cargo/bin
- name: Cache cargo output
- uses: Swatinem/rust-cache@v1
+ uses: Swatinem/rust-cache@v2
with:
- key: ${{ matrix.target.triple }}
+ key: ${{ matrix.kind.name }}
- name: Check feature powerset
- uses: actions-rs/cargo@v1
- with:
- command: hack
- args: |
- check
- --no-dev-deps
- --feature-powerset
- --optional-deps
- --group-features serde,rand
- --exclude-features default,std,formatting,serde-human-readable,serde-well-known,local-offset,quickcheck,quickcheck-dep,time-macros,itoa,js-sys,wasm-bindgen
- --features macros
- --exclude-all-features
- --target ${{ matrix.target.triple }}
- if: matrix.target.has_std == false
-
- # Unconditionally enable the local-offset flag when the target doesn't provide any useful
- # information.
- # This currently _does not_ include NetBSD or Solaris due to a soundness bug.
- - name: Check feature powerset
- uses: actions-rs/cargo@v1
- with:
- command: hack
- args: |
- check
- --no-dev-deps
- --feature-powerset
- --optional-deps
- --group-features serde,rand
- --group-features formatting,parsing
- --group-features serde-human-readable,serde-well-known
- --exclude-features default,quickcheck-dep,time-macros,itoa,js-sys,wasm-bindgen
- --features macros,local-offset
- --target ${{ matrix.target.triple }}
- if: matrix.target.has_std == true && matrix.target.has_local_offset == false
-
- - name: Check feature powerset
- uses: actions-rs/cargo@v1
- with:
- command: hack
- args: |
- check
- --no-dev-deps
- --feature-powerset
- --optional-deps
- --group-features serde,rand
- --group-features formatting,parsing
- --group-features serde-human-readable,serde-well-known
- --exclude-features default,quickcheck-dep,time-macros,itoa,js-sys,wasm-bindgen
- --features macros
- --target ${{ matrix.target.triple }}
- if: matrix.target.has_std == true && matrix.target.has_local_offset == true
+ env:
+ GROUP_FEATURES: ${{ toJSON(matrix.kind.group-features) }}
+ run: |
+ jq -r 'env.GROUP_FEATURES | [.[] | join(",")] | map("--group-features " + .) | join(" ")' \
+ | xargs -d" " \
+ | ( \
+ jq -r 'env.TYPE_CHECK_TARGETS | ${{ matrix.kind.query }} | map("--target " + .) | join(" ")' \
+ | xargs -d" " \
+ cargo hack check \
+ --no-dev-deps \
+ --feature-powerset \
+ --optional-deps \
+ --group-features serde,rand \
+ --exclude-features default,quickcheck-dep,time-macros,itoa,js-sys,wasm-bindgen,${{
+ join(matrix.kind.exclude-features)
+ }} \
+ --features macros,${{ join(matrix.kind.enable-features) }} \
+ --exclude-all-features \
+ )
check-benchmarks:
name: Type-check benchmarks
runs-on: ubuntu-20.04
- if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) || github.event_name == 'push' }}
+ if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) || github.event_name == 'push'
steps:
- name: Checkout sources
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Install toolchain
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: stable
- override: true
+ uses: dtolnay/rust-toolchain@stable
- name: Cache cargo output
- uses: Swatinem/rust-cache@v1
+ uses: Swatinem/rust-cache@v2
- name: Type-check benchmarks
- uses: actions-rs/cargo@v1
- with:
- command: check
- args: --benches --all-features
+ run: cargo check --benches --all-features
env:
- RUSTFLAGS: "--cfg bench"
+ RUSTFLAGS: --cfg bench
test:
name: Test (${{ matrix.os.name }}, ${{ matrix.rust.name }})
runs-on: ${{ matrix.os.value }}
- if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) || github.event_name == 'push' }}
+ if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) || github.event_name == 'push'
strategy:
matrix:
rust:
- - { version: "1.59", name: MSRV }
+ - { version: "1.59.0", name: MSRV }
- { version: stable, name: stable }
os:
- { name: Ubuntu, value: ubuntu-20.04 }
@@ -170,17 +159,14 @@ jobs:
steps:
- name: Checkout sources
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Install toolchain
- uses: actions-rs/toolchain@v1
+ uses: dtolnay/rust-toolchain@master
with:
- profile: minimal
toolchain: ${{ matrix.rust.version }}
- override: true
- name: Install cargo-hack
- shell: bash
run: |
host=$(rustc -Vv | grep host | sed 's/host: //')
if [[ $host =~ windows ]]; then
@@ -194,143 +180,120 @@ jobs:
fi
- name: Cache cargo output
- uses: Swatinem/rust-cache@v1
-
- - name: Check feature powerset
- uses: actions-rs/cargo@v1
- with:
- command: hack
- args: |
- check
- --feature-powerset
- --optional-deps
- --group-features serde,rand
- --group-features formatting,parsing
- --group-features serde-human-readable,serde-well-known
- --exclude-features default,quickcheck-dep,time-macros,itoa,js-sys,wasm-bindgen
- --features macros
- if: matrix.os.has_local_offset == true
+ uses: Swatinem/rust-cache@v2
- name: Test
- uses: actions-rs/cargo@v1
- with:
- command: test
- args: --all-features
+ run: cargo test --all-features
cross-build:
name: Cross-build
runs-on: ubuntu-20.04
- if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) || github.event_name == 'push' }}
+ if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) || github.event_name == 'push'
steps:
- name: Checkout sources
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Install toolchain
- uses: actions-rs/toolchain@v1
+ uses: dtolnay/rust-toolchain@stable
with:
- profile: minimal
- toolchain: stable
- override: true
- target: x86_64-pc-windows-gnu
+ targets: x86_64-pc-windows-gnu
- name: Cache cargo output
- uses: Swatinem/rust-cache@v1
+ uses: Swatinem/rust-cache@v2
- name: Install dependencies
run: sudo apt install gcc-mingw-w64
+ # We're testing the linking, so running `cargo check` is insufficient.
- name: Cross-build tests
- uses: actions-rs/cargo@v1
- with:
- # We're testing the linking, so running `cargo check` is insufficient.
- command: build
- args: --tests --all-features --target x86_64-pc-windows-gnu
+ run: cargo build --tests --all-features --target x86_64-pc-windows-gnu
fmt:
name: Formatting
runs-on: ubuntu-20.04
- if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) || github.event_name == 'push' }}
+ if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) || github.event_name == 'push'
steps:
- name: Checkout sources
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Install toolchain
- uses: actions-rs/toolchain@v1
+ uses: dtolnay/rust-toolchain@nightly
with:
- profile: minimal
- toolchain: nightly
- override: true
components: rustfmt
- name: Check formatting
- uses: actions-rs/cargo@v1
- with:
- command: fmt
- args: --all -- --check
+ run: cargo fmt --all -- --check
env:
- RUSTFLAGS: "--cfg bench"
+ RUSTFLAGS: --cfg bench
clippy:
name: Clippy
runs-on: ubuntu-20.04
- if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) || github.event_name == 'push' }}
+ if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) || github.event_name == 'push'
steps:
- name: Checkout sources
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Install toolchain
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: stable
- override: true
+ uses: dtolnay/rust-toolchain@stable
+
+ - name: Install targets
+ run: |
+ rustup target add \
+ x86_64-unknown-linux-gnu \
+ aarch64-apple-darwin \
+ x86_64-pc-windows-gnu \
+ x86_64-unknown-netbsd \
+ x86_64-unknown-illumos \
+ wasm32-wasi
- name: Cache cargo output
- uses: Swatinem/rust-cache@v1
+ uses: Swatinem/rust-cache@v2
- name: Run clippy
- uses: actions-rs/clippy-check@v1
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
- args: --all-features --benches --tests
+ run: |
+ cargo clippy \
+ --all-features \
+ --benches \
+ --tests \
+ --target x86_64-unknown-linux-gnu \
+ --target aarch64-apple-darwin \
+ --target x86_64-pc-windows-gnu \
+ --target x86_64-unknown-netbsd \
+ --target x86_64-unknown-illumos \
+ --target wasm32-wasi
env:
- RUSTFLAGS: "--cfg bench"
+ RUSTFLAGS: --cfg bench
documentation:
name: Documentation
runs-on: ubuntu-20.04
- if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) || github.event_name == 'push' }}
+ if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) || github.event_name == 'push'
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
persist-credentials: false
- name: Install toolchain
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: nightly
- override: true
+ uses: dtolnay/rust-toolchain@nightly
- name: Cache cargo output
- uses: Swatinem/rust-cache@v1
+ uses: Swatinem/rust-cache@v2
- name: Document public API
- uses: actions-rs/cargo@v1
- with:
- command: doc
- args: --all-features --no-deps -Zrustdoc-map
+ run: cargo doc --all-features --no-deps -Zrustdoc-map
env:
RUSTDOCFLAGS: --cfg __time_03_docs
- name: Create top-level redirect
run: |
- echo "" > ./target/doc/index.html
+ echo "" \
+ > ./target/doc/index.html
- name: Publish public docs
uses: JamesIves/github-pages-deploy-action@releases/v4
@@ -343,16 +306,14 @@ jobs:
if: github.event_name == 'push' && github.ref == format('refs/heads/{0}', github.event.repository.master_branch)
- name: Document internal API
- uses: actions-rs/cargo@v1
- with:
- command: doc
- args: --all-features --no-deps -Zrustdoc-map --document-private-items
+ run: cargo doc --all-features --no-deps -Zrustdoc-map --document-private-items
env:
RUSTDOCFLAGS: --cfg __time_03_docs --document-hidden-items
- name: Create top-level redirect
run: |
- echo "" > ./target/doc/index.html
+ echo "" \
+ > ./target/doc/index.html
- name: Publish internal docs
uses: JamesIves/github-pages-deploy-action@releases/v4
@@ -367,31 +328,30 @@ jobs:
coverage:
name: Coverage
runs-on: ubuntu-20.04
- if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) || github.event_name == 'push' }}
+ if: (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork) || github.event_name == 'push'
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
persist-credentials: false
- name: Install toolchain
- uses: actions-rs/toolchain@v1
- with:
- profile: minimal
- toolchain: nightly
- override: true
+ uses: dtolnay/rust-toolchain@nightly
- name: Install cargo-llvm-cov
run: |
- curl -LsSf https://github.com/taiki-e/cargo-llvm-cov/releases/latest/download/cargo-llvm-cov-x86_64-unknown-linux-gnu.tar.gz | tar xzf - -C ~/.cargo/bin
+ curl -LsSf https://github.com/taiki-e/cargo-llvm-cov/releases/latest/download/cargo-llvm-cov-x86_64-unknown-linux-gnu.tar.gz \
+ | tar xzf - -C ~/.cargo/bin
- name: Generate coverage report
run: |
cargo llvm-cov clean --workspace
cargo llvm-cov test --no-report --all-features -- --test-threads=1
- RUSTFLAGS="--cfg __ui_tests" cargo llvm-cov test --no-report --tests --all-features -- compile_fail
+ cargo llvm-cov test --no-report --tests --all-features -- compile_fail
cargo llvm-cov report --lcov > lcov.txt
+ env:
+ RUSTFLAGS: --cfg __ui_tests
- name: Upload coverage report
uses: codecov/codecov-action@v1
diff --git a/.github/workflows/powerset.yaml b/.github/workflows/powerset.yaml
index c357d50ec0..13de3bf727 100644
--- a/.github/workflows/powerset.yaml
+++ b/.github/workflows/powerset.yaml
@@ -2,11 +2,39 @@ name: "Check powerset"
env:
RUSTFLAGS: -Dwarnings
+ TARGETS: '{
+ "no_std": [
+ "thumbv7em-none-eabihf"
+ ],
+ "std_no_offset": [
+ "aarch64-fuchsia",
+ "x86_64-fuchsia"
+ ],
+ "std_with_offset": [
+ "aarch64-unknown-linux-gnu",
+ "i686-pc-windows-gnu",
+ "i686-pc-windows-msvc",
+ "i686-unknown-linux-gnu",
+ "x86_64-apple-darwin",
+ "x86_64-pc-windows-gnu",
+ "x86_64-pc-windows-msvc",
+ "x86_64-unknown-linux-gnu",
+ "aarch64-linux-android",
+ "wasm32-wasi",
+ "x86_64-linux-android",
+ "x86_64-unknown-netbsd",
+ "x86_64-unknown-illumos"
+ ]
+ }'
concurrency:
group: powerset-${{ github.head_ref }}
cancel-in-progress: true
+defaults:
+ run:
+ shell: bash
+
on:
schedule:
- cron: "0 0 * * 1" # midnight on Monday
@@ -19,120 +47,71 @@ on:
workflow_dispatch:
jobs:
- check-powerset:
- name: Type checking (${{ matrix.target.name }})
+ check:
+ name: Type checking (${{ matrix.kind.name }}, ${{ matrix.rust.name }})
runs-on: ubuntu-latest
strategy:
- fail-fast: true
matrix:
- target:
- # All tier 1 platforms as of 2022-01-26
- - name: ARM64 Linux
- triple: aarch64-unknown-linux-gnu
- has_std: true
- - name: 32-bit MinGW
- triple: i686-pc-windows-gnu
- has_std: true
- - name: 32-bit MSVC
- triple: i686-pc-windows-msvc
- has_std: true
- - name: 32-bit Linux
- triple: i686-unknown-linux-gnu
- has_std: true
- - name: 64-bit macOS
- triple: x86_64-apple-darwin
- has_std: true
- - name: 64-bit MinGW
- triple: x86_64-pc-windows-gnu
- has_std: true
- - name: 64-bit MSVC
- triple: x86_64-pc-windows-msvc
- has_std: true
- - name: 64-bit Linux
- triple: x86_64-unknown-linux-gnu
- has_std: true
- # Select tier 2 platforms as of 2022-01-26
- - name: ARM64 Fuchsia
- triple: aarch64-fuchsia
- has_std: true
- - name: ARM64 Android
- triple: aarch64-linux-android
- has_std: true
- - name: Bare Cortex
- triple: thumbv7em-none-eabihf
- has_std: false
- - name: WASI
- triple: wasm32-wasi
- has_std: true
- - name: 64-bit Fuchsia
- triple: x86_64-fuchsia
- has_std: true
- - name: 64-bit x86 Android
- triple: x86_64-linux-android
- has_std: true,
- - name: NetBSD
- triple: x86_64-unknown-netbsd
- has_std: true
- - name: Illumos
- triple: x86_64-unknown-illumos
- has_std: true
+ rust:
+ - { version: "1.59.0", name: MSRV }
+ - { version: stable, name: stable }
+ kind:
+ - name: no_std
+ query: .no_std + .std_no_offset + .std_with_offset
+ exclude_features:
+ - std
+ - local-offset
+ - quickcheck
+ - formatting
+ - serde-human-readable
+ - serde-well-known
+ - name: std_no_offset
+ query: .std_no_offset + .std_with_offset
+ exclude_features: [local-offset]
+ enable_features: [std]
+ - name: std_with_offset
+ query: .std_with_offset
+ enable_features: [std, local-offset]
steps:
- name: Checkout sources
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
+
+ - name: Generate target list
+ run: jq -r 'env.TARGETS | ${{ matrix.kind.query }} | join(",") | "TARGET_LIST=" + .' >> $GITHUB_ENV
- name: Install toolchain
- uses: actions-rs/toolchain@v1
+ uses: dtolnay/rust-toolchain@master
with:
- profile: minimal
- toolchain: stable
- target: ${{ matrix.target.triple }}
- override: true
+ targets: ${{ env.TARGET_LIST }}
+ toolchain: ${{ matrix.rust.version}}
- name: Install cargo-hack
shell: bash
run: |
- curl -LsSf https://github.com/taiki-e/cargo-hack/releases/latest/download/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xzf - -C ~/.cargo/bin
+ curl -LsSf https://github.com/taiki-e/cargo-hack/releases/latest/download/cargo-hack-x86_64-unknown-linux-gnu.tar.gz \
+ | tar xzf - -C ~/.cargo/bin
- name: Check feature powerset
- uses: actions-rs/cargo@v1
- with:
- command: hack
- args: |
- check
- --no-dev-deps
- --version-range 1.59..
- --clean-per-version
- --feature-powerset
- --optional-deps
- --exclude-features default,std,local-offset,quickcheck,quickcheck-dep,time-macros,formatting,itoa,serde-human-readable,serde-well-known,js-sys,wasm-bindgen
- --exclude-all-features
- --target ${{ matrix.target.triple }}
- if: matrix.target.has_std == false
-
- - name: Check feature powerset
- uses: actions-rs/cargo@v1
- with:
- command: hack
- args: |
- check
- --no-dev-deps
- --version-range 1.59..
- --clean-per-version
- --feature-powerset
- --optional-deps
- --exclude-features default,quickcheck-dep,time-macros,itoa,js-sys,wasm-bindgen
- --target ${{ matrix.target.triple }}
- if: matrix.target.has_std == true
+ run: |
+ | jq -r 'env.TARGETS | ${{ matrix.kind.query }} | map("--target " + .) | join(" ")' \
+ | xargs -d" " \
+ cargo hack check \
+ --no-dev-deps \
+ --feature-powerset \
+ --optional-deps \
+ --exclude-features default,quickcheck-dep,time-macros,itoa,js-sys,wasm-bindgen,${{
+ join(matrix.kind.exclude_features)
+ }} ${{ matrix.kind.enable_features && format('--features {0}', join(matrix.kind.enable_features)) }}
release:
name: Create release
if: startsWith(github.ref, 'refs/tags') && github.run_attempt == 1
- needs: check-powerset
+ needs: check
runs-on: ubuntu-latest
steps:
- name: Checkout code
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Create release
uses: actions/create-release@v1
@@ -141,7 +120,7 @@ jobs:
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
- body: "See the [changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md) for details."
+ body: See the [changelog](https://github.com/time-rs/time/blob/main/CHANGELOG.md) for details.
draft: false
prerelease: |
${{
diff --git a/.github/workflows/scheduled.yaml b/.github/workflows/scheduled.yaml
index eb1fba4b55..e959e88221 100644
--- a/.github/workflows/scheduled.yaml
+++ b/.github/workflows/scheduled.yaml
@@ -1,31 +1,33 @@
-name: "Scheduled tasks"
+name: Scheduled tasks
on:
schedule:
- cron: "0 0 * * 1,5" # midnight on Monday, Friday
+ workflow_dispatch:
+
+permissions:
+ pull-requests: write
jobs:
stale:
name: Close stale PRs
runs-on: ubuntu-20.04
steps:
- - uses: actions/stale@v2
+ - uses: actions/stale@v6
with:
- repo-token: ${{ secrets.GITHUB_TOKEN }}
- stale-pr-message: "This pull request has not had any activity recently. It will be closed without further activity."
-
- days-before-stale: 14
+ enable-statistics: false
+ stale-pr-message: This pull request has not had any activity recently. It will be closed without further activity.
+ days-before-stale: 30
days-before-close: 7
-
- stale-pr-label: "C-stale"
- exempt-pr-labels: "C-keep-open"
+ stale-pr-label: C-stale
+ exempt-pr-labels: C-keep-open
security-audit:
name: Security audit
runs-on: ubuntu-20.04
steps:
- name: Checkout sources
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
- name: Audit dependencies
uses: actions-rs/audit-check@v1
diff --git a/src/duration.rs b/src/duration.rs
index 59f55722a1..f8d916f451 100644
--- a/src/duration.rs
+++ b/src/duration.rs
@@ -227,13 +227,18 @@ impl Duration {
/// assert_eq!(Duration::new(1, 2_000_000_000), 3.seconds());
/// ```
pub const fn new(mut seconds: i64, mut nanoseconds: i32) -> Self {
- seconds += nanoseconds as i64 / 1_000_000_000;
+ seconds = expect_opt!(
+ seconds.checked_add(nanoseconds as i64 / 1_000_000_000),
+ "overflow constructing `time::Duration`"
+ );
nanoseconds %= 1_000_000_000;
if seconds > 0 && nanoseconds < 0 {
+ // `seconds` cannot overflow here because it is positive.
seconds -= 1;
nanoseconds += 1_000_000_000;
} else if seconds < 0 && nanoseconds > 0 {
+ // `seconds` cannot overflow here because it is negative.
seconds += 1;
nanoseconds -= 1_000_000_000;
}
@@ -249,7 +254,10 @@ impl Duration {
/// assert_eq!(Duration::weeks(1), 604_800.seconds());
/// ```
pub const fn weeks(weeks: i64) -> Self {
- Self::seconds(weeks * 604_800)
+ Self::seconds(expect_opt!(
+ weeks.checked_mul(604_800),
+ "overflow constructing `time::Duration`"
+ ))
}
/// Create a new `Duration` with the given number of days. Equivalent to
@@ -260,7 +268,10 @@ impl Duration {
/// assert_eq!(Duration::days(1), 86_400.seconds());
/// ```
pub const fn days(days: i64) -> Self {
- Self::seconds(days * 86_400)
+ Self::seconds(expect_opt!(
+ days.checked_mul(86_400),
+ "overflow constructing `time::Duration`"
+ ))
}
/// Create a new `Duration` with the given number of hours. Equivalent to
@@ -271,7 +282,10 @@ impl Duration {
/// assert_eq!(Duration::hours(1), 3_600.seconds());
/// ```
pub const fn hours(hours: i64) -> Self {
- Self::seconds(hours * 3_600)
+ Self::seconds(expect_opt!(
+ hours.checked_mul(3_600),
+ "overflow constructing `time::Duration`"
+ ))
}
/// Create a new `Duration` with the given number of minutes. Equivalent to
@@ -282,7 +296,10 @@ impl Duration {
/// assert_eq!(Duration::minutes(1), 60.seconds());
/// ```
pub const fn minutes(minutes: i64) -> Self {
- Self::seconds(minutes * 60)
+ Self::seconds(expect_opt!(
+ minutes.checked_mul(60),
+ "overflow constructing `time::Duration`"
+ ))
}
/// Create a new `Duration` with the given number of seconds.
@@ -303,6 +320,12 @@ impl Duration {
/// assert_eq!(Duration::seconds_f64(-0.5), -0.5.seconds());
/// ```
pub fn seconds_f64(seconds: f64) -> Self {
+ if seconds > i64::MAX as f64 || seconds < i64::MIN as f64 {
+ crate::expect_failed("overflow constructing `time::Duration`");
+ }
+ if seconds.is_nan() {
+ crate::expect_failed("passed NaN to `time::Duration::seconds_f64`");
+ }
Self::new_unchecked(seconds as _, ((seconds % 1.) * 1_000_000_000.) as _)
}
@@ -314,6 +337,12 @@ impl Duration {
/// assert_eq!(Duration::seconds_f32(-0.5), (-0.5).seconds());
/// ```
pub fn seconds_f32(seconds: f32) -> Self {
+ if seconds > i64::MAX as f32 || seconds < i64::MIN as f32 {
+ crate::expect_failed("overflow constructing `time::Duration`");
+ }
+ if seconds.is_nan() {
+ crate::expect_failed("passed NaN to `time::Duration::seconds_f32`");
+ }
Self::new_unchecked(seconds as _, ((seconds % 1.) * 1_000_000_000.) as _)
}
@@ -364,10 +393,14 @@ impl Duration {
/// As the input range cannot be fully mapped to the output, this should only be used where it's
/// known to result in a valid value.
pub(crate) const fn nanoseconds_i128(nanoseconds: i128) -> Self {
- Self::new_unchecked(
- (nanoseconds / 1_000_000_000) as _,
- (nanoseconds % 1_000_000_000) as _,
- )
+ let seconds = nanoseconds / 1_000_000_000;
+ let nanoseconds = nanoseconds % 1_000_000_000;
+
+ if seconds > i64::MAX as i128 || seconds < i64::MIN as i128 {
+ crate::expect_failed("overflow constructing `time::Duration`");
+ }
+
+ Self::new_unchecked(seconds as _, nanoseconds as _)
}
// endregion constructors
diff --git a/src/lib.rs b/src/lib.rs
index 0583b16686..b13a7df2be 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -283,6 +283,18 @@ macro_rules! const_try_opt {
}
};
}
+
+/// Try to unwrap an expression, panicking if not possible.
+///
+/// This is similar to `$e.expect($message)`, but is usable in `const` contexts.
+macro_rules! expect_opt {
+ ($e:expr, $message:literal) => {
+ match $e {
+ Some(value) => value,
+ None => crate::expect_failed($message),
+ }
+ };
+}
// endregion macros
mod date;
@@ -334,3 +346,11 @@ pub use crate::weekday::Weekday;
/// An alias for [`std::result::Result`] with a generic error from the time crate.
pub type Result = core::result::Result;
+
+/// This is a separate function to reduce the code size of `expect_opt!`.
+#[inline(never)]
+#[cold]
+#[track_caller]
+const fn expect_failed(message: &str) -> ! {
+ panic!("{}", message)
+}
diff --git a/tests/integration/duration.rs b/tests/integration/duration.rs
index 66a2f3e174..b11ccb2c43 100644
--- a/tests/integration/duration.rs
+++ b/tests/integration/duration.rs
@@ -82,6 +82,9 @@ fn new() {
assert_eq!(Duration::new(1, -1_400_000_000), (-400).milliseconds());
assert_eq!(Duration::new(2, -1_400_000_000), 600.milliseconds());
assert_eq!(Duration::new(3, -1_400_000_000), 1_600.milliseconds());
+
+ assert_panic!(Duration::new(i64::MAX, 1_000_000_000));
+ assert_panic!(Duration::new(i64::MIN, -1_000_000_000));
}
#[test]
@@ -90,6 +93,9 @@ fn weeks() {
assert_eq!(Duration::weeks(2), (2 * 604_800).seconds());
assert_eq!(Duration::weeks(-1), (-604_800).seconds());
assert_eq!(Duration::weeks(-2), (2 * -604_800).seconds());
+
+ assert_panic!(Duration::weeks(i64::MAX));
+ assert_panic!(Duration::weeks(i64::MIN));
}
#[test]
@@ -106,6 +112,9 @@ fn days() {
assert_eq!(Duration::days(2), (2 * 86_400).seconds());
assert_eq!(Duration::days(-1), (-86_400).seconds());
assert_eq!(Duration::days(-2), (2 * -86_400).seconds());
+
+ assert_panic!(Duration::days(i64::MAX));
+ assert_panic!(Duration::days(i64::MIN));
}
#[test]
@@ -122,6 +131,9 @@ fn hours() {
assert_eq!(Duration::hours(2), (2 * 3_600).seconds());
assert_eq!(Duration::hours(-1), (-3_600).seconds());
assert_eq!(Duration::hours(-2), (2 * -3_600).seconds());
+
+ assert_panic!(Duration::hours(i64::MAX));
+ assert_panic!(Duration::hours(i64::MIN));
}
#[test]
@@ -138,6 +150,9 @@ fn minutes() {
assert_eq!(Duration::minutes(2), (2 * 60).seconds());
assert_eq!(Duration::minutes(-1), (-60).seconds());
assert_eq!(Duration::minutes(-2), (2 * -60).seconds());
+
+ assert_panic!(Duration::minutes(i64::MAX));
+ assert_panic!(Duration::minutes(i64::MIN));
}
#[test]
@@ -168,6 +183,10 @@ fn whole_seconds() {
fn seconds_f64() {
assert_eq!(Duration::seconds_f64(0.5), 0.5.seconds());
assert_eq!(Duration::seconds_f64(-0.5), (-0.5).seconds());
+
+ assert_panic!(Duration::seconds_f64(f64::MAX));
+ assert_panic!(Duration::seconds_f64(f64::MIN));
+ assert_panic!(Duration::seconds_f64(f64::NAN));
}
#[test]
@@ -185,6 +204,10 @@ fn as_seconds_f64() {
fn seconds_f32() {
assert_eq!(Duration::seconds_f32(0.5), 0.5.seconds());
assert_eq!(Duration::seconds_f32(-0.5), (-0.5).seconds());
+
+ assert_panic!(Duration::seconds_f32(f32::MAX));
+ assert_panic!(Duration::seconds_f32(f32::MIN));
+ assert_panic!(Duration::seconds_f32(f32::NAN));
}
#[test]
@@ -600,6 +623,9 @@ fn std_sub_assign_overflow() {
fn mul_int() {
assert_eq!(1.seconds() * 2, 2.seconds());
assert_eq!(1.seconds() * -2, (-2).seconds());
+
+ assert_panic!(Duration::MAX * 2);
+ assert_panic!(Duration::MIN * 2);
}
#[test]
diff --git a/tests/integration/main.rs b/tests/integration/main.rs
index c30534788e..7a0dcd2e18 100644
--- a/tests/integration/main.rs
+++ b/tests/integration/main.rs
@@ -71,6 +71,16 @@ macro_rules! modifier {
(@value $field:ident $value:expr) => ($value);
}
+/// Assert that the given expression panics.
+macro_rules! assert_panic {
+ ($($x:tt)*) => {
+ assert!(std::panic::catch_unwind(|| {
+ $($x)*
+ })
+ .is_err())
+ }
+}
+
mod date;
mod derives;
mod duration;
diff --git a/tests/integration/offset_date_time.rs b/tests/integration/offset_date_time.rs
index 73baa9459f..c6af8933e5 100644
--- a/tests/integration/offset_date_time.rs
+++ b/tests/integration/offset_date_time.rs
@@ -51,14 +51,8 @@ fn to_offset() {
#[test]
fn to_offset_panic() {
- assert!(
- std::panic::catch_unwind(|| { PrimitiveDateTime::MAX.assume_utc().to_offset(offset!(+1)) })
- .is_err()
- );
- assert!(
- std::panic::catch_unwind(|| { PrimitiveDateTime::MIN.assume_utc().to_offset(offset!(-1)) })
- .is_err()
- );
+ assert_panic!(PrimitiveDateTime::MAX.assume_utc().to_offset(offset!(+1)));
+ assert_panic!(PrimitiveDateTime::MIN.assume_utc().to_offset(offset!(-1)));
}
#[test]