Skip to content

Time Analysis Patterns

Learn advanced patterns for analyzing and processing time periods using split, merge, and other operations.

Flexible Period Splitting

The split operation provides three powerful approaches for dividing periods:

Split by Count

Divide any period into equal parts:

typescript
import { createTemporal, split, period } from '@allystudio/usetemporal'

const temporal = createTemporal({ date: new Date() })

// Split a quarter into 3 equal parts
const quarter = period(
  temporal,
  {
    start: new Date('2024-01-01'),
    end: new Date('2024-03-31')
  }
)

const thirds = split(temporal, quarter, { count: 3 })
// Result: 3 custom periods of ~30 days each

// Split a day into 4 parts (6-hour chunks)
const day = temporal.period( new Date(), 'day')
const quarters = split(temporal, day, { count: 4 })
// Result: 4 custom periods of 6 hours each

Split by Duration

Create chunks of specific duration:

typescript
// Split a year into 2-month chunks
const year = temporal.period( new Date(), 'year')
const bimonthly = split(temporal, year, { 
  duration: { months: 2 } 
})
// Result: 6 periods of 2 months each

// Split a month into weekly chunks
const month = temporal.period( new Date(), 'month')
const weeks = split(temporal, month, { 
  duration: { weeks: 1 } 
})
// Result: 4-5 week periods

// Split a day into 90-minute intervals
const day = temporal.period( new Date(), 'day')
const intervals = split(temporal, day, { 
  duration: { hours: 1, minutes: 30 } 
})
// Result: 16 periods of 90 minutes each

Project Planning

Split project timelines into manageable phases:

typescript
// Split a project timeline into phases
const project = period(
  temporal,
  {
    start: new Date('2024-01-01'),
    end: new Date('2024-12-31')
  }
)

// Equal phases
const phases = split(temporal, project, { count: 5 })
// Result: 5 equal project phases

// Sprint-based
const sprints = split(temporal, project, { 
  duration: { weeks: 2 } 
})
// Result: ~26 two-week sprints

// Quarterly milestones
const quarters = split(temporal, project, { by: 'quarter' })

Data Aggregation

Process large date ranges in batches:

typescript
// Split data range for batch processing
const dataRange = period(
  temporal,
  {
    start: new Date('2023-01-01'),
    end: new Date('2023-12-31')
  }
)

// Process in monthly batches
const batches = split(temporal, dataRange, { by: 'month' })

for (const batch of batches) {
  await processDataForPeriod(batch)
}

// Or process in 10 equal chunks for parallel processing
const chunks = split(temporal, dataRange, { count: 10 })

await Promise.all(
  chunks.map(chunk => processDataForPeriod(chunk))
)

Time Series Analysis

Analyze trends over different time windows:

typescript
const yearData = period(
  temporal,
  {
    start: new Date('2023-01-01'),
    end: new Date('2023-12-31')
  }
)

// Weekly analysis
const weeks = split(temporal, yearData, { duration: { weeks: 1 } })
const weeklyAverages = weeks.map(week => ({
  period: week,
  average: calculateAverage(week)
}))

// Monthly comparison
const months = split(temporal, yearData, { by: 'month' })
const monthlyGrowth = months.map((month, i) => ({
  month: month,
  growth: i > 0 ? calculateGrowth(months[i-1], month) : 0
}))

// Quarterly reports
const quarters = split(temporal, yearData, { count: 4 })
quarters.forEach((quarter, i) => {
  generateQuarterlyReport(quarter, i + 1)
})

Handling Edge Cases

Uneven Splits

When splitting by count, the last period accounts for rounding:

typescript
const timePeriod = period(
  temporal,
  {
    start: new Date('2024-01-01'),
    end: new Date('2024-01-10') // 10 days
  }
)

const parts = split(temporal, timePeriod, { count: 3 })
// parts[0]: ~3.33 days
// parts[1]: ~3.33 days  
// parts[2]: ~3.34 days (includes remainder)

Duration Overflow

When splitting by duration, the last period may be shorter:

typescript
const month = temporal.period( new Date('2024-02-01'), 'month')
const weeks = split(temporal, month, { 
  duration: { weeks: 1 } 
})
// Last week may be partial (1-3 days in February)

Complex Durations

Multiple duration units are added together:

typescript
const period = split(temporal, year, { 
  duration: { 
    months: 1, 
    weeks: 2 
  } 
})
// Each chunk is 1 month + 2 weeks (~6 weeks)

Comparison: split vs divide

Featuredividesplit
Split by unit✓ (with by option)
Split by count
Split by duration
ReturnsStandard periodsCustom periods (except with by)
Use caseCalendar unitsFlexible divisions
typescript
// These are equivalent:
divide(temporal.adapter, year, 'month')
split(temporal, year, { by: 'month' })

// But split offers more:
split(temporal, year, { count: 10 })      // 10 equal parts
split(temporal, year, { duration: { days: 30 } }) // 30-day chunks

Merging Periods

Combine multiple periods into larger units:

typescript
// Merge weeks into a month
const weeks = divide(temporal.adapter, month, 'week')
const mergedMonth = merge(temporal.adapter, weeks)

// Combine business days
const workDays = days.filter(day => !isWeekend(day))
const workPeriod = merge(temporal.adapter, workDays)

Advanced Analysis Patterns

Rolling Windows

typescript
function rollingAverage(data: Period[], windowSize: number) {
  const results = []
  
  for (let i = windowSize - 1; i < data.length; i++) {
    const window = data.slice(i - windowSize + 1, i + 1)
    const merged = merge(temporal.adapter, window)
    
    results.push({
      period: merged,
      average: calculateAverage(merged)
    })
  }
  
  return results
}

// 7-day rolling average
const days = divide(temporal.adapter, month, 'day')
const rolling7Day = rollingAverage(days, 7)

Period Overlap Detection

typescript
function findOverlaps(periods: Period[]): Array<[Period, Period]> {
  const overlaps = []
  
  for (let i = 0; i < periods.length - 1; i++) {
    for (let j = i + 1; j < periods.length; j++) {
      if (contains(temporal.adapter, periods[i], periods[j].start) ||
          contains(temporal.adapter, periods[j], periods[i].start)) {
        overlaps.push([periods[i], periods[j]])
      }
    }
  }
  
  return overlaps
}

// Check meeting conflicts
const meetings = [/* array of meeting periods */]
const conflicts = findOverlaps(meetings)

Gap Analysis

typescript
function findGaps(periods: Period[], container: Period): Period[] {
  const sorted = periods.sort((a, b) => 
    a.start.getTime() - b.start.getTime()
  )
  
  const gaps = []
  let lastEnd = container.start
  
  for (const period of sorted) {
    if (period.start > lastEnd) {
      gaps.push(temporal.period({ start: lastEnd, end: period.start }))
    }
    lastEnd = period.end > lastEnd ? period.end : lastEnd
  }

  if (lastEnd < container.end) {
    gaps.push(temporal.period({ start: lastEnd, end: container.end }))
  }
  
  return gaps
}

// Find free time slots
const meetings = [/* array of meeting periods */]
const workDay = period(
  temporal,
  {
    start: new Date('2024-01-15T09:00:00'),
    end: new Date('2024-01-15T17:00:00')
  }
)
const freeSlots = findGaps(meetings, workDay)

See Also

Released under the MIT License.