npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

neamtime-log-parser

v0.7.0

Published

Parses neamtime time tracking logs

Readme

neamtime-log-parser

A TypeScript library for parsing timestamped markdown time logs into structured data. Handles the neamtime format where work sessions are documented with timestamps, making them machine-parseable for reporting and analysis.

Features

  • Timestamp Format Support: Parse markdown files with timestamped entries
  • Session Detection: Extract work sessions with start/end times
  • Duration Calculations: Automatically calculate session durations
  • Timezone Support: Handle multiple timezone specifications
  • Pause Detection: Track breaks and pauses in work sessions
  • Frontmatter Parsing: Extract client, project, and category defaults from YAML frontmatter
  • Client/Project Tracking: Entries include client and project fields from frontmatter or .:: tags
  • Flexible Category Syntax: Three-format .:: tags for category, client/project, or full override
  • Data Export: Generate structured time reports and CSV exports
  • CLI & API: Use as a command-line tool or integrate into your application

Installation

npm install neamtime-log-parser

Format Overview

The neamtime log format is a simple markdown-based time tracking format:

start 2021-01-02 (+0200) 10:11

2021-01-02 (+0200) 10:21, working on documentation

paus 12:10->

start 13:00

2021-01-02 (+0200) 14:30, finished documentation

#endts

Key Elements:

  • start - Begin a new work session
  • paus or pause - Mark a pause/break
  • Timestamps with format: YYYY-MM-DD (±ZZZZ) HH:MM
  • Task descriptions after timestamps
  • .:: Category / Subcategory - Categorize time entries (can appear before first entry)
  • #endts - End of time log

Frontmatter:

Time logs can include YAML frontmatter to set defaults for client, project, and category:

---
client: Acme Corp
project: Website Redesign
default_category: Development
---

start 2025-01-15 10:00

2025-01-15 10:15, working on feature X

Categories with .:: Tags:

The .:: marker supports three formats for organizing time entries:

.:: Category                        # Just category (client/project from frontmatter)
.:: Client / Project                # Override client and project (category from frontmatter)
.:: Client / Project : Category     # Full override - client, project, and category

Example with all formats:

---
client: Acme Corp
project: Main App
default_category: Development
---

start 2025-01-15 10:00

.:: Development
2025-01-15 10:15, working on feature X    # Uses Acme Corp / Main App / Development

.:: BigCorp / Mobile App
2025-01-15 11:30, code review             # Uses BigCorp / Mobile App / Development

.:: Startup Inc / API Project : Onboarding
2025-01-15 13:00, quick side task         # Uses Startup Inc / API Project / Onboarding

paus->

Parsed entries include client, project, and category fields populated according to these rules.

Usage

Command Line Interface

Parse a time log file and output structured JSON:

neamtime-log-parser --filePath path/to/timelog.tslog

Output includes:

  • Total reported time
  • Session count
  • Time report data
  • Individual time log entries with metadata
  • Processing errors (if any)

Programmatic API (Recommended)

The new high-level API provides a clean, type-safe interface with structured error handling:

import { parseTimeLog } from 'neamtime-log-parser';

// Parse a time log from string content
const result = parseTimeLog(content, {
  timezone: 'UTC', // optional, defaults to UTC
  includeProcessor: true, // optional, for advanced use
});

// Check status
if (result.status === 'OK') {
  console.log('✅ Parsed successfully!');
} else if (result.status === 'Warnings') {
  console.warn(`⚠️ Parsed with ${result.errorCount} warnings`);
  result.errors.forEach(err => console.warn(`  ${err.ref}: ${err.message}`));
} else {
  console.error('❌ Parsing failed:', result.errors[0].message);
}

// Access parsed data (available even with warnings)
console.log(`Total hours: ${result.metadata.totalHours}`);
console.log(`Sessions: ${result.metadata.sessionCount}`);

// Use time log entries
result.entries.forEach(entry => {
  console.log(`${entry.gmtTimestamp}: ${entry.hours}h - ${entry.text}`);
});

// Access processor for advanced use (if includeProcessor: true)
if (result.processor) {
  const markdown = result.processor.contentsWithTimeMarkers;
  const sessions = result.processor.sessions;
}

Parse from file:

import { parseTimeLogFile } from 'neamtime-log-parser';

// Automatically reads .tzFirst file if present
const result = await parseTimeLogFile('/path/to/timelog.tslog');

console.log(`Status: ${result.status}`);
console.log(`Entries: ${result.entries.length}`);

Type-safe error handling:

import type { ProcessingError } from 'neamtime-log-parser';

function handleErrors(errors: ProcessingError[]): void {
  errors.forEach(error => {
    console.log(`${error.ref}: ${error.message}`);

    if (error.sourceLine) {
      console.log(`  Line: ${error.sourceLine}`);
    }

    if (error.lineWithComment) {
      console.log(`  Entry: ${error.lineWithComment}`);
    }
  });
}

Legacy API (Still Supported)

The original class-based API continues to work for backward compatibility:

import { getProcessedTimeSpendingLog } from 'neamtime-log-parser';

// Parse a time log file
const processed = getProcessedTimeSpendingLog('/path/to/timelog.tslog');

// Get total time
const totalTime = processed.calculateTotalReportedTime();

// Get time log entries
const entries = processed.getTimeLogEntriesWithMetadata();

// Get processing errors
const errors = processed.getProcessingErrors();

// Get time report data
const timeLogProcessor = processed.getTimeLogProcessor();
const reportData = timeLogProcessor.timeReportData;
const sessions = timeLogProcessor.sessions;

Advanced Usage

Finding Time Log Files in a Folder:

import { timeSpendingLogPathsInFolder } from 'neamtime-log-parser';

// Get all .tslog files in a directory
const logPaths = timeSpendingLogPathsInFolder('/path/to/logs');

// Process each log
for (const logPath of Object.values(logPaths)) {
  const processed = getProcessedTimeSpendingLog(logPath);
  console.log(`Total time: ${processed.calculateTotalReportedTime()} minutes`);
}

Timezone Specification:

You can specify a timezone by creating a .tzFirst file alongside your .tslog file:

echo "Europe/Stockholm" > timelog.tslog.tzFirst

If not specified, UTC is used as the default timezone.

API Reference

Recommended API

parseTimeLog(content: string, options?: ParseOptions): TimeLogParseResult

Parse a time log from string content.

Options:

  • timezone?: string - Timezone to use (default: 'UTC')
  • includeTroubleshootingInfo?: boolean - Include troubleshooting info in result
  • includeProcessor?: boolean - Include the processor instance in result

Returns: TimeLogParseResult with:

  • success: boolean - Whether parsing completed without fatal errors
  • status: 'OK' | 'Warnings' | 'Failed' - Parse status
  • entries: TimeLogEntryWithMetadata[] - Parsed time log entries
  • metadata: ParseMetadata - Statistics (totalHours, sessionCount, etc.)
  • errors: ProcessingError[] - Any errors encountered
  • errorCount: number - Number of errors
  • processor?: TimeLogProcessor - Raw processor (if includeProcessor: true)
  • troubleshootingInfo?: any - Debug info (if includeTroubleshootingInfo: true)

parseTimeLogFile(filePath: string, options?: ParseOptions): Promise<TimeLogParseResult>

Parse a time log from a file. Automatically reads .tzFirst file if present.

Returns: Promise resolving to TimeLogParseResult

Type Definitions

TimeLogParseResult

interface TimeLogParseResult {
  success: boolean;
  status: 'OK' | 'Warnings' | 'Failed';
  entries: TimeLogEntryWithMetadata[];
  metadata: ParseMetadata;
  errors: ProcessingError[];
  errorCount: number;
  processor?: TimeLogProcessor;
  troubleshootingInfo?: any;
}

ParseMetadata

interface ParseMetadata {
  totalHours: number;
  sessionCount: number;
  processedLines: number;
  oldestTimestamp?: Date;
  mostRecentTimestamp?: Date;
  leadTimeHours?: number;
  name?: string;
}

ProcessingError

interface ProcessingError {
  ref: string;              // Error reference identifier
  message: string;          // Human-readable error message
  data?: any;              // Additional context data
  sourceLine?: number;     // Source line number
  dateRaw?: string;        // Raw date entry
  lineWithComment?: string; // Log entry line
  log?: string;            // Error log details
}

TimeLogEntryWithMetadata

interface TimeLogEntryWithMetadata {
  gmtTimestamp: string;     // UTC timestamp
  category: string;         // Work category
  client?: string;          // Client name (from frontmatter or .:: tag)
  project?: string;         // Project name (from frontmatter or .:: tag)
  date: string;             // Date string
  dateRaw: string;          // Raw date from log
  hours: number;            // Duration in hours
  hoursRounded: number;     // Rounded duration
  text: string;             // Entry description
  ts: number;               // Unix timestamp
  tz: string;               // Timezone
  sessionMeta: {
    session_ref: string;    // Session reference
    tzFirst: string;        // Session timezone
  };
}

TimeLogFrontmatter

interface TimeLogFrontmatter {
  client?: string;          // Default client for entries
  project?: string;         // Default project for entries
  default_category?: string; // Default category for entries
}

Utility Functions

parseTimeLogFrontmatter(content: string): TimeLogFrontmatter

Parse YAML frontmatter from raw time log content to extract client, project, and default_category.

parseCategoryTag(tag: string, defaults?: TimeLogFrontmatter): ParsedCategoryTag

Parse a .:: category tag using three-format rules:

  • "Category" → uses defaults for client/project
  • "Client / Project" → overrides client/project, uses default category
  • "Client / Project : Category" → full override

stripFrontmatter(content: string): string

Remove YAML frontmatter from content, returning just the log body.

Legacy API

getProcessedTimeSpendingLog(timeSpendingLogPath: string): ProcessedTimeSpendingLog

Parses a time spending log file and returns a processed log object.

timeSpendingLogPathsInFolder(pathToFolder: string): string[]

Finds all time spending log files (.tslog) in a folder, including subdirectories.

ProcessedTimeSpendingLog

The main result object with methods:

  • calculateTotalReportedTime(): number - Total time in minutes
  • getTimeLogEntriesWithMetadata(): TimeLogEntryWithMetadata[] - Individual entries
  • getProcessingErrors(): ProcessingError[] - Any errors encountered
  • getTroubleshootingInfo() - Metadata for debugging
  • getTimeLogProcessor(): TimeLogProcessor - Access to raw processor

TimeLogProcessor

Contains parsed data:

  • sessions: Session[] - Array of work sessions
  • timeReportData: object - Structured time report
  • timeReportCsv: string - CSV formatted report
  • nonEmptyPreprocessedLines(): string[] - Cleaned log lines

Examples

See the fixtures directory for example time log files.

Development

Building

npm run build

Testing

# Run tests (versioned fixtures only - suitable for CI/CD)
npm test

# Run tests including unversioned fixtures (if available locally)
npm run test:all

# Or set environment variable directly
INCLUDE_UNVERSIONED_FIXTURES=true npm test

Note: By default, tests skip unversioned fixtures that are not committed to git. This ensures tests pass in CI/CD environments and fresh clones. If you have unversioned fixtures locally (in directories matching *unversioned*), you can include them in tests by setting the INCLUDE_UNVERSIONED_FIXTURES environment variable.

Coverage

npm run cov

Release Process

Key Principles

  • Test before merging: Test features (npm pack + local install) on feature branches
  • Publish from main: Only publish to npm from main branch after PR merge
  • Tag releases: Create git tags for published versions (automated via bumpp)

Workflow

  1. Merge features: All features merged to main via GitHub PRs
  2. Release: On main branch, run npm run release
    • Validates you're on main branch
    • Runs all checks (lint, tests, build)
    • Creates and tests package locally
    • Interactive version bump (via bumpp)
    • Publishes to npm and creates git tag
    • Pushes tag to GitHub (triggers automated release notes)
  3. Bug fixes: If issues found, fix via PR then re-run npm run release

Testing Releases

Before publishing, test the package locally:

npm run release:test

This will:

  • Run all checks
  • Build the package
  • Install it globally from the tarball
  • Test the CLI works

GitHub Actions

The project uses GitHub Actions for:

  • CI: Runs on all PRs and pushes to main (tests on Node 18, 20, 22)

License

MIT © motin

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support