β€’  

Build a Simple Calendar with Carbon (and Laravel)

β€’ Comments

Recently I needed to build a fairly simple calendar for a project. To get some ideas on how to approach it, I started searching the Googles and found lots of good articles. In case you're curious, I'll post links to a few of the articles I skimmed at the bottom.

My requirements were pretty simple:

  • Get the entire month for a given date
  • Display the month in either list or calendar view
  • Display events (not covered in this post, but would be easy to implement with the samples below)
  • Bonus, get the days for the previous and following months that overlap with the given months weeks

Most of the articles I found did what I wanted, some more than I needed, and often seemed overly complex. Given the limitations of PHP (maybe πŸ€·πŸ»β€β™‚οΈ) no matter what I did, it just seemed a little clunky. I was coming at it entirely with PHP and then it dawned on β€” you have Carbon!

Starting over, I went back to my requirements to figure out what I needed at a minimum and realized I could get that easily with Carbon. Really, all I needed was the first date and the last date. From there, I can just loop through all the days between the two and I have my calendar!

The example is really quite simple β€”

  • Start with a given date or grab the current date
  • Find the first date of the calendar (again, I wanted to display full weeks, meaning I would want days from the previous month if the month didn't happen to start on the first day of the week).
  • Find the last day of the calendar (same as above, but with the following month).
  • Loop through every day from the beginning to the end, spitting out the day and adding a day on each loop

You can see it's pretty straight forward below. The ->startOfWeek(Carbon::SUNDAY) and ->endOfWeek(Carbon::SATURDAY) are optional if you want to change the default start and end to a week.

public function calendar($date = null)
{
    $date = empty($date) ? Carbon::now() : Carbon::createFromDate($date);
    $startOfCalendar = $date->copy()->firstOfMonth()->startOfWeek(Carbon::SUNDAY);
    $endOfCalendar = $date->copy()->lastOfMonth()->endOfWeek(Carbon::SATURDAY);

    $html = '';
    while($startOfCalendar <= $endOfCalendar)
    {
        $html .= $startOfCalendar->format('j');
        $startOfCalendar->addDay();
    }
    return $html;
}

And your output...

Unformatted calendar

You'll quickly see that even though all the days are there, it's not really usable.

There are literally limitless ways that you could modify and style this. The main purpose of the post is the above☝🏻to show how easy it is to get the dates and then let your imagination run wild. With that said, I still wanted to provide a slightly more polished example to help you get started.

Below I'm doing a few things β€”Β 

  • I'm adding a header to present the month name and year
  • Displaying the labels for the days of the week
  • Formatting the days so it actually looks like a calendar
  • De-emphasizing the days not part of the displayed month
  • Highlighting "today" if it's on the calendar

Again, both the method and the CSS below could be totally different β€” simpler or more complex β€” whatever you need.

public function calendar($date = null)
{
    $date = empty($date) ? Carbon::now() : Carbon::createFromDate($date);
    $startOfCalendar = $date->copy()->firstOfMonth()->startOfWeek(Carbon::SUNDAY);
    $endOfCalendar = $date->copy()->lastOfMonth()->endOfWeek(Carbon::SATURDAY);

    $html = '<div class="calendar">';

    $html .= '<div class="month-year">';
    $html .= '<span class="month">' . $date->format('M') . '</span>';
    $html .= '<span class="year">' . $date->format('Y') . '</span>';
    $html .= '</div>';

    $html .= '<div class="days">';

    $dayLabels = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
    foreach ($dayLabels as $dayLabel)
    {
        $html .= '<span class="day-label">' . $dayLabel . '</span>';
    }

    while($startOfCalendar <= $endOfCalendar)
    {
        $extraClass = $startOfCalendar->format('m') != $date->format('m') ? 'dull' : '';
        $extraClass .= $startOfCalendar->isToday() ? ' today' : '';

        $html .= '<span class="day '.$extraClass.'"><span class="content">' . $startOfCalendar->format('j') . '</span></span>';
        $startOfCalendar->addDay();
    }
    $html .= '</div></div>';
    return $html;
}
.calendar {
    display: flex;
    position: relative;
    padding: 16px;
    margin: 0 auto;
    max-width: 320px;
    background: white;
    border-radius: 4px;
    box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}

.month-year {
    position: absolute;
    bottom:62px;
    right: -27px;
    font-size: 2rem;
    line-height: 1;
    font-weight: 300;
    color: #94A3B8;
    transform: rotate(90deg);
    -webkit-transform: rotate(90deg);
    -moz-transform: rotate(90deg);
    -ms-transform: rotate(90deg);
}

.year {
    margin-left: 4px;
    color: #CBD5E1;
}

.days {
    display: flex;
    flex-wrap: wrap;
    flex-grow: 1;
    margin-right: 46px;
}

.day-label {
    position: relative;
    flex-basis: calc(14.286% - 2px);
    margin: 1px 1px 12px 1px;
    font-weight: 700;
    font-size: 0.65rem;
    text-transform: uppercase;
    color: #1E293B;
}

.day {
    position: relative;
    flex-basis: calc(14.286% - 2px);
    margin: 1px;
    border-radius: 999px;
    cursor: pointer;
    font-weight: 300;
}

.day.dull {
    color: #94A3B8;
}

.day.today {
    color: #0EA5E9;
    font-weight: 600;
}

.day::before {
    content: '';
    display: block;
    padding-top: 100%;
}

.day:hover {
    background: #E0F2FE;
}

.day .content {
    position: absolute;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
}

Formatted calendar

And there you go! πŸŽ‰

I hope you found that helpful in thinking about creating a simple calendar with Carbon. If you have any questions, feedback or suggestions, please leave a comment below.



I found lots of good articles on creating calendars with PHP. For me, many of them did more than I needed them to, but they gave me some ideas on how to think about it or approach it. If you're curious, here are a few that I found useful.