# Cron library for Zig

**URL:** <https://ziggit.dev/t/cron-library-for-zig/777>\
**Category:** Showcase\
**Created:** [June 18, 2023, 1:39am UTC](https://ziggit.dev/t/cron-library-for-zig/777 "2023-06-18T01:39:52Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Hanaasagi](https://ziggit.dev/user_avatar/ziggit.dev/hanaasagi/32/448_2.png) [@Hanaasagi](https://ziggit.dev/u/Hanaasagi)\
**Post date:** [June 18, 2023, 1:39am UTC](https://ziggit.dev/t/cron-library-for-zig/777/1 "2023-06-18T01:39:53Z")

</div>

> **[GitHub - dying-will-bullet/cron: Cron library for Zig](https://github.com/dying-will-bullet/cron)**
>
> Cron library for Zig

Cron is a parser library for crontab schedule entries and determining the next/prev execution time. The basic functionality has been completed, and a fuzz test is scheduled to run daily on GitHub Actions to verify its correctness.

### Basic API

```zig
// create a cron object
var c = Cron.init();
// parse cron entry
try c.parse("5 4 * * *");
// Get next execution time from `now`
const next = c.next(now);

```

### Simple Scheduler Example

```zig
const std = @import("std");
const Cron = @import("cron").Cron;
const datetime = @import("datetime").datetime;

fn job1(i: usize) !void {
    const now = datetime.Datetime.now();

    var buf: [64]u8 = undefined;
    const dt_str = try now.formatISO8601Buf(&buf, false);
    std.log.info("{s} {d}th execution", .{ dt_str, i });
}

pub fn main() !void {
    var c = Cron.init();
    // At every minute.
    try c.parse("*/1 * * * *");

    for (0..5) |i| {
        const now = datetime.Datetime.now();

        // Get the next run time
        const next_dt = try c.next(now);
        const duration = next_dt.sub(now);
        // convert to nanoseconds
        const nanos = duration.totalSeconds() * std.time.ns_per_s + duration.nanoseconds;

        // wait next
        std.time.sleep(@intCast(u64, nanos));

        try job1(i + 1);
    }
}

```

Currently, it depends on [zig-datetime](https://github.com/frmdstryr/zig-datetime) to handle time. I’m considering exposing a set of interfaces with timestamp parameters. Feedback is welcome.

---

<div class="post-metadata">

**Author:** ![AndrewCodeDev](https://ziggit.dev/letter_avatar_proxy/v4/letter/a/278dde/32.png) [@AndrewCodeDev](https://ziggit.dev/u/AndrewCodeDev)\
**Post date:** [June 18, 2023, 7:41pm UTC](https://ziggit.dev/t/cron-library-for-zig/777/2 "2023-06-18T19:41:03Z")

</div>

Cool! Thanks for sharing your work!

Have you considered moving the parse function’s string argument to comptime? I’m writing a comptime parser for strings to do einsum tensor operations and Zig has really made that easy to do.

Do you see most arguments being passed to the parse function as being something specified by the user directly or as something that can only be runtime determined? I don’t know enough about your design to be able to say from this vantage point.

---

<div class="post-metadata">

**Author:** ![Hanaasagi](https://ziggit.dev/user_avatar/ziggit.dev/hanaasagi/32/448_2.png) [@Hanaasagi](https://ziggit.dev/u/Hanaasagi)\
**Post date:** [June 19, 2023, 3:43am UTC](https://ziggit.dev/t/cron-library-for-zig/777/3 "2023-06-19T03:43:51Z")

</div>

In some scenarios, parameters can be determined directly at compile-time. However, in other scenarios, such as reading a file like “/etc/crontab” at runtime, it depends on how the user chooses.

---

<div class="post-metadata">

**Author:** ![AndrewCodeDev](https://ziggit.dev/letter_avatar_proxy/v4/letter/a/278dde/32.png) [@AndrewCodeDev](https://ziggit.dev/u/AndrewCodeDev)\
**Post date:** [June 19, 2023, 5:25am UTC](https://ziggit.dev/t/cron-library-for-zig/777/4 "2023-06-19T05:25:47Z")

</div>

Okay! So I see that this can have some file system integration then (makes sense now that you’ve said that).

So help me understand the structure of your project a bit more. I read the lib file and it seems to me that the lib file is currently for running tests (such as “test normal”, etc). This may be nitpicking, but I tend to think that lib (as it’s normally defined) is where implementations are either dynamically or statically linked in. Your cron.zig file is where the main cron struct is found but it does not depend at all on lib as a file - currently the import structure is:

```zig
const std = @import("std");
const log = std.log;
const datetime = @import("datetime").datetime;
const ext = @import("./datetime.zig");

const Error = @import("./error.zig").Error;
const isDigit = @import("./utils.zig").isDigit;
const CronExpr = @import("./expr.zig").CronExpr;
const CronField = @import("./expr.zig").CronField;
const FieldTag = @import("./expr.zig").FieldTag;

```

I would try to place those tests closer to their implementation files. The reason is for cascading tests. If I test a zig file itself, it will then test all of the dependent files upstream. Since lib is not upstream of cron.zig, it would seem like those tests need to be run independently (by testing the lib file alone). Thus, lib needs to be run independently of the main file in the current state? Any reason for that?

---

<div class="post-metadata">

**Author:** ![Hanaasagi](https://ziggit.dev/user_avatar/ziggit.dev/hanaasagi/32/448_2.png) [@Hanaasagi](https://ziggit.dev/u/Hanaasagi)\
**Post date:** [June 20, 2023, 1:27am UTC](https://ziggit.dev/t/cron-library-for-zig/777/5 "2023-06-20T01:27:51Z")

</div>

> [@AndrewCodeDev](#):
>
> I would try to place those tests closer to their implementation files.

Yes, you are correct. Tests should be placed closer to their implementations to improve clarity. The reason I structured it this way is because the `build.zig` generated by `zig init-lib` uses `lib.zig` as the root file.

```zig
    // Creates a step for unit testing. This only builds the test executable
    // but does not run it.
    const main_tests = b.addTest(.{
        .root_source_file = .{ .path = "src/lib.zig" },
        .target = target,
        .optimize = optimize,
    });

    const run_main_tests = b.addRunArtifact(main_tests);

    // This creates a build step. It will be visible in the `zig build --help` menu,
    // and can be selected like this: `zig build test`
    // This will evaluate the `test` step rather than the default, which is "install".
    const test_step = b.step("test", "Run library tests");
    test_step.dependOn(&run_main_tests.step);

```

It seems that I should follow the approach used in `std.zig` by adding `testing.refAllDecls(@This());` in `lib.zig`, and then move the tests to separate modules.

> <https://github.com/ziglang/zig/blob/0f5aff34414bcb024443540fe905039f3783803a/lib/std/std.zig#L211-L213>
