Dates and Ranges
Markwhen supports a variety of date formats and mechanisms for expressing periods of time.
When parsing, EDTF takes precedence over other date formats mentioned here. So if there is some ambiguity in how a date range is expressed, and it fits the EDTF range format, it will be parsed as EDTF.
Every event has an associated date range, whether it has an explicitly written end date or not. A date range is a period from one date to another.
EDTF Date Ranges
Markwhen is currently level 0 EDTF compliant, supporting ranges such as:
1964/2008
2004-06 / 2006-08
2004-02-01/ 2005-02-08
2004-02-01 /2005-02
2004-02-01/2005
2005/2006-02
2005/now
2018/6 months
Open-ended ranges are not supported.
Ranges start and end with either a EDTF Date or Relative Date or the special keyword now
.
EDTF Date
Essentially the first part of a full ISO8601 date, whose regex could be expressed as \d{4}(-\d{2}(-\d{2})?)?
:
1981
2012-05
2022-01-30
Non-EDTF Date Ranges
Many other expressions of dates are supported by markwhen, not just EDTF. A date range is typically Date[-Date]
; that is, one date optionally followed by a dash (-
) or the word to
and another date.
If an end date is not specified, the range is as long as its granularity. For example, the event
2001: A Space Odyssey
starts January 1, 2001, and lasts through December 31, 2001.
Example | Inferred Range | Explanation |
---|---|---|
2024 | 2024-01-01T00:00:00Z to 2025-01-01T00:00:00Z | From the start of 2024 to the end of 2024 |
04/1776 | 1776-04-01T00:00:00Z to 1776-05-01T00:00:00Z | From the start of April 1776 to the end of April 1776 |
01/01/2024 | 2024-01-01T00:00:00Z to 2024-01-02T00:00:00Z | From the start of January 1, 2024, to the end of January 1, 2024 (the whole day). |
11/11/2024-12/12/2024 | 2024-11-11T00:00:00Z to 2024-12-13T00:00:00Z | From the start of November 11, 2024, to the end of December 12, 2024. |
2031-11-19T01:35:10Z-2099-08-04T18:22:48Z | 2031-11-19T01:35:10Z to 2099-08-04T18:22:48Z | Exactly as specific as the ISO dates say. |
January 3 - Apr 6 | 2022-01-01T00:00:00Z to 2022-04-07T00:00:00Z | As this documentation was written in 2022, the year 2022 is inferred. Note how the range extends to the end or April 6, which makes it the beginning of April 7. |
now - 10 years 6 months 3 days | now to 10 years, 6 months, and 3 days later | now is whatever time the timeline is rendered, not when it was written. 10 years 6 months 3 days is a relative date. |
3:30pm - 4:30pm | Today's date, from 15:30 to 16:30 | When a time is by itself, it is based off of the last date seen, or, if there isn't any, today. |
1 Jan 1998 to 11/11/2011 8am | 1998-01-01T00:00:00Z to 2011-11-11T08:00:00Z | |
Nov 11 02:30 | 2011-11-11T02:30:00Z to 2011-11-11T02:30:00Z | When a time is specified (hour/minute), the granularity is instant. |
Non-EDTF Dates
A date can be expressed in a few forms. Human readable dates are supported, like 1665
, 03/2222
, 09/11/2001
, 18 March 2026
, Aug 30 9:45am
, as well as IO8601 dates, like 2031-11-19T01:35:10Z
. Human readable date formatting defaults to the American Month/Day/Year but can be changed to European formatting via the header.
Relative Dates
If you have events that are based off of, or relative to, other events, you can describe their relationship to get the range you want.
For example, say you are working on a project tracker. You could outline the phases of your project by using absolute dates, like the following:
// To indicate we are using European date formatting
dateFormat: d/M/y
// 2 weeks
01/01/2023 - 14/01/2023: Phase 1 #Exploratory
// Another 2 weeks
15/01/2023 - 31/01/2023: Phase 2 #Implementation
// 1 month
02/2023: Phase 3 #Implementation
// 3 days, after a one week buffer
07/03/2023 - 10/03/2023: Phase 4 - kickoff! #Launch
However, as soon as something changes (say something slips or an estimate was wrong), you would have to go through all events and change their dates manually. This would be especially troublesome if the change is early on.
With relative dates, we can express the same timeline like so:
// 2 weeks
01/01/2023 - 2 weeks: Phase 1 #Exploratory
// Another 2 weeks
2 weeks: Phase 2 #Implementation
// 1 month
1 month: Phase 3 #Implementation
// One week after phase 3 ends, a 3 days kickoff event
1 week - 3 days: Phase 4 - kickoff! #Launch
Relative dates base themselves off the previous date, and this goes all the way back to our first date, 01/01/2023
.
Event IDs
This works well enough for serial dates that are each dependent on the last, but what if we have multiple events that are all dependent on the same event? We can do that using event ids:
// Event ids are represented by an exclamation point followed
// by the id - like !Phase1
01/01/2023 - 2 weeks: Phase 1 #Exploratory !Phase1
// Another 2 weeks
after !Phase1 2 weeks: Phase 2, in parallel with Phase 3 #Implementation
// 1 month
after !Phase1 1 month: Phase 3, in parallel with Phase 2 #Implementation
// 3 days, after a one week buffer
1 week - 3 days: Phase 4 - kickoff! #Launch
The word after
is optional, we could say !Phase1 2 weeks: Phase 2, in parallel with Phase 3 #Implementation
to have the same effect.
Relative dates will first attempt to refer to the event that was specified by a provided event id. For !Phase1 2 weeks: Phase 2
, the event with the id Phase1
is looked for, is checked for when it ends, and is used as the reference upon which 2 weeks
is based.
If we can't find the event id, or no event id is given, the relative date is instead based upon the last date in the timeline - "last" here meaning most recently written, as the timeline is parsed from top to bottom. So if we have a timeline like this:
2020: Pandemic
2021 - 2023: More pandemic
1 year: Less pandemic?
1 year
is based off the last date seen, which would be 2023
, or, more specifically, the end of 2023
.
This also means that we can base our end date off of our start date:
12/25/2022: Christmas
5 days - 3 days: New Years' stuff
Here, 5 days
is five days after the previously seen date (12/25/2022
), which would make it 12/30/2022
, while 3 days
is three days after the previous date, which is our start date of 12/30/2022
.
Two relative dates together, like x days - y weeks: ...
, can therefore essentially be read as x days after the previous event and lasts for y weeks
.
The only exception to this is the shorthand singular relative date, like x years:...
, which means immediately after the last event and lasts for x years
.
Due dates
In the same way you can represent an event taking place after a prior event, you can indicate that an event should come before another. Let's say we wanted to get some things done before Christmas:
2022-12-25: Christmas !Christmas
before !Christmas 1 month: Buy presents
before !Christmas 2 weeks: Get a tree
By using event ids, we specify the due date, and specify the amount of time before that event. Like all event ids, the id must be defined earlier in the document in order to be able to reference it; something like the following would not work:
before !Christmas 1 month: Buy presents
before !Christmas 2 weeks: Get a tree
2022-12-25: Christmas !Christmas
because the event with the id of !Christmas
is after the events that refer to it. A good way to deal with this is to define the known dates at the start of your timeline and then look at a sorted view to see them in order.
Similar to relative events that are dependent on preceding events, events with due dates can also have start and end times:
2022-12-25: Christmas !Christmas
before !Christmas 1 week - 1 month: Buy presents
Here, the Buy presents
event ends 1 week before Christmas, and lasts for 1 month. When dealing with relative events, the first part of the range (if there is one) can be considered the "buffer," while the second part is the duration. If there is only one part (no range; before !Christmas 1 month
instead of before !Christmas 1 week - 1 month
), then it will abut the event it is basing itself off of with the specified duration.
Also similarly to other relative events, if no event id is specified, it will be dependent on the previous event.
Before
and by
can both be used to represent happening before another event. These are equivalent:
by !Chistmas 1 day: ...
before !Christmas 1 day: ...
Week days
When using relative dates you can also take advantage of being able to specify week days
- this calculates durations based on how many non-weekend days it takes.
For example:
July 13, 2022 - 5 week days: Item estimate
10 week days: Second part of item
July 13, 2022 is a Wednesday, and we're counting 5 week days, so we go Wednesday, Thursday, Friday (3), and then the following Monday and Tuesday (2), which gets us to the end of July 19 (technically midnight July 20, a Wednesday).
The second event starts after the first and lasts 10 weekdays, which would take us to two Wednesdays into the future, or 14 calendar days.
Week
, work
, and business
are supported as prefixes to day
when working with weekdays. These are all equivalent:
10 business days: ...
10 weekdays: ...
10 work days: ...
Week days do not take into account holidays - only weekends. It also assumes a 5 day work week, unfortunately. Hopefully soon it will be 4 days.
Recurring events
To have an event repeat itself some number of times, you can use recurrence syntax between the event range and the event description:
October 7, 1989 every year for 10 years: ...
2025-03-04 every week for 12 weeks: ...
2022-01/2022-03 every other year x9: ...
Feb 1 2023 every 6 months for 10 times: ...
Recurrence syntax essentially takes the form of
every (duration) (for (number of times | duration)) | x(amount)