All files / js calendar.js

81.57% Statements 93/114
73.68% Branches 14/19
63.63% Functions 7/11
81.57% Lines 93/114

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 11573x 73x 73x 73x 73x 73x 73x 73x 73x 73x 73x 73x 247x 247x 247x   247x 73x 73x 73x 73x 72x 72x 72x 72x 72x 72x 72x 72x 72x 72x 72x 72x 72x 72x 72x 72x 72x 58x 58x 14x 72x 73x 73x 73x 174x 174x 174x 174x 73x 73x 73x     73x 73x 73x             73x 73x 73x       73x 73x 73x                   73x 73x 73x 73x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 71x 73x 73x 73x 71x 71x 71x 71x 73x 73x 73x 4x 4x 4x 73x 73x 73x 28x 28x  
// calendar.js — Time/date functions (port of calendar.c)
// CRITICAL: getnow() returns the session's pinned datetime, never wall clock.
// This ensures phase_of_the_moon() and friday_13th() are deterministic.
// See LORE Lesson #7 and DECISIONS.md.
 
import { game } from './gstate.js';
 
// C ref: calendar.c:31 getnow()
// Returns the pinned session datetime (from NETHACK_FIXED_DATETIME env var).
// The C version reads the env var in a patched getnow(); we store the
// pinned time in game.fixed_datetime.
export function getnow() {
    if (game && game.fixed_datetime) {
        return game.fixed_datetime;
    }
    return new Date();
}
 
// Convert a "yyyymmddhhmmss" string to a Date object.
// C ref: calendar.c:119 time_from_yyyymmddhhmmss()
export function time_from_yyyymmddhhmmss(buf) {
    if (!buf || buf.length !== 14) return null;
    const y = parseInt(buf.slice(0, 4));
    const mo = parseInt(buf.slice(4, 6)) - 1; // JS months are 0-based
    const d = parseInt(buf.slice(6, 8));
    const h = parseInt(buf.slice(8, 10));
    const mi = parseInt(buf.slice(10, 12));
    const s = parseInt(buf.slice(12, 14));
    const parsed = new Date(y, mo, d, h, mi, s);
    // C harness patch seeds struct tm from localtime(now), preserving tm_isdst
    // before overriding Y/M/D/H/M/S. Reproduce that mktime quirk here.
    const now = new Date();
    const janOffset = new Date(now.getFullYear(), 0, 1).getTimezoneOffset();
    const julOffset = new Date(now.getFullYear(), 6, 1).getTimezoneOffset();
    const stdOffset = Math.max(janOffset, julOffset);
    const nowDst = now.getTimezoneOffset() < stdOffset ? 1 : 0;
    const parsedDst = parsed.getTimezoneOffset() < stdOffset ? 1 : 0;
    if (nowDst !== parsedDst) {
        return new Date(parsed.getTime() - ((nowDst - parsedDst) * 3600 * 1000));
    }
    return parsed;
}
 
// Get local time struct equivalent from getnow().
// Returns a Date object (JS equivalent of struct tm *).
function getlt() {
    const dt = getnow();
    return dt instanceof Date ? dt : new Date(dt);
}
 
// C ref: calendar.c:48 getyear()
export function getyear() {
    return getlt().getFullYear();
}
 
// C ref: calendar.c:55 yyyymmdd()
export function yyyymmdd(date) {
    const lt = date ? new Date(date) : getlt();
    let datenum = lt.getFullYear();
    datenum = datenum * 100 + (lt.getMonth() + 1);
    datenum = datenum * 100 + lt.getDate();
    return datenum;
}
 
// C ref: calendar.c:79 hhmmss()
export function hhmmss(date) {
    const lt = date ? new Date(date) : getlt();
    return lt.getHours() * 10000 + lt.getMinutes() * 100 + lt.getSeconds();
}
 
// C ref: calendar.c:94 yyyymmddhhmmss()
export function yyyymmddhhmmss(date) {
    const lt = date ? new Date(date) : getlt();
    const y = lt.getFullYear();
    const mo = String(lt.getMonth() + 1).padStart(2, '0');
    const d = String(lt.getDate()).padStart(2, '0');
    const h = String(lt.getHours()).padStart(2, '0');
    const mi = String(lt.getMinutes()).padStart(2, '0');
    const s = String(lt.getSeconds()).padStart(2, '0');
    return `${y}${mo}${d}${h}${mi}${s}`;
}
 
// C ref: calendar.c:190 phase_of_the_moon()
// Returns 0-7: 0=new moon, 4=full moon
export function phase_of_the_moon() {
    const lt = getlt();
    const diy = Math.floor(
        (lt - new Date(lt.getFullYear(), 0, 1)) / (1000 * 60 * 60 * 24)
    ); // day of year (0-based)
    const goldn = (lt.getFullYear() % 19) + 1;
    let epact = (11 * goldn + 18) % 30;
    if ((epact === 25 && goldn > 11) || epact === 24)
        epact++;
 
    return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7) | 0;
}
 
// C ref: calendar.c:205 friday_13th()
export function friday_13th() {
    const lt = getlt();
    // getDay(): 0=Sunday, 5=Friday
    return lt.getDay() === 5 && lt.getDate() === 13;
}
 
// C ref: calendar.c:214 night()
export function night() {
    const hour = getlt().getHours();
    return hour < 6 || hour > 21;
}
 
// C ref: calendar.c:222 midnight()
export function midnight() {
    return getlt().getHours() === 0;
}