Fuck yeah, Coffee!
Coffee is a daily ritual for me. A typical morning begins with opening a bag of freshly roasted beans. The deep aroma teases me into wakefulness. Carefully, I measure the beans and grind them to the perfect fineness for the day. Moments later the coffee is brewing. I pour my wife and I our cups. We sip together before we prepare for our day.
We use a drip coffee maker most of the time. Though we have recently added a Chemex coffee maker into the mix. Classic french press is another good way to make a cup. We order our beans from Josuma.
Mid day we often find ourselves placing an order to our favorite coffee shop. Taking a break from work to grab a cup, take our dog Olive for a walk, maybe run some errands, or just sit and enjoy each other's company before wrapping up work.
Working remote has many perks, one of which is being to to choose what remote means for me. My home office is quite perfect and has been a great place to work from. Yet, often I need a change of scenery. The coffee shop is an excellent coworking space, and affordable. A few dollars and I can focus on my projects for a couple of hours. The noise of the coffee shop is just enough to keep me focused, yet not so loud that I can't hear myself think. Body doubling effectively keeps me on task.
Meeting friends and coworkers for real human connection is equally important. It cannot always be about productivity. Reno has a vibrant coffee culture. Many coffee shops here are locally owned and operated. We are spoiled for choice.
Coffeebar
Coffeebar has a few locations in Reno. Each spot has their own unique qualities. The closest location to me has a ton of space to work and to sit and enjoy the cup. The baristas are friendly and the coffee is quite good. My favorite part of each visit is the warm welcome I receive when I pick up my to go cups.
Midnight Coffee Roasters
They are open very late. They make their own syrups. And they brew a strong, tasty cup of coffee. Our go to spot on a friday night after dinner.
Hub Coffee Roasters
Hub has many locations in town, and also has one of the more beautiful spots by the river downtown. They make a very strong cup. The central location is ideal for my friends to meetup and co-work during the day.
Bibo Coffee
I learned all about Monsoon Medley Single Origin Coffee from Bibo.
The coffee’s bean size, color, roast, and cup characteristics are the result of “monsooning,” a post-harvest process unique to India, where it is exposed to monsoon winds in an open-walled warehouse for 12 to 16 weeks.
It's all we drink in my home now. Bibo themselves are awesome. Always friendly, good conversation, and great coffee.
Caffeine
Brewed coffee has roughly around 96 mg of caffeine per 8oz cup. The actual amount of caffeine can vary widely based on brewing time.
The coffee cups in my house are more like 12oz. The calculator below assumes a 12oz cup, and around 12mg of caffeine for each ounce of coffee. That changes to 8mg for weakly brewed, and 15mg for strongly brewed.
The half-life of caffeine is around 3-7 hours for most adults. This means that after 3 to 7 hours, about half of the caffeine in your system is metabolized and eliminated.
The general guideline for safe caffeine consumption is up to 400/mg/day for most healthy adults. That is roughly 4 (8 oz) cups of brewed coffee.
If you consume 200 mg of caffeine, after 3-4 hours, you may have ~100 mg remaining in your system, making another cup more reasonable. Listen to your body—avoid caffeine if you feel jittery or anxious.
Should you have another cup of coffee?
Yes, obviously. Let's calculate it anyway!
The source code for the caffeine calculations
/**
* Calculate the remaining caffeine in the system based on consumption times and amounts.
*
* @param firstCupTime - Time of first cup (HH:mm format)
* @param lastCupTime - Time of last cup (HH:mm format)
* @param numberOfCups - Number of cups consumed
* @param strength - Caffeine per cup in mg
* @param currentTime - Current time (HH:mm format)
* @param halfLife - Half-life of caffeine in hours (default: 5)
* @returns Object with caffeineRemaining and canHaveAnotherCup
*/
export function calculateCaffeine(
firstCupTime: string,
lastCupTime: string,
numberOfCups: number,
strength: number,
currentTime: string,
halfLife: number = 5
): { caffeineRemaining: number; canHaveAnotherCup: boolean } {
const parseTime = (time: string): Date => {
const [hours, minutes] = time.split(":").map(Number);
const now = new Date();
return new Date(now.getFullYear(), now.getMonth(), now.getDate(), hours, minutes);
};
const firstCup = parseTime(firstCupTime);
const lastCup = parseTime(lastCupTime);
const current = parseTime(currentTime);
// Let's not defy physics.
if (firstCup.getTime() > lastCup.getTime()) {
throw new Error("First cup must be before your last cup.");
}
// Maybe we return a success response instead of an error?
if (numberOfCups <= 0) {
throw new Error("You haven't had any coffee yet.");
}
// If you _just had_ your cup we can return without calculating.
if (firstCup.getTime() == current.getTime()) {
return {
caffeineRemaining: strength,
canHaveAnotherCup: strength <= 100
};
}
const caffeinePerCup = strength;
const cups: Date[] = [];
let interval = 0;
if (numberOfCups == 1) {
interval = 1;
}
if (numberOfCups > 2) {
interval = (lastCup.getTime() - firstCup.getTime()) / numberOfCups;
}
for (let i = 0; i < numberOfCups; i++) {
cups.push(new Date(firstCup.getTime() + i * interval));
}
let totalCaffeineRemaining = 0;
cups.forEach((cupTime) => {
const hoursSinceCup = (current.getTime() - cupTime.getTime()) / (1000 * 60 * 60);
if (hoursSinceCup > 0) {
const caffeineRemaining = caffeinePerCup * Math.pow(0.5, hoursSinceCup / halfLife);
totalCaffeineRemaining += caffeineRemaining;
}
});
return {
caffeineRemaining: Math.ceil(totalCaffeineRemaining),
canHaveAnotherCup: totalCaffeineRemaining <= 100,
};
}
the tests:
import { describe, it, expect } from "vitest";
import { calculateCaffeine } from "./caffeine_calculator";
describe("Caffeine Calculator", () => {
it("calculates caffeine correctly for a single cup", () => {
const tests:array = [
{test: {firstcup: "08:00", lastcup: "08:00", numcups: 1, strength: 96, curtime: "13:00"}, expected: [48, true]},
{test: {firstcup: "08:00", lastcup: "08:00", numcups: 1, strength: 96, curtime: "8:00"}, expected: [96, true]},
{test: {firstcup: "08:00", lastcup: "08:00", numcups: 1, strength: 96, curtime: "8:01"}, expected: [96, true]},
{test: {firstcup: "08:00", lastcup: "09:00", numcups: 1, strength: 96, curtime: "9:00"}, expected: [84, true]}
];
tests.forEach((t) => {
const result = calculateCaffeine(t.test.firstcup, t.test.lastcup, t.test.numcups, t.test.strength, t.test.curtime);
expect(result.caffeineRemaining).toBeCloseTo(t.expected[0]); // Half-life decay
expect(result.canHaveAnotherCup).toBe(t.expected[1]);
});
});
it("handles multiple cups spread out during the day", () => {
const tests:array = [
{test: {firstcup: "08:00", lastcup: "12:00", numcups: 3, strength: 96, curtime: "23:00"}, expected: [44, true]},
{test: {firstcup: "08:00", lastcup: "10:00", numcups: 2, strength: 96, curtime: "17:00"}, expected: [56, true]},
{test: {firstcup: "08:00", lastcup: "11:00", numcups: 4, strength: 96, curtime: "17:00"}, expected: [130, false]},
{test: {firstcup: "08:00", lastcup: "14:00", numcups: 5, strength: 96, curtime: "17:00"}, expected: [198, false]}
];
tests.forEach((t) => {
const result = calculateCaffeine(t.test.firstcup, t.test.lastcup, t.test.numcups, t.test.strength, t.test.curtime);
expect(result.caffeineRemaining).toBeCloseTo(t.expected[0]); // Half-life decay
expect(result.canHaveAnotherCup).toBe(t.expected[1]);
});
});
it("throws an error if first cup is after last cup", () => {
expect(() => {
calculateCaffeine("14:00", "08:00", 1, 96, "10:00");
}).toThrow("First cup must be before your last cup.");
});
it("throws an error if no coffee was consumed", () => {
expect(() => {
calculateCaffeine("08:00", "08:00", 0, 96, "10:00");
}).toThrow("You haven't had any coffee yet.");
});
});