// Shared · Session Timings — the three daily trading sessions with live // countdowns, shown as a "Sessions" tab in BOTH the student and admin portals. // Exposes window.SessionTimings({ onGoSupport }). // // Difference from the public homepage version: the Telegram buttons do NOT // link anywhere. Invite links rotate, so a hardcoded link in the portal goes // stale and members end up on a dead URL. Clicking opens a dialog telling them // to get the current link from their coach, with a button through to Support. // // All time math is done in Philippine Time (UTC+8) derived from UTC, so the // countdown is identical for every member regardless of device timezone. // PH has no DST, so the +8 offset is safe year-round. (function(){ const { useState, useEffect, useRef } = React; const SESSIONS = [ { key:'asian', name:'Asian', sub:'Session', hour:7, min:0, time:'7:00', ap:'AM', what:'Signal via Telegram', flags:[['ph','Philippines'],['sg','Singapore'],['jp','Japan']] }, { key:'london', name:'London', sub:'Killzone Session', hour:16, min:0, time:'4:00', ap:'PM', what:'Daily LIVE trading with community', flags:[['gb','United Kingdom'],['de','Germany']] }, { key:'ny', name:'New York', sub:'Opening', hour:21, min:30, time:'9:30', ap:'PM', what:'Daily LIVE trading with community', flags:[['us','United States'],['ca','Canada']] }, ]; const TG_PATH = "M21.9 4.3 18.9 19c-.2 1-.8 1.3-1.7.8l-4.6-3.4-2.2 2.1c-.3.3-.5.5-1 .5l.3-4.7 8.6-7.8c.4-.3-.1-.5-.6-.2L6.9 13.1 2.4 11.7c-1-.3-1-1 .2-1.5l18-6.9c.8-.3 1.5.2 1.3 1z"; const TgIcon = ({cls}) => ; // PH wall-clock = UTC + 8h. Adding 8h lets getUTC* read as PH local time. function phNow(){ return new Date(Date.now() + 8*3600000); } // Next Mon-Fri occurrence of a PH hour:min. Sessions run 60 minutes. function nextSession(ph, hour, min){ var c = new Date(ph.getTime()); c.setUTCHours(hour, min, 0, 0); for (var i = 0; i < 8; i++){ var day = c.getUTCDay(); if (day >= 1 && day <= 5){ var start = c.getTime(), end = start + 3600000; if (ph.getTime() < end) return { start:start, end:end, during: ph.getTime() >= start }; } c.setTime(c.getTime() + 86400000); c.setUTCHours(hour, min, 0, 0); } return { start:c.getTime(), end:c.getTime(), during:false }; } // ph-space epoch -> real epoch. phNow() is Date.now()+8h, so every timestamp // derived from it carries that same +8h shift; subtract it to get real UTC. function realEpoch(phEpoch){ return phEpoch - 8*3600000; } // The session's PH time expressed in the member's own zone. Derived from the // actual next-occurrence timestamp, so their local DST is handled correctly — // "4:00 PM PH" is 9:00 AM in London during BST but 8:00 AM in GMT. function inZone(phEpoch, tz){ if(!tz) return null; try{ return new Date(realEpoch(phEpoch)) .toLocaleTimeString('en-US',{timeZone:tz, hour:'numeric', minute:'2-digit'}); }catch(e){ return null; } } function Countdown({hour, min, tz, onStart}){ const [s, setS] = useState(()=>nextSession(phNow(), hour, min)); useEffect(()=>{ const t = setInterval(()=>setS(nextSession(phNow(), hour, min)), 1000); return ()=>clearInterval(t); },[hour,min]); useEffect(()=>{ if(onStart) onStart(s.start); },[s.start]); const left = (s.during ? s.end : s.start) - phNow().getTime(); const ms = left < 0 ? 0 : left; let sec = Math.floor(ms/1000); const d = Math.floor(sec/86400); sec -= d*86400; const h = Math.floor(sec/3600); sec -= h*3600; const m = Math.floor(sec/60); sec -= m*60; const U = ({v,u}) => {v}{u}; const parts = d > 0 ? [, , ] : h > 0 ? [, , ] : [, ]; return
{s.during ? 'Live now · ends in' : 'Starts in'} {parts}
; } // Local-time line under each card. Recomputed each minute so it stays right // across a DST boundary without a reload. function LocalLine({hour, min, tz}){ const [, tickState] = useState(0); useEffect(()=>{ const t=setInterval(()=>tickState(x=>x+1), 60000); return ()=>clearInterval(t); },[]); if(!tz) return null; const s = nextSession(phNow(), hour, min); const local = inZone(s.start, tz); if(!local) return null; return
{local} your time
; } function LinkNote({onClose, onGoSupport}){ const ref = useRef(null); useEffect(()=>{ const onKey = e => { if(e.key === 'Escape') onClose(); }; document.addEventListener('keydown', onKey); if(ref.current) ref.current.focus(); return ()=>document.removeEventListener('keydown', onKey); },[onClose]); return
e.stopPropagation()}>

Getting the channel link

Our Telegram invite links are rotated regularly to keep the channels private to paying members, so we don't publish a permanent link here.

To get the current link: message your assigned coach directly — they'll send you the active invite. You can also check the live trade announcement channel listed under Support.

{onGoSupport && }
; } function SessionTimings({ onGoSupport, timezone }){ const tz = timezone || null; const [noteOpen, setNoteOpen] = useState(false); return
Chart Growth Learning Hub

Session Timings

of Daily Signals

Monday — Friday · all times in Philippine Time

{tz &&

Also shown in your timezone · {tz.replace(/_/g,' ')}

} {!tz &&

Set your timezone in Account to see these in your local time

}
{SESSIONS.map(s=>(
{s.flags.map(([code,label])=>( {label+" ))}

{s.name} {s.sub}

{s.what}

{s.time}{s.ap}
PH
))}
Trade together. Grow together. Win together.
{noteOpen && setNoteOpen(false)} onGoSupport={onGoSupport}/>}
; } window.SessionTimings = SessionTimings; })();