{"id":22741741,"url":"https://github.com/crystallabs/virtualdate","last_synced_at":"2026-05-25T09:06:00.894Z","repository":{"id":211812861,"uuid":"729667654","full_name":"crystallabs/virtualdate","owner":"crystallabs","description":"Advanced time, calendar, schedule, and remind library for Crystal","archived":false,"fork":false,"pushed_at":"2023-12-21T08:29:13.000Z","size":200,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-05T06:33:46.764Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Crystal","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/crystallabs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-12-10T00:26:20.000Z","updated_at":"2023-12-11T00:36:35.000Z","dependencies_parsed_at":"2025-02-05T06:29:02.796Z","dependency_job_id":"ac878ee3-3f86-4007-9908-aecfd33552a6","html_url":"https://github.com/crystallabs/virtualdate","commit_stats":null,"previous_names":["crystallabs/virtualdate"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crystallabs%2Fvirtualdate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crystallabs%2Fvirtualdate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crystallabs%2Fvirtualdate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/crystallabs%2Fvirtualdate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/crystallabs","download_url":"https://codeload.github.com/crystallabs/virtualdate/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246273552,"owners_count":20750906,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-11T00:16:42.966Z","updated_at":"2025-12-26T00:37:58.706Z","avatar_url":"https://github.com/crystallabs.png","language":"Crystal","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Linux CI](https://github.com/crystallabs/virtualdate/workflows/Linux%20CI/badge.svg)](https://github.com/crystallabs/virtualdate/actions?query=workflow%3A%22Linux+CI%22+event%3Apush+branch%3Amaster)\n[![Version](https://img.shields.io/github/tag/crystallabs/virtualdate.svg?maxAge=360)](https://github.com/crystallabs/virtualdate/releases/latest)\n[![License](https://img.shields.io/github/license/crystallabs/virtualdate.svg)](https://github.com/crystallabs/virtualdate/blob/master/LICENSE)\n\n## Installation\n\nAdd the following to your application's \"shard.yml\":\n\n```\n dependencies:\n   virtualdate:\n     github: crystallabs/virtualdate\n     version: ~\u003e 1.0\n```\n\nAnd run `shards install` or just `shards`.\n\n## Introduction\n\nVirtualDate is a companion project to [virtualtime](https://github.com/crystallabs/virtualtime).\n\nVirtualDate implements the high-level part, the actual items one might want to schedule, with\nadditional options and fields, and with the support for complex and flexible, and often recurring,\ntime/event scheduling.\n\nThe class is intentionally called `VirtualDate` not to imply a particular type or purpose\n(i.e. it can be a task, event, recurring appointment, reminder, etc.)\n\nLikewise, it does not contain any task/event-specific properties -- it only concerns itself with\nthe scheduling aspect.\n\n## Usage\n\nComments in the code hopefully show how to use it:\n\n```crystal\nvd = VirtualDate.new\n\n# Create a VirtualTime that matches every other day from Mar 10 to Mar 20:\nmarch = VirtualTime.new\nmarch.month = 3\nmarch.day = (10..20).step 2\n\n# Add this VirtualTime as a due date to our VirtualDate:\nvd.due \u003c\u003c march\n\n# Create a VirtualTime that matches Mar 20 specifically, and omit the event\n# on that particular day:\nmarch_20 = VirtualTime.new\nmarch_20.month = 3\nmarch_20.day = 20\nvd.omit \u003c\u003c march_20\n\n# If event falls on an omitted date, try rescheduling it for 2 days later:\nvd.shift = 2.days\n```\n\nNow we can check when the VD is due and when it is not (ignore the `Time[]` syntax):\n\n```crystal\n# VirtualDate is not due on Feb 15, 2017 because that's not in March:\np vd.on?( Time[\"2017-02-15\"]) # ==\u003e false\n\n# VirtualDate is not due on Mar 15, 2017 because that's not a day of\n# March 10, 12, 14, 16, 18, or 20:\np vd.on?( Time[\"2017-03-15\"]) # ==\u003e false\n\n# VirtualDate is due on Mar 16, 2017:\np vd.on?( Time[\"2017-03-16\"]) # ==\u003e true\n\n# VirtualDate is due on Mar 18, 2017:\np vd.on?( Time[\"2017-03-18\"]) # ==\u003e true\n\n# And it is due on any Mar 18, doesn't need to be in 2017:\np vd.on?( Time[\"2023-03-18\"]) # ==\u003e true\n\n# But it is not due on Mar 20, 2017, because that date is omitted, and the system will give us\n# a span of time (offset) when it can be scheduled. Based on our reschedule settings above, this\n# will be a span for 2 days later.\np vd.on?( Time[\"2017-03-20\"]) # ==\u003e #\u003cTime::Span @span=2.00:00:00\u003e\n\n# Asking whether the VD is due on the rescheduled date (Mar 22) will tell us no, because currently\n# rescheduled dates are not counted as due/on dates:\np vd.on?( Time[\"2017-03-22\"]) # ==\u003e nil\n```\n\nHere's another example of a VirtualDate that is due on every other day in March, but if it falls\non a weekend it is ignored:\n\n```crystal\nvd = VirtualDate.new\n\n# Create a VirtualTime that matches every other (every even) day in March:\nmarch = VirtualTime.new\nmarch.month = 3\nmarch.day = (2..31).step 2\nvd.due \u003c\u003c march\n\n# But on weekends it should not be scheduled:\nweekend = VirtualTime.new\nweekend.day_of_week = [6,7]\nvd.omit \u003c\u003c weekend\n\n# If item falls on an omitted day, consider it as not scheduled (don't\n# try rescheduling):\nvd.shift = nil # or 'false' to explicitly say it's omitted; false is the default value\n\n# Now let's check when it is due and when not in March:\n# (Do this by printing a list for days 1 - 31):\n(1..31).each do |d|\n  p \"Mar-#{d} = #{vd.on?( Time.local(2023, 3, d)}\"\nend\n```\n\n## More Information\n\nFor a realistic, schedulable item it is not enough to have just one `VirtualTime` that controls\nwhen that item is active/scheduled (or simply \"on\" in VT's terminology).\n\nAt a minimum, you might want to specify multiple `VirtualTimes` at which the item is on, and\nspecify an omit list when an item should not be on (e.g. on weekends or public holidays).\n\nAlso, if an item would fall on an omitted date or time, then it might be desired to automatically\nreschedule it by shifting it by certain amount of time before or after the original time.\n\nThus, altogether class `VirtualDate` has the following properties:\n\n- `begin`, an absolute start time, before which the VirtualDate is never on\n- `end`, an absolute end time, after which the VirtualDate is never on\n- `due`, a list of VirtualTimes on which the VirtualDate is on\n- `omit`, a list of VirtualTimes on which the VirtualDate is omitted (not on)\n- `shift`, governing whether, and by how much time, the VirtualDate should be shifted if it falls on an omitted date/time\n- `max_shifts`, a maximum number of shift attempts to make in an attempt to find a suitable rescheduled date and time\n- `max_shift`, a maximum Time::Span by which the VirtualDate can be shifted before being considered unschedulable\n- `on`, a property which overrides all other VirtualDate's fields and calculations and directly sets VirtualDate's `on` status\n- `duration`\n- `flags`, i.e. categories/groups/tags\n- `parallel`, how many vdates from the same `flag` group can be scheduled in parallel\n- `stagger`, if scheduling in parallel, by how much time to stagger/sequence/order each parallel vdate\n- `priority`, higher = schedule first\n- `fixed`, whether this vdate is fixed/immovable\n- `id`, unique ID (string)\n- `depends_on`, list of vdates it depends on\n- `deadline`, will fail to schedule if it can't complete before this\n\nIf the 's list of due dates is empty, it is considered as always \"on\".\nIf the item's list of omit dates is empty, it is considered as never omitted.\n\nA value of `shift` can be nil, `Boolean`, or`Time::Span`.\n\n- Nil instructs that event should not be rescheduled, and to simply treat it as not scheduled on a particular date\n- A `Boolean` explicitly marks the item as scheduled or rejected when it falls on an omitted time\n- A `Time::Span` implies that rescheduling should be attempted and controls by how much time the item should be shifted (into the past or future) on every attempt\n\nIf there are multiple `VirtualTime`s set for a field, e.g. for `due` date, the matches are logically OR-ed;\none match is enough for the field to match.\n\n### Start and End Dates\n\nIn addition to absolute `Time` values, `start` and `end` can be `VirtualTime`s. When they are set to those types,\nthey don't act like usual to confine VD's scheduling to the from-to period, but the actual times asked must\n`match?` both of them (if they are specified) in the usual, VT's sense.\n\nIn other words, to have a VD active during say, summer months, you could define `start = VirtualTime.new month: 4..10`,\nand would not need any `stop` value.\n\nThis functionality is quite redundant with the usual `due` and `omit` dates, is highly experimental,\nand probably not something to be used.\n\n## Scheduling\n\nThere is support for loading/saving schedule to YAML and exporting to iCal.\n\n\n# Other Projects\n\nList of interesting or similar projects in no particular order:\n\n- https://dianne.skoll.ca/projects/remind/ - a sophisticated calendar and alarm program\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrystallabs%2Fvirtualdate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcrystallabs%2Fvirtualdate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcrystallabs%2Fvirtualdate/lists"}