Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ junit.xml

# generated
src/version.json

# videos
public/videos/
2 changes: 1 addition & 1 deletion src/components/CountdownTimer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class CountdownTimer extends Component {
};

static defaultProps = {
targetDate: '2025-08-18T00:00:00',
targetDate: '2026-08-17T00:00:00',
};

state = {
Expand Down
28 changes: 2 additions & 26 deletions src/components/HeroSection/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,11 @@ import CountdownTimer from '../CountdownTimer';
import './styles.css';

const HeroSection = () => {
const getNextFriday9amPT = () => {
const now = new Date();
const laNow = new Date(
now.toLocaleString('en-US', {timeZone: 'America/Los_Angeles'})
);
const laTarget = new Date(laNow.getTime());
const laDay = laNow.getDay();
const daysUntilFriday = (5 - laDay + 7) % 7;
laTarget.setDate(laNow.getDate() + daysUntilFriday);
laTarget.setHours(9, 0, 0, 0);
if (
daysUntilFriday === 0 &&
(laNow.getHours() > 9 ||
(laNow.getHours() === 9 &&
(laNow.getMinutes() > 0 ||
laNow.getSeconds() > 0 ||
laNow.getMilliseconds() > 0)))
) {
laTarget.setDate(laTarget.getDate() + 7);
}
const offsetDeltaMs = now.getTime() - laNow.getTime();
const targetInstantMs = laTarget.getTime() + offsetDeltaMs;
return new Date(targetInstantMs);
};
return (
<section className="hero-section">
<div className="hero-content">
<h1>Hackweek 2025</h1>
<CountdownTimer targetDate={getNextFriday9amPT()} />
<h1>Hackweek 2026</h1>
<CountdownTimer targetDate="2026-08-17T00:00:00" />
<p className="hero-subtitle">until the hacking begins</p>
</div>
</section>
Expand Down
4 changes: 2 additions & 2 deletions src/components/MicroCountdownTimer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, {useState, useEffect} from 'react';
import './styles.css';

const calculateTimeLeft = () => {
const hackweekDate = new Date('2025-08-18T00:00:00');
const hackweekDate = new Date('2026-08-17T00:00:00');
const difference = hackweekDate - new Date();
Comment on lines +5 to 6

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential bug: Countdown timers now use a date string without a timezone, causing them to resolve to the user's local time instead of a single global time.
  • Description: The application's countdown timers were changed to use a hardcoded date string, 2026-08-17T00:00:00, without a timezone specifier. Both the native Date constructor and the moment.js library interpret such strings in the user's local timezone. The previous implementation contained logic to synchronize the countdown to a specific moment in Pacific Time. As a result of this change, users in different timezones will see the countdown end at different absolute times, which can cause significant confusion for a globally coordinated event like a hackweek.

  • Suggested fix: To ensure the countdown is synchronized globally, modify the date string to include a timezone. For example, use an ISO 8601 string with a UTC designator, such as '2026-08-17T07:00:00Z', to represent a specific moment (e.g., midnight Pacific Daylight Time). Alternatively, restore the logic that dynamically calculates the target time in a specific timezone.
    severity: 0.7, confidence: 0.95

Did we get this right? 👍 / 👎 to inform future reviews.


if (difference > 0) {
Expand Down Expand Up @@ -32,7 +32,7 @@ const MicroCountdownTimer = () => {
return <h3 className="micro-countdown">VOTE NOW!!</h3>;
}

return <h3 className="micro-countdown">{timeLeft.days} days away</h3>;
return <h3 className="micro-countdown">{timeLeft.days} day till hackweek</h3>;
};

export default MicroCountdownTimer;
60 changes: 8 additions & 52 deletions src/pages/Login.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,68 +11,24 @@ import InfoSection from '../components/InfoSection';
import './styles.css';

const Login = ({firebase, auth, profile, router}) => {
// Compute if hackweek has started and countdown to Friday 9 AM PT
const HACKWEEK_START_ISO = '2025-08-18T00:00:00';
// Compute if hackweek has started and countdown to August 17th 2026
const HACKWEEK_START_ISO = '2026-08-17T00:00:00';

const getMsUntilFriday9PT = () => {
const getMsUntilHackweek = () => {
const now = new Date();
const laNow = new Date(
now.toLocaleString('en-US', {timeZone: 'America/Los_Angeles'})
);

// Determine upcoming Friday 9:00:00 in LA time
const laTarget = new Date(laNow.getTime());
const laDay = laNow.getDay(); // 0=Sun ... 5=Fri
let daysUntilFriday = (5 - laDay + 7) % 7;
// If it's Friday and past 9 AM, move to next Friday
const isFriday = daysUntilFriday === 0;
laTarget.setDate(laNow.getDate() + daysUntilFriday);
laTarget.setHours(9, 0, 0, 0);
if (
isFriday &&
(laNow.getHours() > 9 ||
(laNow.getHours() === 9 && (laNow.getMinutes() > 0 || laNow.getSeconds() > 0)))
) {
laTarget.setDate(laTarget.getDate() + 7);
}

// Convert LA wall time target to actual instant by adjusting with offset between local and LA now
const offsetDeltaMs = now.getTime() - laNow.getTime();
const targetInstantMs = laTarget.getTime() + offsetDeltaMs;
const diff = targetInstantMs - now.getTime();
const hackweekStart = new Date(HACKWEEK_START_ISO);
const diff = hackweekStart.getTime() - now.getTime();
return diff > 0 ? diff : 0;
};

const getHasHackStarted = () => new Date() >= new Date(HACKWEEK_START_ISO);

const [msUntilFridayPT, setMsUntilFridayPT] = useState(getMsUntilFriday9PT());
const [msUntilHackweek, setMsUntilHackweek] = useState(getMsUntilHackweek());
const [hasHackStarted, setHasHackStarted] = useState(getHasHackStarted());

const getNextFriday9amPT = () => {
const now = new Date();
const laNow = new Date(
now.toLocaleString('en-US', {timeZone: 'America/Los_Angeles'})
);
const laTarget = new Date(laNow.getTime());
const laDay = laNow.getDay(); // 0=Sun ... 6=Sat
const daysUntilFriday = (5 - laDay + 7) % 7; // 5 = Friday
laTarget.setDate(laNow.getDate() + daysUntilFriday);
laTarget.setHours(9, 0, 0, 0);
if (
daysUntilFriday === 0 &&
(laNow.getHours() > 9 ||
(laNow.getHours() === 9 && (laNow.getMinutes() > 0 || laNow.getSeconds() > 0)))
) {
laTarget.setDate(laTarget.getDate() + 7);
}
const offsetDeltaMs = now.getTime() - laNow.getTime();
const targetInstantMs = laTarget.getTime() + offsetDeltaMs;
return new Date(targetInstantMs);
};

useEffect(() => {
const interval = setInterval(() => {
setMsUntilFridayPT(getMsUntilFriday9PT());
setMsUntilHackweek(getMsUntilHackweek());
setHasHackStarted(getHasHackStarted());
}, 1000);
return () => clearInterval(interval);
Expand Down Expand Up @@ -139,7 +95,7 @@ const Login = ({firebase, auth, profile, router}) => {
<section className="hero-section">
<div className="hero-content">
<h1>HACK TIME!!!</h1>
<CountdownTimer targetDate={getNextFriday9amPT()} />
<CountdownTimer targetDate={new Date(HACKWEEK_START_ISO)} />
<p className="hero-subtitle">until video submission deadline</p>
</div>
</section>
Expand Down
2 changes: 1 addition & 1 deletion src/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {loginRequired} from './auth';

export default (
<Route path="/" component={App}>
<IndexRedirect to={`/projects`} />
<IndexRedirect to={`/years`} />
<Route path="/login" component={Login} />
<Route path="/projects" component={loginRequired(ProjectList)} />
<Route path="/new-project" component={loginRequired(NewProject)} />
Expand Down