GithubHelp home page GithubHelp logo

cron's People

Contributors

adeschamps avatar aldaronlau avatar atouchet avatar bittrance avatar blackdex avatar bstrie avatar deankarn avatar dsander avatar elbe0046 avatar hgzimmerman avatar koenichiwa avatar lholden avatar mehcode avatar messense avatar mmstick avatar nickelc avatar rtyler avatar sebest avatar sqwishy avatar william20111 avatar zslayton avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cron's Issues

cron 0.3.0 fails to compile on rustc nightly-2017-07-09

Receiving the following error on rustc nightly-2017-07-09:

error: use of unstable library feature 'iterator_step_by': unstable replacement of Range::step_by (see issue #27741)
   --> /Users/joshua.holmer/.cargo/registry/src/github.com-1ecc6299db9ec823/cron-0.3.0/src/time_unit/mod.rs:219:55
    |
219 |                 Ok((start..Self::inclusive_max() + 1).step_by(step).collect())
    |                                                       ^^^^^^^
    |
    = help: add #![feature(iterator_step_by)] to the crate attributes to enable

error[E0308]: mismatched types
   --> /Users/joshua.holmer/.cargo/registry/src/github.com-1ecc6299db9ec823/cron-0.3.0/src/time_unit/mod.rs:219:63
    |
219 |                 Ok((start..Self::inclusive_max() + 1).step_by(step).collect())
    |                                                               ^^^^ expected usize, found u32

error: aborting due to 2 previous errors

error: Could not compile `cron`.

cron 0.3.0 compiles successfully on rustc nightly-2017-07-02.

Impl TryFrom<&str> for Schedule

A downstream crate I use exposes an API that takes strings, converts them to Schedules, which may fail.
Ideally, I'd like to construct Schedules myself and pass them to the crate's functions, and handle possible errors before the library has a chance to produce errors.
For this to be practical, it would need to avoid breaking API compatibility so severely, and the best way to do that would be for their functions to take the schedule argument as a S: TryInto<cron::Schedule>.

If TryFrom<&str> for Schedule is implemented, this will allow the functions to take &strs like they do currently, as well as support taking cron::Schedule, since it has a blanket impl for TryFrom<T> for T.

Incorrectly parses shorthand with suffix e.g. "@dailyBla"

Hi, first thanks for this crate. When testing it I've found that a shorthand (e.g. @weekly) followed by any characters (e.g. @weeklyFoo) will be parsed as @weekly.

To be more strict it probably should return an error in this case or what do you think?

More meaningful error messages

Due to my unfamiliarity with nom's error handling, the meaningful messages produced when errors occur are lost when bubbling up. The map_res! macro discards them.

impl Clone for Schedule

It would be usefull to be able to clone a Schedule and not to have to parse a string multiple times.

Is it possible to use as a CLI?

I’d like to use this tool as a CLI, but I don’t know it’s possible since its README doesn’t mention about it.

If it’s not possible for now, is there a plan to implement that?

Thank you!

Upcoming year for past date not used for schedule

I am observing an issue where using a cron set to a date next year but already passed this years results in no upcoming date.

Observed behavior:

I am posting this issue on 2021-11-24, yesterday was 2021-11-23. Setting a cron to yesterday next year (2022) results in no upcoming date.

    let schedule = Schedule::from_str("0 0 1 23 11 * 2022").unwrap();
    println!("Upcoming fire times:");
    for datetime in schedule.upcoming(Utc).take(3) {
        println!("-> {}", datetime);
    }
Upcoming fire times:

Workaround:

This is solved by not setting the year or setting a repeated year. e.g.

    let schedule = Schedule::from_str("0 0 1 23 11 * 2022/1").unwrap();
// equivalent: let schedule = Schedule::from_str("0 0 1 23 11 * * ").unwrap();
// equivalent: let schedule = Schedule::from_str("0 0 1 23 11 * ").unwrap();
    println!("Upcoming fire times:");
    for datetime in schedule.upcoming(Utc).take(3) {
        println!("-> {}", datetime);
    }
Upcoming fire times:
-> 2023-11-23 01:00:00 UTC
-> 2024-11-23 01:00:00 UTC
-> 2025-11-23 01:00:00 UTC

Merge both Query structs into one / Reimplement query functions

PrevFromQuery and NextAfterQuery are almost the same. This is also true for next_after(...) and prev_from(...) in Schedule. This seems to break with the DRY principle.

You could implement Query as follows:

pub enum QueryDirection {
    Next,
    Previous,
}

pub struct Query<Z> 
    where
    Z: TimeZone,
{
    initial_datetime: DateTime<Z>,
    first_month: bool,
    first_day_of_month: bool,
    first_hour: bool,
    first_minute: bool,
    first_second: bool,
    direction: QueryDirection,
}

impl<Z> Query<Z> 
where
    Z: TimeZone,
{
    pub fn from(date: &DateTime<Z>, direction: QueryDirection) -> Query<Z> {
        let new_date = match direction {
            QueryDirection::Next => date.clone() + Duration::seconds(1),
            QueryDirection::Previous => date.clone() - Duration::seconds(1)
        };
        Query {
            initial_datetime: new_date,
            first_month: true,
            first_day_of_month: true,
            first_hour: true,
            first_minute: true,
            first_second: true,
            direction: direction
        }
    }

    pub fn year_bound(&self) -> Ordinal {
        // Unlike the other units, years will never wrap around.
        self.initial_datetime.year() as u32
    }

    pub fn month_bound(&mut self) -> Ordinal {
        if self.first_month {
            self.first_month = false;
            return self.initial_datetime.month();
        }
        match &self.direction {
            QueryDirection::Next => Months::inclusive_min(),
            QueryDirection::Previous => Months::inclusive_max()
        }
    }

    pub fn reset_month(&mut self) {
        self.first_month = false;
        self.reset_day_of_month();
    }
    // etc..
}

This still leaves the functions breaking the DRY principle. You could also factor out Query and reimplement the functions with a combinator like so (NB this will currently fail the tests, but I think thats because of nanosecond issues)

    fn next_after<Z>(&self, after: &DateTime<Z>) -> Option<DateTime<Z>>
    where
        Z: TimeZone,
    {
        self.years()
            .iter()
            .skip_while(|year| *year < after.year() as u32)
            .flat_map(|year| {
                iter::repeat(after.with_year(year as i32))
                    .zip(self.months().iter())
            })
            .skip_while(|(date, month)| {
                date.as_ref().unwrap() < after ||
                (date.as_ref().unwrap() == after &&
                *month < after.month())
            })
            .flat_map(|(date, month)| {
                iter::repeat(date.unwrap().with_month(month))
                    .zip(self.days_of_month().iter())
            })
            .skip_while(|(date, day)| {
                date.as_ref().unwrap() < after ||
                (date.as_ref().unwrap() == after &&
                *day < after.day())
            })
            .map(|(date, day)| {
                date.unwrap().with_day(day)
            })
            .filter(|date| {
                self.days_of_week().includes(
                    date.as_ref().unwrap().weekday().number_from_sunday()
                )
            })
            .flat_map(|date| {
                iter::repeat(date).zip(self.hours().iter())
            })
            .skip_while(|(date, hour)| {
                date.as_ref().unwrap() < after ||
                (date.as_ref().unwrap() == after &&
                *hour < after.hour())
            })
            .flat_map(|(date, hour)| {
                iter::repeat(date.unwrap().with_hour(hour)).zip(self.minutes().iter())
            })
            .skip_while(|(date, minute)| {
                date.as_ref().unwrap() < after ||
                (date.as_ref().unwrap() == after &&
                *minute < after.minute())
            })
            .flat_map(|(date, minute)| {
                iter::repeat(date.unwrap().with_minute(minute)).zip(self.seconds().iter())
            })
            .skip_while(|(date, second)| {
                date.as_ref().unwrap() < after ||
                (date.as_ref().unwrap() == after &&
                *second <= after.second())
            })
            .map(|(date, second)| {
                date.unwrap().with_second(second)
            })
            .next()
            .flatten()
    }

This releaves the function of some indentation, and if you take a QueryDirection enum as an argument and slap some match clauses in there, you might be able to merge both functions together.

reject invalid cron values

I can use values greater than 59 for seconds and minutes, and greater than 24 for hours, in a cron expressions, and that gives surprising values without errors.

Example of accepted expression:

0/100 0/100 0/100 * * *

Unusable contructors

I tried to use the contructors from and from_field_list but the structs I need for that are in an private module. Why do these constructors exist? These constructors are also not used in the tests.

Parse step values (common extension that originated in BSD)

https://www.freebsd.org/cgi/man.cgi?crontab%285%29

Step values can be used in conjunction with ranges. Following a range
with /<number> specifies skips of the number's value through the
range. For example, 0-23/2 can be used in the hours field to specify
command execution every other hour (the alternative in the V7 standard is
0,2,4,6,8,10,12,14,16,18,20,22). Steps are also permitted after an
asterisk, so if you want to say ``every two hours'', just use */2.

Split Schedule into source and a ScheduleFields struct. Also remove Option from Schedule::source

It might be interesting to create a ScheduleFields struct like such:

pub struct ScheduleFields {
    years: Years,
    days_of_week: DaysOfWeek,
    months: Months,
    days_of_month: DaysOfMonth,
    hours: Hours,
    minutes: Minutes,
    seconds: Seconds,
}

And change the Schedule field to:

pub struct Schedule {
    source: String,
    fields: ScheduleFields
}

This means that you can change the FromStr implementation for Schedule to:

 fn from_str(expression: &str) -> Result<Self, Self::Err> {
    match schedule(Input(expression)) {
        Ok((_, schedule_fields)) => { 
            Ok(Schedule::new(
                String::from(expression), 
                schedule_fields)
            )
        },  // Extract from nom tuple
        Err(_) => Err(ErrorKind::Expression("Invalid cron expression.".to_owned()).into()), //TODO: Details
    }
}

And because of that, source doesn't need to be an Option anymore.

To prevent any breaking changes you can change the TimeSpec functions in Schedule to

pub fn years(&self) -> &impl TimeUnitSpec {
    &self.fields.years // NB: instead of &self.hours
}
// etc

Lastly If you expose ScheduleFields (and add a getter to Schedule), it can be used for equality. This means that you can use the Schedule::source for a quick equality check of Schedules, and if you have a use case where you need to check for equivalence, you can manually choose to use the more "expensive" ScheduleFields equality check. This might be useful for #61

0.6.1 release

Heya @zslayton is there a chance to get a quick 0.6.1 release with the clone derive? :) pleeeaaaasseee?

v1.0 Metaissue

Items in this list need to be completed before 1.0 is cut, but they will be released incrementally in minor version bumps.

TODO:

  • PartialEq and Eq implementations for Schedule (See #61)

  • Allow users to test whether a given DateTime matches their schedule (See #16)

  • Allow users to iterate over a Schedule backwards, viewing matching historical DateTimes (See #66)

  • Provide a convenience method for iterating over a window of time (schedule.from(time1).to(time2))

  • ScheduleParserBuilder that allows users to specify different configuration options. Options included TBD, but ideas include:

    • 0- vs 1-based indexing for some fields
    • Recognizing a different set of strings for days of the week or months of the year
    • Specifying whether an extra field represents seconds or years (See #13)

    No individual option is required for 1.0, but introducing a ScheduleParserBuilder is. Individual options can be added without it being a breaking change.

  • Proper documentation

  • Re-license to dual MIT/Apache2

Panic on invalid cron expression

The string 1/0 causes a panic when fed to cron::Schedule::from_str (0.11.0):

thread 'testing' panicked at 'assertion failed: step != 0', /rustc/6dd68402c5d7da168f87d8551dd9aed1d8a21893/library/core/src/iter/adapters/step_by.rs:21:9
stack backtrace:
   0: rust_begin_unwind
             at /rustc/6dd68402c5d7da168f87d8551dd9aed1d8a21893/library/std/src/panicking.rs:584:5
   1: core::panicking::panic_fmt
             at /rustc/6dd68402c5d7da168f87d8551dd9aed1d8a21893/library/core/src/panicking.rs:142:14
   2: core::panicking::panic
             at /rustc/6dd68402c5d7da168f87d8551dd9aed1d8a21893/library/core/src/panicking.rs:48:5
   3: core::iter::adapters::step_by::StepBy<I>::new
             at /rustc/6dd68402c5d7da168f87d8551dd9aed1d8a21893/library/core/src/iter/adapters/step_by.rs:21:9
   4: core::iter::traits::iterator::Iterator::step_by
             at /rustc/6dd68402c5d7da168f87d8551dd9aed1d8a21893/library/core/src/iter/traits/iterator.rs:384:9
   5: cron::time_unit::TimeUnitField::ordinals_from_root_specifier
             at /home/timo/.cargo/registry/src/github.com-1ecc6299db9ec823/cron-0.11.0/src/time_unit/mod.rs:310:17
   6: <T as cron::parsing::FromField>::from_field
             at /home/timo/.cargo/registry/src/github.com-1ecc6299db9ec823/cron-0.11.0/src/parsing.rs:60:50

This also happens in earlier versions (I tested 0.6.0), though that requires subexpressions to be present: 1/0 * * * * *

::from_str but no ::to_str

Going from a cron string to a Schedule struct is nice and easy, but why can't you easily go from a Schedule back to its string representation?

Schedule::after doesn't take milliseconds into account

This is a fantastic crate! However, I'm running into some issues because it seems that Schedule::after doesn't take milliseconds into account. This shows up when iterating over the upcoming times in reverse order to get previous times.

let schedule = Schedule::from_str("* * * * * * *").unwrap();
let start_time = Utc.ymd(2022, 1, 1).and_hms_milli(12, 0, 0, 500);
println!("{}", schedule.after(&start_time).rev().next().unwrap());

I would expect this code to print "2022-01-01 12:00:00 UTC", but it prints "2022-01-01 11:59:59 UTC" instead.

Document Weekday Ordinals

Hi,

First of all, thanks for this great crate.

However, the numbering of weekdays seems unusual to me. On the machines I usually work, the crontab uses a 0 for Sunday, 1 for Monday and so on.
This crate seems to use 1 for Sunday and not accept 0.

Maybe this should be documented somewhere.

Upgrade chrono to 0.4

The dependency on 0.3 is causing dependency issues in my app. Can we get this upgraded? I know the implications of upgrading require a rename from UTC to Utc. Not sure of there are any changes required.

0.8.1 release for fixing nightly Rust

Hi!

First of all, thanks for your effort in this crate. We've been using it for a while with great success!

However, since the latest nightly we've been experiencing some problems with it:

error[E0432]: unresolved import `std::collections::Bound`
 --> /opt/app-root/src/.cargo/registry/src/github.com-1ecc6299db9ec823/cron-0.8.0/src/schedule.rs:6:23
  |
6 | use std::collections::Bound::{Included, Unbounded};
  |                       ^^^^^ `Bound` is a type alias, not a module
error: aborting due to previous error

It looks like the deprecated alias has been removed.

We saw that the problem has been fixed by #84 . Would it be possible to make a 0.8.1 release that contains this change?

Thank you!

Some important function not public

Hi bro I'm trying to write a new crate base on your crate cron, but use some function, it's not a success because that not public such as from_str I wanna create a new Struct Schedule from str, but it's just private function I can't use it.

Relicense under dual MIT/Apache-2.0

Why?

The MIT license requires reproducing countless copies of the same copyright
header with different names in the copyright field, for every MIT library in
use. The Apache license does not have this drawback, and has protections from
patent trolls and an explicit contribution licensing clause. However, the
Apache license is incompatible with GPLv2. This is why Rust is dual-licensed as
MIT/Apache (the "primary" license being Apache, MIT only for GPLv2 compat), and
doing so would be wise for this project. This also makes this crate suitable
for inclusion in the Rust standard distribution and other project using dual
MIT/Apache.

How?

To do this, get explicit approval from each contributor of copyrightable work
(as not all contributions qualify for copyright) and then add the following to
your README:

## License

Licensed under either of
 * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
 * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.

### Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you shall be dual licensed as above, without any
additional terms or conditions.

and in your license headers, use the following boilerplate (based on that used in Rust):

// Copyright (c) 2015 t developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

And don't forget to update the license metadata in your Cargo.toml!

Contributor checkoff

Allow for a 'years' field

Partially implemented, needs to play nicely with the seconds field that will be introduced in #5. If 8 fields are present, treat the last field as a years field.

Trying to parse a cron expression for every two minutes fails

extern crate cron;
extern crate chrono;

use cron::Schedule;
use chrono::Utc;
use std::str::FromStr;

fn main() {
// sec min hour day of month month day of week year
let expression = "* 0/2 * * * * *";
let schedule = Schedule::from_str(expression).unwrap();
println!("Upcoming fire times:");
for datetime in schedule.upcoming(Utc).take(10) {
println!("-> {}", datetime);
}
}

produces

-> 2018-01-23 13:08:59 UTC
-> 2018-01-23 13:10:00 UTC
-> 2018-01-23 13:10:01 UTC
-> 2018-01-23 13:10:02 UTC
-> 2018-01-23 13:10:03 UTC
-> 2018-01-23 13:10:04 UTC
-> 2018-01-23 13:10:05 UTC
-> 2018-01-23 13:10:06 UTC
-> 2018-01-23 13:10:07 UTC
-> 2018-01-23 13:10:08 UTC

Panic: Invalid Time

The following app will panic when tests are run. I believe this is an internal error in the cron library that isn't being handled. This is probably related to the fact that the time being used here is the DST change-over time for the target timezone (Europe/London).

I attempted to hack around this in my app by using catch_unwind (https://doc.rust-lang.org/std/panic/fn.catch_unwind.html), but unfortunately chrono Tz objects are not UnwindSafe.

use cron::Schedule;
use chrono::{DateTime};
use chrono::offset::TimeZone;

pub fn currently_in_schedule<Z>(schedules: &[Schedule], now: &DateTime<Z>) -> bool
where
    Z: TimeZone,
{
    for schedule in schedules {
        if let Some(next) = schedule.after(&now).next() {
            let duration = next.clone() - now.clone();
            if duration.num_seconds() <= 1 {
                return true
            }
        }
    }
    false
}

#[cfg(test)]
mod tests {
    // Note this useful idiom: importing names from outer (for mod tests) scope.
    use super::*;
    use std::str::FromStr;
    use chrono::Utc;
    use chrono_tz::Tz;

    #[test]
    fn repro_crash_sre3898() {
        let schedule_tz: Tz = "Europe/London".parse().unwrap();
        let dt = schedule_tz
            .ymd(2019, 10, 27)
            .and_hms(0, 3, 29)
            .checked_add_signed(Duration::hours(1)) // puts it in the middle of the DST transition
            .unwrap();
        let schedule = Schedule::from_str("* * * * * Sat,Sun *").unwrap();
        // true, false, isn't really relevant... not panic'ing is
        assert_eq!(currently_in_schedule(&[schedule], &dt), true);
    }
}

Panic w/ backtrace:

---- schedule::tests::repro_crash_sre3898 stdout ----
thread 'schedule::tests::repro_crash_sre3898' panicked at 'invalid time', src/libcore/option.rs:1166:5
stack backtrace:
   0: backtrace::backtrace::libunwind::trace
             at /Users/vsts/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.34/src/backtrace/libunwind.rs:88
   1: backtrace::backtrace::trace_unsynchronized
             at /Users/vsts/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.34/src/backtrace/mod.rs:66
   2: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:47
   3: std::sys_common::backtrace::print
             at src/libstd/sys_common/backtrace.rs:36
   4: std::panicking::default_hook::{{closure}}
             at src/libstd/panicking.rs:200
   5: std::panicking::default_hook
             at src/libstd/panicking.rs:211
   6: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:477
   7: std::panicking::continue_panic_fmt
             at src/libstd/panicking.rs:384
   8: rust_begin_unwind
             at src/libstd/panicking.rs:311
   9: core::panicking::panic_fmt
             at src/libcore/panicking.rs:85
  10: core::option::expect_failed
             at src/libcore/option.rs:1166
  11: core::option::Option<T>::expect
             at /rustc/625451e376bb2e5283fc4741caa0a3e8a2ca4d54/src/libcore/option.rs:345
  12: chrono::date::Date<Tz>::and_hms
             at /Users/mathieu.fenniak/.cargo/registry/src/github.com-1ecc6299db9ec823/chrono-0.4.7/src/date.rs:78
  13: cron::schedule::Schedule::next_after
             at /Users/mathieu.fenniak/.cargo/registry/src/github.com-1ecc6299db9ec823/cron-0.6.0/src/schedule.rs:214
  14: <cron::schedule::ScheduleIterator<Z> as core::iter::traits::iterator::Iterator>::next
             at /Users/mathieu.fenniak/.cargo/registry/src/github.com-1ecc6299db9ec823/cron-0.6.0/src/schedule.rs:338
  15: schedulevacuum::schedule::currently_in_schedule
             at src/schedule.rs:10
  16: schedulevacuum::schedule::tests::repro_crash_sre3898
             at src/schedule.rs:91
  17: schedulevacuum::schedule::tests::repro_crash_sre3898::{{closure}}
             at src/schedule.rs:79
  18: core::ops::function::FnOnce::call_once
             at /rustc/625451e376bb2e5283fc4741caa0a3e8a2ca4d54/src/libcore/ops/function.rs:235
  19: <alloc::boxed::Box<F> as core::ops::function::FnOnce<A>>::call_once
             at /rustc/625451e376bb2e5283fc4741caa0a3e8a2ca4d54/src/liballoc/boxed.rs:787
  20: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:80
  21: std::panicking::try
             at /rustc/625451e376bb2e5283fc4741caa0a3e8a2ca4d54/src/libstd/panicking.rs:275
  22: std::panic::catch_unwind
             at /rustc/625451e376bb2e5283fc4741caa0a3e8a2ca4d54/src/libstd/panic.rs:394
  23: test::run_test::run_test_inner::{{closure}}
             at src/libtest/lib.rs:1408
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

Make it ready for v0.9.0

Because I fixed two of the metaissues in #67 and I would like to use them easily in my own personal project. I was wondering what could be done to make this project ready for v0.9.0

I was thinking a couple of things:

  • Run a more heftier version of clippy against the codebase.
    • I tried running clippy::pedantic and some parts of clippy::nursery, and even though it did have a couple of false positives it did help me spot a couple of issues. E.g. unneeded borrows, changing .map(f).unwrap_or_else(g) into .map_or_else(g,f), or with ranges: (min..max + 1) into (min..=max)
  • Small refactor
    • Giving Field it's own file and ScheduleFields it's own file. Also changing the location of things like FromString for Schedule to the schedule.rs file.

If there are any other issues that need to be addressed I would love to see it.

Edit:

  • Relicense to dual MIT/Apache-2

Cron reference used ?

Hi,

I see the spec for DaysOfWeek defines the rangs as 1 -> 7. I'm using this project to parse cronfiles on a debian/ubuntu, which uses the range 0 -> 6. I guess this means this project is not based on the same specification ?

  • Maybe it would be interesting to reference what cron specification this project complies with ?
  • Is it imaginable to also support debian/ubuntu's spec ?

ScheduleIterator from `Schedule::after` sometimes skips the first event

extern crate cron;
extern crate chrono;

fn main() {
    use chrono::TimeZone;

    let schedule = "0 0/10 * * * * *".parse::<cron::Schedule>().unwrap();

    let dt = chrono::NaiveDate::from_ymd(2017, 10, 24).and_hms(0, 0, 59);
    let dt = chrono::Utc.from_utc_datetime(&dt);
    println!("{}", schedule.after(&dt).next().unwrap());

    let dt = chrono::NaiveDate::from_ymd(2017, 10, 24).and_hms(0, 1, 0);
    let dt = chrono::Utc.from_utc_datetime(&dt);
    println!("{}", schedule.after(&dt).next().unwrap());
}

Both line should print 2017-10-24 00:10:00 UTC. But I got this:

2017-10-24 00:10:00 UTC
2017-10-24 00:20:00 UTC

Make 'seconds' field optional

The year field is currently optional, with expressions being comprised of six or seven fields accordingly. I would like to also accept expressions which have neither seconds nor years, for a total of five fields. Importantly, expressions that do not include seconds but which do include years would be illegal.

Documentation

  • Need to spell out which cron expression features are supported and which ones (all non-standard) aren't.
  • Need meaningful rustdoc comments on the few public facing types/methods.

Schedule to implement debug

Hello,
First off great library, thanks for all the work you've done.

I am having an issue where I am trying to implement schedule as a custom parser for structopt/clap. Unfortunately it won't let me because schedule needs to implement debug in order to printed by the library. I'm kinda new to rust thought so if there is a good reason that it doesn't implement debug that totally fine.

Noob question: why nightly ? is it a permanent choice ?

Disclaimer: I'm rather new to rust's world. My question might come from a lack of knowledge of how things work.

I'm interested in using this lib is a small pet project. I have been using rust's stable release so far for that project, which means it will fail to compile cron, which requires some nightly (?) features.

Is this permanent, or does cron aim to support rust stable at some point ?

More generally, is the choice to use nightly features guided by some kind of recommendation ? Should I myself follow those ?

Thanks for your help !

Bug in time zone change

Here is a small code to reproduce, note that I dont know precisely if the bug is in this project or chrono_tz...

Cargo.toml:

[dependencies]
cron = "0.9.0"
chrono = "0.4.19"
chrono-tz = "0.5.3"

Code:

extern crate cron;
extern crate chrono;
extern crate chrono_tz;

use cron::Schedule;
use chrono::{ DateTime, TimeZone };
use std::str::FromStr;

pub fn next_ticks<Z>(schedule: &str, from_time: &DateTime<Z>, take: usize) -> Vec<DateTime::<Z>> where Z: TimeZone {
    //               sec  min   hour   day of month   month   day of week   year
    // let expression = "0   30   9,12,15     1,15       May-Aug  Mon,Wed,Fri  2018/2";
    match Schedule::from_str(schedule) {
        Ok(schedule) => schedule.after(from_time).take(take).collect::<Vec<DateTime::<Z>>>(),
        Err(_e) => Vec::new()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use chrono::SecondsFormat;
    use chrono_tz::Europe::Paris;

    #[test]
    fn test_time_zone_change_up() {
        // During the night from the 28 to 29 march 2020, France has changed its time from 2am to 3am
        // So when we reached "2020-03-29T02:00:00 Europe/Paris" we advanced to "2020-03-29T03:00:00 Europe/Paris"
        let results = next_ticks("0 * * * * * *", &Paris.ymd(2020, 3, 29).and_hms(1, 55, 0), 10);
        assert_eq!(results.len(), 10);
        assert_eq!(results[0].to_rfc3339_opts(SecondsFormat::Millis, false), "2020-03-29T01:56:00.000+01:00");
        assert_eq!(results[1].to_rfc3339_opts(SecondsFormat::Millis, false), "2020-03-29T01:57:00.000+01:00");
        assert_eq!(results[2].to_rfc3339_opts(SecondsFormat::Millis, false), "2020-03-29T01:58:00.000+01:00");
        assert_eq!(results[3].to_rfc3339_opts(SecondsFormat::Millis, false), "2020-03-29T01:59:00.000+01:00");
        // Time zone shift here
        assert_eq!(results[4].to_rfc3339_opts(SecondsFormat::Millis, false), "2020-03-29T03:00:00.000+02:00");
        assert_eq!(results[5].to_rfc3339_opts(SecondsFormat::Millis, false), "2020-03-29T03:01:00.000+02:00");
        assert_eq!(results[6].to_rfc3339_opts(SecondsFormat::Millis, false), "2020-03-29T03:02:00.000+02:00");
        assert_eq!(results[7].to_rfc3339_opts(SecondsFormat::Millis, false), "2020-03-29T03:03:00.000+02:00");
        assert_eq!(results[8].to_rfc3339_opts(SecondsFormat::Millis, false), "2020-03-29T03:04:00.000+02:00");
        assert_eq!(results[9].to_rfc3339_opts(SecondsFormat::Millis, false), "2020-03-29T03:05:00.000+02:00");
    }

    #[test]
    fn test_time_zone_change_down() {
        // During the night from the 24 to 25 october 2020, France has changed its time from 3am to 2am
        // So when we reached "2020-10-25T03:00:00 Europe/Paris" we went back to "2020-10-25T02:00:00 Europe/Paris"
        let results = next_ticks("0 * * * * * *", &Paris.ymd(2020, 10, 25).and_hms(2, 55, 0), 1);
        assert_eq!(results.len(), 10);
        assert_eq!(results[0].to_rfc3339_opts(SecondsFormat::Millis, false), "2020-10-25T02:56:00.000+02:00");
    }
}

The first works, the second test_time_zone_change_down panic with "thread 'tests::test_time_zone_change_down' panicked at 'invalid time', /Users/<>/.cargo/registry/src/github.com-1ecc6299db9ec823/chrono-0.4.19/src/date.rs:83:42"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.