 So I'm going to be talking about recurring dates today. See John run on Twitter if anyone is interested in hearing more about recurring dates. This is my blog. The slides that we posted here tonight, you can also find the slides on the GitHub page for Ice Cube, which is the gem I'm going to talk about today. Not the wrapper, the gem. I know he's gotten a lot of attention lately, but I work at Patch in New York City. Patch is a hyper-local news startup. It's a network of websites, about 117 Sharong in little towns all across America. The reason that this project started is Patch, as part of its offering, has an events calendar. And often the events calendar on Patch needs to have committee meetings and board meetings. And board meetings often have weird schedules. It's not like every Monday, like a soccer game might be or anything like that. Normally board meetings are the second Tuesday of the month, or the third Sunday, or the second to last Monday. They need to be weird things. And you don't want people to have to go back to the site every three months to make sure that they have the next three months of board meetings set up in the events calendar. They need to be able to say the schedule once and have it exist. So I set to make a gem that would handle date recurrence. And notice that there's a lot of problems with dates just in general. In fact, dates are normally a sticking point for any language. Often when a language comes around, it normally has a built-in time and date library that lasts maybe a year until people decide that it doesn't do what it's supposed to do, or it's too buggy, or the interface isn't right. But just times in general have some problems, too. Daylight savings times a very common time problem. Also time zones in general. And with them, they're UTC GMT offsets. And what people don't often realize is that different areas of the globe actually have different, obviously GMT offsets, but also have different times that they jump in daylight savings time and jump during different times of the year. So to negotiate all of the switches between different time zones is a huge issue. And then fucking February comes out of nowhere. And it's only got 28 days. So on top of the time problem, there's the recurrence problem, which is basically the first and last Monday of every month. And it seems like a really easy problem when humans go to describe it. But when we try to describe it in code, we end up having to allocate for all the weird time issues. So one easy way to get around that might be to use the date library and just step forward in time, the amount of time we have to go, second by second, and let the date library handle all the weird idiosyncrasies with time. That ends up being really slow, because you have to step through every second between now and the next occurrence. So you're going to end up with something that's difficult to implement. You're going to end up with code that's really not generic at all. You're probably going to end up with code that's ugly. You're probably going to end up with code that's slow. But most importantly, this just really isn't code that you should ever have to write. There should be a library that allows this to happen. And that's what IceCube is. So here's the GitHub page, github.com.cjohnrun. Slash IceCube. On there, there's also a link to the introduction page, which is like a pretty version of the read me. A good starting point for IceCube is actually the iCalendar RFC. You figure that somebody already went through the trouble of making recurring dates. And they shouldn't throw away all the work that they've already done, because it already exists. Someone can describe recurring dates better than Kron, and it's called iCalendar. iCalendar, you might have also heard this referred to as vEvent, or vCal, or iCal. And those are all the same standard. So IceCube actually, in its specs, we implement every example inside the iCalendar RFC. This is just to make sure that we have coverage with every example that iCalendar would have, which obviously led to a lot of reading. We came out with a really clean Ruby syntax for the entire thing. And an awesome name, question mark? This guy doesn't think so. But he thinks that IceCube's pretty awesome. So hopefully by the end of the talk, you'll think the same. I'm going to give you a quick example of what using IceCube might look like, just so we can get the idea of what we're headed toward. So you create a schedule. The schedule has a start time. You create a new schedule, and you give it a time that it starts on. In this case, the first of 2010 in January. And you attach rules to the schedule. So this rule is just a rule that occurs monthly on the day of the month when it's 13 on Fridays. So this is a rule that describes every Friday the 13th. Then you have methods on the schedule that you can call, like schedule.first10. And as you- Is our rule typo, or is it our rule? It's our rule. Our rule stands for recurrence rule, and we'll get a little bit more into why it's our rule. Our rule is actually a throwback also from the iCalendar spec. If you don't like that syntax, you can also do schedule.add underscore recurrence underscore rule. And that's what it really stands for. So you can see here that we generate the times. And on the next slide, if you go through the exact same example, you see that iCalendar is actually, or that ice cube is actually negotiating the offsets as it goes through time. My 240 specs run in 1.1 seconds. So this thing is really fast, too. So to talk about rules. Rules are really the foundation of ice cube. And there are a bunch of different kinds of rules. There's yearly, monthly, weekly, daily, hourly, minute, and secondly. So something like every month would be really simply written, just ruled out monthly. You create a new monthly rule. Every other month, ruled out monthly of two. This is called the interval in iCalendar. And obviously, every end month is ruled out monthly of n. But that's not really interesting, because all you can do is weekly events, or daily events, or yearly events, and they can only start on the date that you want them to start on. So then enters validations. So something like every day, this really simple thing, can become more complicated by just saying, every day, when the day is Friday. And you can actually chain them together, like I did in the example before. Every day where the day is Friday and the day of the month is 13. So multiple validations on the same rule form an and relationship. And you can have as many of these chained onto the rules as you want. These are the different ones. So we'll use most of these through the example. But you can pretty much imagine what most of them do. Day is for an individual day of the week. Day of week sounds like it would be the same thing, but it's actually like this day of this week in the month. So the first Friday, or the second to last Tuesday. The rest are pretty self-explanatory. So then schedules, group rules. So you can make a new schedule that starts now and occurs every day. And you would just do schedule.rrule of rule.daily. But it doesn't occur on days that are Saturdays, schedule.x rule of rule.daily.dayofsaturday. So you can combine different types of rules. And this is why rrule and not just rule. Because you can actually have exclusion rules as well as recursion rules. So when you ask if this occurs on today, it does. This was actually made yesterday. Wait, is that right? Yeah. So it doesn't, because today is Saturday. And when you ask if it occurs tomorrow, it does, because tomorrow's not Saturday. Make sense? So you can actually also have blackouts on your schedules. You can add rdates and xdates. Exactly what they sound like. And rdate is a single occurrence that has to happen. And xdate is a single occurrence that can't happen. The order of precedence, because that's probably what everyone's thinking right now, goes like this. And this is directly out of my calendar. I hope this makes perfect sense. It basically says that if I say there shouldn't be a date, there definitely shouldn't. Then if I say that there should be a date, there definitely should. Then if I have a rule that blocks a recurrence rule, but should be exception rules, that those will have precedence. So how we can use some of the ice cube? How am I doing on time here? How we can start to use ice cube? We can do what we did there, which is does this have an occurrence on a single date? Does it occur on this date? You can ask if this rule occurs at a given time. And you can actually ask if your schedule has a duration. You can actually ask if the schedule has an event that's occurring at a given time. So if the schedule has a duration and the time that you query inside of occurring at falls any time within the seconds of the start date of that event, this will return true. And then there's basic expansion. So you could do things like the first 10 occurrences, or all occurrences, or all of the occurrences until a time, or all of the occurrences between two times. Any questions on any of this? Yeah, there is. That's a good question. So rules can actually have, not on these slides, but can actually have until dates and counts. So an until date would be this rule is not effective after this date, but all the other things are. And count is this rule is not effective after it has contributed to a certain number of occurrences toward the schedule. All of the rules have to have either an until or account in order to use all occurrences. There is no default. We just stop you from using all occurrences. It'll throw. I think if you try to call all occurrences, you'll get an argument error back. Or maybe, yeah. Ice Cube also speaks. So you take a rule, and it's a monthly rule. And the day of the week is the last, or second to last Friday. First you have two hash, which is basically just a nice hash-based representation of this. It has nothing to do with any objects at all. So you know how you sometimes call two hash or two YAML on things? You end up with the objects just marshaled into YAML. Ice Cube won't do that. And the YAML representation inside of Ice Cube actually is backed by the hash representation. So you end up with this, not like some crazy. And of course it goes out further. But the representations end up smaller and not dependent on any kind of implementation. There's also a port of Ice Cube called Ice Cube for P. It's written for PHP. So PHP and Ruby Ice Cube could actually interchange data and not have a problem when other frameworks would have an issue with that. Two iCal, I think is easy enough, because since everything's based on iCal, I can just generate these. So you get that back. And then just for fun, this is not i18n compliant, but 2S monthly on the second to last Friday. So no matter how many things you add, this will try to generate something. If someone wants to take up a pet project to make it better, that would be cool. Are you giving them another library for that? This is all internal, everything. Actually, there's some places where it'd be really convenient to use active support calls. And if you have required active support, I'll use the active support calls. And if they're not there, then I fall back on internal Ice Cube calls. Are you planning on taking it and translating it to where Tickel does out here? So I've talked to the Tickel guy. He was actually messaging me early on. And I was saying that it would be cool if Ice Cube were to back Tickel, and that Tickel were more just like the layer that were to make these things happen. So we talked about that a bit. I don't know if it's going to happen, but maybe. But right now, this is all internal. So Tickel is more just like the date. But it would really be cool if the Tickel project were to take on recurrence like this. There's straight Ruby date types. So internally, everything in Ice Cube is represented by time. This time is faster than date time. And it can represent times. And then we also do some. So if you take a time that you have in Ruby, and you're using Rails, and you roll the time into the database, Rails will actually convert it to UTC before it gets to the database, which is bad for recurrence because you lose the valuable information inside the start date when you un-martial it. It comes back out like. So what we do, we realize that the date time and time representations of a date are exactly the same. So before we roll it into the database, we actually convert it to a date time, the internal dates for Ice Cube. That way it gets saved still with its time zone. When you roll it back out, it comes out as a time with zone if you're using Rails. Any other questions on this? What's up? I don't know. I don't know, actually. That's a good question, though. So schedules can also speak. So I add an R rule and an X rule to this. And I convert it to hash, to ham, to YAML. You can convert it to iCal. And it'll actually do the start with the two rules underneath it, and you'll get new lines between them. So this is actually, if you were to embed this directly inside of a V event on your page, if you wanted to have like send to Outlooks and iCal, send to X, Y, Z button, you could actually embed this directly inside V event, which is what we do at Patch. And the actual 2S representation on the schedule, we also use that at Patch to display to the user how the thing occurs. Which is why it would be really cool if someone were to take on the project of making it i18n compatible. Because then, when we go to Europe, it'll be an easy transition. So I know that's not the best representation for us. So I wanted to talk about Ice Cube's magic, like what makes all of this possible, and why it's not slow. So there's a lot of combinations, obviously. And those combinations bring about scary performance implications, just because of the sheer amount of things that you would have to do. So it came up with a cool pattern on how to solve this problem. And it involves educated guesses, which is kind of, I know that's kind of a scary term in computers. But the way that the educated guesses work is basically every validation that you would put on a rule or every rule submits basically what the closest date that it could happen on is. So if I'm a day of Friday, I say the closest day that I could happen is this Friday, the upcoming Friday. And then also on the rule, there's a validation that says the 13th of the month, but it's maybe the 20th of the month. So he says, the closest that I could possibly happen is next month on the 13th. Then we just take all of those, take the farthest away one, and jump to that. So you skip all the time in between. And that's how Ice Cube's able to be really quick. Ice Cube's usage, so how is this being used right now? So people have events calendars, like we have events calendars. There's actually a project about the open source project to make a single interface that people can use and just drop into their site that backs itself with Ice Cube. Time notifications, so people are saying, I want to send out an email every time that it is board meeting day. People are doing a schedule conflict resolution, so there's actually some people that are saying, Tom is only free on Wednesdays, and Steve is only free every other Tuesday. Tom is only free on Wednesdays and Tuesdays. Steve is only free on every Tuesday. That's the third Tuesday of the month. You can throw them all together and just call dot-first on the schedule, and you'll get the next time that those people could possibly meet. So people are using it for that. Yeah, if there's no match, you get no. And first, so something weird about first, just if you call dot-first on a schedule and you give it no argument, it'll actually remove the array from backing it and just give you a single element back. But if you call dot-first of one, then you'll get it inside the array outwardly. So that's a little weird. Some people are making this cron monster thing, where basically you would schedule your tasks with, it's kind of like whenever, but a little bit more complicated. So you'd schedule your tasks with Ice Cube, and then basically they tear through the Ice Cube schedules to see when things should happen. So it's like a monster thing. I don't even know if it's a good idea, but they're doing it. So I challenge all of you to also make something awesome and let me know what you do with it, because I think the library is really powerful. And it tackles an area that I don't know. You probably wouldn't touch ever. This is my blog. I just want to use this time for questions and maybe a demo if anyone's interested. So any questions about the whole thing? What do you guys think? Yeah? That would be good. So everyone heard the question, I guess. So I think some people, when they use this, are directly querying Ice Cube schedules. Other people are actually expanding the dates, and then having those sit in kind of like a denormalized format off to the side so they can query it easily. Is querying is something you have to do often? Being able to easily query against all of them? I think that would be a really, really cool application of this. Right? Yeah? Yes? Anyone else? No, go ahead, please. No, I don't think it would. I think this would be a fair application. And I also think that there's a lot of convenience methods built into Ice Cube that allow you to query it in ways that we didn't show here. For example, if you do a month of year and you have to say July and August, you can also represent those as integers, I think it would work totally fine. And I think that it's natural. So there's also this concept of adding schedules to schedules that people have been using Ice Cube for. And I think you would have to do some of that. So you have two schedules, and you're basically querying occurrences across both of them at the same time. People use that if they need events in different schedules that have different durations. So we should talk more about how that integration would go. Yeah, intersection or union of multiple rules. So if you just elaborate a little bit more. If you have two rules, yeah, absolutely. So if you put, so there's definitely intersection. So if you add two R rules to the same schedule, it'll give you all of the dates that occur on any of those R rules. But you can't. So if you want all of the things that occur when all of those things are happening, you would accomplish that with a single rule. That has all of the attributes that you would have put on the multiple rules. So validations on a single rule form an AND relationship. And multiple rules on the same schedule form an OR relationship with each other. Right, yeah, yep. And you could also accomplish that with multiple schedules and use any or all with the schedules. Yeah, so the built-in time library in Ruby only supports local or UTC. Those are the only two things that it can do. Time with zone in Rails, inside of active support, allows you to do in time zone and use any time zone that you want. So if you're using time with zone, the support is there, and it's fully tested and fully compliant across all of time zone. So you can use it with any time zone you want, and it'll auto-negotiate, like passing through time zones, or you can even have end dates on schedules or inside of repeat rules that are in different time zones. And it'll do all of that. But if you're using the standard time library, you only have access to what Ruby has access to, which is local and UTC. But you could also just convert everything to UTC first. Does that answer your question? Also, people have been thinking about, I don't know if anyone's done it successfully, but I don't understand why they wouldn't be able to. Integrating this with third base and homerun, one of the two, so you get even better performance out of IceCube if you were to use a separate time library. So if anyone doesn't know, homerun is a time library that was written in pure C. That's a lot faster than time.rbs. Anyone else? Any other questions? OK, yeah? Yeah. OK, so the first Monday after February 14th, if you were to do the first Monday after February 14th, you would have to set the start time to be February 14th inside the schedule, then add a rule that said every Monday, and then just call dot first on the schedule. Oh, I see what you're saying. So you want, OK, three days. Right, right. I guess you could have two schedules. And what also you could do is like, you know the date is going to be like the third Wednesday of the month, right? That's a good question. You go three days before that, yeah? Right, because there's no way to negotiate what the beginning of the month would be. So you can't just say send it out on the Monday instead. What you could do is just calculate the occurrences as if it was the event, and then just subtract three from all of the things that it generates. Subtract minus three, three days, right? And then you know if it's inside that occurrence set that you should notify. I know that's kind of like a hack, but yeah. iCalendar doesn't support that by itself. And we really tried to not go outside the bounds of iCal just because the two iCal method is really nice to have. One thing I wanted to do for the longest time was support rather than having to maintain multiple schedules for different time events. I wanted to support different durations for different rules, but iCalendar can't do that. So I didn't do it. OK? Yeah? No? No one else? My slides are located at cjonrun.github.com slash icecube. So this is a page that's set up for it. And if you go straight down to the bottom, the slides are right there. And there's also an introductory deck that I used at Ruby NYC a couple weeks ago. Yeah, yeah, totally. This is actually what we do at my job. So the fact that all of these methods like dot day validation can actually take integers instead of just symbols makes it really easy to integrate with front end code. So you have basically like a 0 through 6, and the values are 0 through 6 for the selections of what day this thing should happen on. And then when it comes back, you can push that right into the rule. And then when you store this entire structure, the schedule, dot 2 YAML inside the database, you can easily pull it out and query it. Some people choose to store like a basic denormalized form separate of it so that they don't have to pull all of these out and recreate the schedules. Anyone else? Yeah? To Cron? That would be awesome, yeah. If you could like install an icecube schedule? Yeah, that'd be sweet. That's actually, can you submit a ticket for that? There's a lighthouse account. That'd be great, yeah. And it wouldn't be that hard either. The one fallback of it is like if people use things that Cron can't do. Right, right. Yeah, yeah. I'd say like wipe the hard drive and reinstall. Does it use what? Yeah, so I use a zone info database that's tied to time.rb. So it actually, date.rb, I think it's in date.rb. But they have the basic time zones there. They just don't allow you to like create one in the different time zone, which is really weird. Like time.rb can completely handle zones that are not your own. It just doesn't expose that method. It doesn't expose like switching time zones. It just doesn't do it. Nope. And I use all of that for when I marshal the times back into iCal, you actually have to put like the string representation of that time zone. And I use time.rb for that also. Custom time zones? Yeah, I mean, like what cost them time zone? What? Mars? Yeah, if you wanted to add Mars, I guess you could. Yeah. Mars is in the eastern daylight. Mars is wherever the Earth lines up with Mars at that point. It's in Pennsylvania. It's in Pennsylvania. Yeah, yeah. There's actually, I have a prototype branch just completely local on my machine that does from iCal, which I think would be a huge addition to this whole thing. Then you can just take that iCal database you're talking about, run them all through his rules, and have like is today a holiday? Yeah. Which I mean, that's like really cool use case. The best one I've found so far is from the iCal under RFC, it's American election days. So every four years, I think it's like the Tuesday after the first something in November. And it can do that totally fine. I don't do moons. Yeah, Easter would be tough. But there's a whole bunch of, I can't do moons obviously, but the tricks that you have to employ to do the election day, you basically have to say, you can look for the example online. It's pretty, actually, I could even show you it. To do what? Oh. Yeah, let me show you this code for this thing. What's that? Oh, yeah, I will. So there's RFCs back here. So this is the presidential election one here. So every four years, the first Tuesday after a Monday in November, after the first Monday in November. So you create a schedule. In this case, I had it start in 1996, because that's from the RFC. You add a recurrence rule. The way the recurrence rule works. Month of year in November, the day is Tuesday. And the day of the month. So this is one of these tricks I'm talking about to use an iCalendar. The day of the month is one of these. So you're basically saying exactly what the rule says. Yeah, you can use a range check. Right, yeah. OK? Yeah? Yeah. So this is one of two things that is in our calendar that isn't in iCube right now. There's that, the business days one. And there's also buy set pass. Buy set pass is, it's obviously a buy set position. It basically says, I want the fifth occurrence that this thing would happen on, but none of the other ones. There is, yeah. And this is actually, business time was featured alongside iCube on Ruby 5. So yeah, business time does that. But it doesn't do all the other stuff iCube does. And there's also, there's another gem that does something like iCube does. It's called, I think it's like rocket recurrence or something. But the way that they do it, I'm just going to type code up here, the way that they do it, they'll do like add rule daily, and they'll do like this inside of a string. So it's like your string programming, and this represents the actual days of the week, what days this thing should occur on. And that's how this whole thing started. We went and saw that, and we're like, this is not going to happen. So thank you for having me in Austin. It's a great city. If anyone has any questions or wants to talk more, wants to follow me on Twitter, or install iCube, do it.