Code Monkey home page Code Monkey logo

ccronexpr's Introduction

Project archived

This project is no longer maintained, see its updated and extended fork in exander77/supertinycron repo.

Cron expression parsing in ANSI C

travis appveyor

Given a cron expression and a date, you can get the next date which satisfies the cron expression.

Supports cron expressions with seconds field. Based on implementation of CronSequenceGenerator from Spring Framework.

Compiles and should work on Linux (GCC/Clang), Mac OS (Clang), Windows (MSVC), Android NDK, iOS and possibly on other platforms with time.h support.

Supports compilation in C (89) and in C++ modes.

Usage example

#include "ccronexpr.h"

cron_expr expr;
const char* err = NULL;
memset(&expr, 0, sizeof(expr));
cron_parse_expr("0 */2 1-4 * * *", &expr, &err);
if (err) ... /* invalid expression */
time_t cur = time(NULL);
time_t next = cron_next(&expr, cur);

Compilation and tests run examples

gcc ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c89 -DCRON_TEST_MALLOC -o a.out && ./a.out
g++ ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c++11 -DCRON_TEST_MALLOC -o a.out && ./a.out
g++ ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c++11 -DCRON_TEST_MALLOC -DCRON_COMPILE_AS_CXX -o a.out && ./a.out

clang ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c89 -DCRON_TEST_MALLOC -o a.out && ./a.out
clang++ ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c++11 -DCRON_TEST_MALLOC -o a.out && ./a.out
clang++ ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c++11 -DCRON_TEST_MALLOC -DCRON_COMPILE_AS_CXX -o a.out && ./a.out

cl ccronexpr.c ccronexpr_test.c /W4 /D_CRT_SECURE_NO_WARNINGS && ccronexpr.exe

Examples of supported expressions

Expression, input date, next date:

"*/15 * 1-4 * * *",  "2012-07-01_09:53:50", "2012-07-02_01:00:00"
"0 */2 1-4 * * *",   "2012-07-01_09:00:00", "2012-07-02_01:00:00"
"0 0 7 ? * MON-FRI", "2009-09-26_00:42:55", "2009-09-28_07:00:00"
"0 30 23 30 1/3 ?",  "2011-04-30_23:30:00", "2011-07-30_23:30:00"

See more examples in tests.

Timezones

This implementation does not support explicit timezones handling. By default all dates are processed as UTC (GMT) dates without timezone infomation.

To use local dates (current system timezone) instead of GMT compile with -DCRON_USE_LOCAL_TIME, example:

gcc -DCRON_USE_LOCAL_TIME ccronexpr.c ccronexpr_test.c -I. -Wall -Wextra -std=c89 -DCRON_TEST_MALLOC -o a.out && TZ="America/Toronto" ./a.out

License information

This project is released under the Apache License 2.0.

Changelog

2019-03-27

  • CRON_USE_LOCAL_TIME usage fixes

2018-05-23

  • merged #8
  • merged #9
  • minor cleanups

2018-01-27

  • merged #6
  • updated license file (to the one parse-able by github)

2017-09-24

  • merged #4

2016-06-17

  • use thread-safe versions of gmtime and localtime

2015-02-28

  • initial public version

ccronexpr's People

Contributors

akapust1n avatar alfred-ai avatar denispryt avatar geoffrey-vl avatar jwoodrich avatar msantos avatar remittor avatar sstorm avatar staticlibs avatar thomte 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  avatar  avatar  avatar  avatar  avatar  avatar

ccronexpr's Issues

Use bit fields instead of chars for parsed cron_expr

Hey,
I like the lib but it's a bit useless for Embedded systems due to the amount of memory it is using. A parsed CRON expression has ~8 times more bit's than it needs. You could just store the "flags" in bits instead of chars to dramatically reduce memory consumption.

I'm also looking for alternatives. If I do not find anything better I might also start to refactor it.

cron_next returns past date on DST summer day

I have set CRON_USE_LOCAL_TIME to use local time.
I have set the POSIX string to simulate a summer time shift: today is Friday(5) August (8) and the POSIX string to have a time shift next night 'CET-1CEST,M8.1.6,M12.5.0/3' (check specifications here: https://www.ibm.com/developerworks/aix/library/au-aix-posix/index.html).
My cron job expression is the following, aiming to set up a job every day at 00h05: '0 0 5 */1 * *'.
The cron_next function fails and returns a date in the past (1 day earlier).
In general, I have found that during 24h (at least) close the DST time shift, the cron_next returns an earlier date.
The issue is only seen on forward time shift (summer) and not on backward time shift (winter).
Has anyone experienced this?

Arduino support

Hi there

thanks for this work!
It would be great to add support for SAMD21 arduino family, like mkr nb 1500, mkr GSM 1400, mkr wifi 1010 and so on..

In my opinion the following change would be enough:

#elif defined(ESP8266) || defined(ESP_PLATFORM) || defined(TARGET_LIKE_MBED)

becomes

#elif defined(ESP8266) || defined(ESP_PLATFORM) || defined(TARGET_LIKE_MBED) || defined(__SAMD21G18A__)

I'm not sure if a change if also required here

#if !defined(_WIN32) && !defined(__AVR__) && !defined(ESP8266) && !defined(ESP_PLATFORM) && !defined(ANDROID) && !defined(TARGET_LIKE_MBED)

but leaving that line as-is in my case seems to work.

If this if fine for you I'll be happy to submit a PR.

Thanks again!

isupper() and isspace() calls does not conform to some ctypes.h macro implementation

Hello,

We are trying to use your library in an embedded system with a really strict compile environment. We noticed that in lines 491 (isupper) , 580 (isspace), 606 (isspace) you make such calls with a char subscript. However some compile options (-Werror=char-subscripts) and some libraries enforce int subscripts and will compile fail with error: array subscript has type char.

The fix is simple use an int variable for subscript or at least cast it to int like:

isspace(((int)str[i])

or

int c = str[i];
isspace(c);

Regards,

DM

Memory leak when calling cron_next

At this moment I really have no idea where it could be.

I have narrowed it down to calling the cron_next. The used cron_expr is every time the same.

I'm running this code on an ESP8266, using the esp8266/Arduino platform (2.6.3)

The amount the free heap decreases is depending on the expression complexity when calling cron_next.

For example

  • * * * * * * decreases the heap by 64 bytes.
  • */2 * * * * * decreases the heap by 80 bytes.

Sometimes it is also decreased by 112 bytes.

Example of log output running the code below:

60613 : Info  : Cron: Free Heap Decreased: 80 (12920 -> 12840
61256 : Info  : Cron: Free Heap Decreased: 64 (12720 -> 12656
61258 : Info  : Cron: Free Heap Decreased: 64 (12656 -> 12592
61286 : Info  : EVENT: Cron#bla
62257 : Info  : Cron: Free Heap Decreased: 64 (12936 -> 12872
62271 : Info  : EVENT: Cron#bla
63260 : Info  : Cron: Free Heap Decreased: 64 (12872 -> 12808
63293 : Info  : EVENT: Cron#bla
64257 : Info  : Cron: Free Heap Decreased: 64 (12808 -> 12744
64270 : Info  : EVENT: Cron#bla
65281 : Info  : Cron: Free Heap Decreased: 64 (11272 -> 11208
66258 : Info  : Cron: Free Heap Decreased: 64 (12080 -> 12016
66671 : Info  : EVENT: Cron#bla
66771 : Info  : EVENT: Cron#bla
67258 : Info  : Cron: Free Heap Decreased: 64 (12672 -> 12608
67272 : Info  : EVENT: Cron#bla
68259 : Info  : Cron: Free Heap Decreased: 64 (12608 -> 12544
68272 : Info  : EVENT: Cron#bla
69258 : Info  : Cron: Free Heap Decreased: 64 (12544 -> 12480
69272 : Info  : EVENT: Cron#bla
70260 : Info  : Cron: Free Heap Decreased: 64 (12408 -> 12344
70787 : Info  : EVENT: Cron#bla
71256 : Info  : Cron: Free Heap Decreased: 64 (12072 -> 12008
71292 : Info  : EVENT: Cron#bla
72257 : Info  : Cron: Free Heap Decreased: 64 (12352 -> 12288
72287 : Info  : EVENT: Cron#bla
73257 : Info  : Cron: Free Heap Decreased: 64 (12288 -> 12224
73286 : Info  : EVENT: Cron#bla
74256 : Info  : Cron: Free Heap Decreased: 64 (12224 -> 12160
74287 : Info  : EVENT: Cron#bla
75282 : Info  : Cron: Free Heap Decreased: 64 (9896 -> 9832
76257 : Info  : Cron: Free Heap Decreased: 64 (10840 -> 10776
76489 : Info  : EVENT: Cron#bla
76790 : Info  : EVENT: Cron#bla
77257 : Info  : Cron: Free Heap Decreased: 64 (12032 -> 11968
77290 : Info  : EVENT: Cron#bla
78257 : Info  : Cron: Free Heap Decreased: 64 (12160 -> 12096
78291 : Info  : EVENT: Cron#bla
79257 : Info  : Cron: Free Heap Decreased: 64 (12096 -> 12032
79289 : Info  : EVENT: Cron#bla
80256 : Info  : Cron: Free Heap Decreased: 64 (11576 -> 11512
80490 : Info  : EVENT: Cron#bla
81256 : Info  : Cron: Free Heap Decreased: 64 (11624 -> 11560
81290 : Info  : EVENT: Cron#bla
82257 : Info  : Cron: Free Heap Decreased: 64 (11904 -> 11840
82290 : Info  : EVENT: Cron#bla
83256 : Info  : Cron: Free Heap Decreased: 64 (11840 -> 11776
83290 : Info  : EVENT: Cron#bla
84256 : Info  : Cron: Free Heap Decreased: 64 (11776 -> 11712
84290 : Info  : EVENT: Cron#bla
85259 : Info  : Cron: Free Heap Decreased: 64 (11344 -> 11280
86190 : Info  : EVENT: Cron#bla
86257 : Info  : Cron: Free Heap Decreased: 64 (11224 -> 11160
86290 : Info  : EVENT: Cron#bla
87257 : Info  : Cron: Free Heap Decreased: 64 (11584 -> 11520
87290 : Info  : EVENT: Cron#bla
88257 : Info  : Cron: Free Heap Decreased: 64 (11184 -> 11120
88290 : Info  : EVENT: Cron#bla
89257 : Info  : Cron: Free Heap Decreased: 64 (11456 -> 11392
89290 : Info  : EVENT: Cron#bla
90306 : Info  : Cron: Free Heap Decreased: 64 (10936 -> 10872
90490 : Info  : EVENT: Cron#bla
91257 : Info  : Cron: Free Heap Decreased: 64 (10984 -> 10920
91290 : Info  : EVENT: Cron#bla
92259 : Info  : Cron: Free Heap Decreased: 64 (11264 -> 11200
92291 : Info  : EVENT: Cron#bla
93259 : Info  : Cron: Free Heap Decreased: 64 (11200 -> 11136
93292 : Info  : EVENT: Cron#bla
94258 : Info  : Cron: Free Heap Decreased: 64 (11136 -> 11072
94291 : Info  : EVENT: Cron#bla
95257 : Info  : Cron: Free Heap Decreased: 64 (10704 -> 10640
96190 : Info  : EVENT: Cron#bla
96257 : Info  : Cron: Free Heap Decreased: 64 (10584 -> 10520
96290 : Info  : EVENT: Cron#bla
97257 : Info  : Cron: Free Heap Decreased: 64 (10944 -> 10880
97289 : Info  : EVENT: Cron#bla
98257 : Info  : Cron: Free Heap Decreased: 64 (10808 -> 10744
98290 : Info  : EVENT: Cron#bla
99257 : Info  : Cron: Free Heap Decreased: 64 (10544 -> 10480
99293 : Info  : EVENT: Cron#bla

The ESP node is running a lot more, so that's why it sometimes does show a slight increase in free memory.
But as can be seen in the code segment below, the sampled free memory is just wrapping the call to cron_next.

Part of my code calling the function and logging the heap decrease:

    int32_t freeHeapStart = ESP.getFreeHeap();

    time_t res = P081_data->get_cron_next(last);

    int32_t freeHeapEnd = ESP.getFreeHeap();

    if (freeHeapEnd < freeHeapStart) {
      String log = F("Cron: Free Heap Decreased: ");
      log += String(freeHeapStart - freeHeapEnd);
      log += F(" (");
      log += freeHeapStart;
      log += F(" -> ");
      log += freeHeapEnd;
      addLog(LOG_LEVEL_INFO, log);
    }

The object wrapper I made to keep the expression and the calls to this library all in one place:

struct P081_data_struct : public PluginTaskData_base {
  P081_data_struct(const String& expression)
  {
    const char *error;

    memset(&_expr, 0, sizeof(_expr));
    cron_parse_expr(expression.c_str(), &_expr, &error);

    if (!error) {
      _initialized = true;
    } else {
      _error = String(error);
    }
  }

  ~P081_data_struct() {}

  bool isInitialized() {
    return _initialized;
  }

  bool hasError(String& error) {
    if (_initialized) { return false; }
    error = _error;
    return true;
  }

  time_t get_cron_next(time_t date) {
    if (!_initialized) { return CRON_INVALID_INSTANT; }
    return cron_next((cron_expr *)&_expr, date);
  }

  time_t get_cron_prev(time_t date) {
    if (!_initialized) { return CRON_INVALID_INSTANT; }
    return cron_prev((cron_expr *)&_expr, date);
  }

private:

  String    _error;
  cron_expr _expr;
  bool      _initialized = false;
};

Like I said, I really have not a clue where this memory leak should come from. It could also be something specific to esp8266/Arduino or even that I'm doing things completely wrong.

Issue occurs in final month

Issue occurs in final month

Example:
Expression: " 0 0 17 16 * * "
image

Example correct result:
Expression: " 0 0 17 16 * * "
Current time: 2023-12-16 17:00:00

New tests fail around year parsing

I was having some issues on my CentOS 7 server, so I added 2 new tests to ccronexp_test.c.

check_next("0 */2 * * * *",     "2012-07-01_09:00:00", "2012-07-01_09:02:00");
check_next("0 */2 * * * *",     "2018-09-14_14:24:00", "2018-09-14_14:26:00");

The first one passes, the second one does not.
Program output:

Initial: 2018-09-14_14:24:00
Expected: 2018-09-14_14:26:00
Actual: 2008-09-14_14:26:00
ccrontest: ccronexpr_test.c:220: check_next: Assertion `0' failed.

Another that does not pass is if I alter the first one to have a year of 2013 instead of 2012.

Initial: 2013-07-01_09:00:00
Expected: 2013-07-01_09:02:00
Actual: 1900-07-01_09:02:00
ccrontest: ccronexpr_test.c:220: check_next: Assertion `0' failed.

cron_prev 29-feb infinite loop

cron_prev function infinite loop on 29 February. Issue occurs in non leap years.

Example infinite loop:
Expression: "* * * 29 2 *"
Current time: 2021-12-7 12:00:00

Example correct result:
Expression: "* * * 29 2 *"
Current time: 2020-12-7 12:00:00

Compile with mingw32

Hi! If to use mingw32, then a error:
ccronexpr.cpp:83: error: undefined reference to `_mkgmtime'
Maybe can use this for UTC time?
#ifdef _WIN32 time_t cron_mktime(struct tm* tm) { return mktime(tm) - timezone; }

next day error

the next day of expression "0 15 10 ? * 6#3" is error

[suggestion] Add compile for AVR and ESP8266 and organize conditional compilation

Dear Alex,
Thank you for programming this library.
I just created a pull request to allow compilation for AVR and ESP8266 (#24).
The code for the time conversion functions has become a bit messy.
Do you think it would be sensible to refactor it to a series of preprocessor directives in order explicitly stating each platform and a final default one?

#if !defined(_WIN32) && !defined(__AVR__) && !defined(ESP8266) && !defined(ANDROID)
/* forward declaration for platforms that may need it */
/* can be hidden in time.h */
time_t timegm(struct tm* __tp);
#endif

time_t cron_mktime(struct tm* tm) {
#if defined(_WIN32)
    return _mkgmtime(tm);
#elif defined(__AVR__)
    return mk_gmtime(tm);
#elif defined(ESP8266)
    /*code...*/
#elif defined(ANDROID)
    /*code...*/
#else
    return timegm(tm);
#endif
}

... and equivalent structured code for cron_time() and for the same two functions in the case of local time.

Invalid match

Hello,

i've found a potential bug in the cron implementation. The following test demonstrates the issue:

 // Asserts: Expected next match: "2022-11-01_00:00:00", actual next match: "2022-12-01_00:00:00"
check_next("0 0 0 ? 11-12 *",  "2022-10-31_00:00:00", "2022-11-01_00:00:00");

// Works: Expected and actual next match: "2022-11-01_00:00:00"
check_next("0 0 0 ? 11-12 *",  "2022-10-30_00:00:00", "2022-11-01_00:00:00");

Can anybody reproduce the issue?
What could be the reason for the invalid next match?

Remove warning

Can we just add a simple #ifdef in order to remove a warning such as:

#ifndef _WIN32
struct tm *gmtime_r(const time_t *timep, struct tm *result);
#ifdef CRON_USE_LOCAL_TIME    <-------- THIS ONE
struct tm *localtime_r(const time_t *timep, struct tm *result);
#endif
#endif

I'm compiling it using SystemWorkbench for STM32
Thank you

compile with __LP64__ got an error

[2021-06-07 15:18:00.994] In file included from jni/../../../ccronexpr.h:37:
909[2021-06-07 15:18:00.994] D:/android-ndk/android-ndk-r20b/build//../toolchains/llvm/prebuilt/windows-x86_64/sysroot/usr/include\time64.h:36:2: error: Your time_t is already 64-bit.
910[2021-06-07 15:18:00.994] #error Your time_t is already 64-bit.
911[2021-06-07 15:18:00.994] ^
912[2021-06-07 15:18:00.994] jni/../../../ccronexpr.cpp:133:5: error: unknown type name 'time64_t'
913[2021-06-07 15:18:00.994] time64_t result = timegm64(tm);
914[2021-06-07 15:18:00.994] ^
915[2021-06-07 15:18:00.994] jni/../../../ccronexpr.cpp:133:23: error: use of undeclared identifier 'timegm64'
916[2021-06-07 15:18:00.994] time64_t result = timegm64(tm);
917[2021-06-07 15:18:00.994] ^
918[2021-06-07 15:18:00.994] jni/../../../ccronexpr.cpp:153:12: error: use of undeclared identifier 'gmtime_r'
919[2021-06-07 15:18:00.994] return gmtime_r(date, out);
920[2021-06-07 15:18:00.994] ^
921[2021-06-07 15:18:00.994] jni/../../../ccronexpr.cpp:158:7: error: member access into incomplete type 'struct tm'
922[2021-06-07 15:18:00.994] tm->tm_isdst = -1;
923[2021-06-07 15:18:00.994] ^
924[2021-06-07 15:18:00.994] jni/../../../ccronexpr.cpp:102:30: note: forward declaration of 'bd_baselib::tm'
925[2021-06-07 15:18:00.994] time_t cron_mktime_gm(struct tm *tm) {
926[2021-06-07 15:18:00.994] ^
927[2021-06-07 15:18:00.994] jni/../../../ccronexpr.cpp:159:12: error: use of undeclared identifier 'mktime'; did you mean 'mktemp'?
928[2021-06-07 15:18:00.994] return mktime(tm);
929[2021-06-07 15:18:00.994] ^~~~~~
930[2021-06-07 15:18:00.994] mktemp

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.