import {Meta} from '@storybook/addon-docs/blocks'

<Meta title='@dxtmisha/functional/en/Classes/Datetime'/>

# Datetime Class

A class for working with dates that provides powerful formatting, manipulation, and localization capabilities. Integrates with geographic settings and supports various output formats with timezone and regional considerations.

## Key Features

- **Localized formatting** — automatic date formatting according to regional settings
- **Date manipulation** — convenient methods for changing and moving dates
- **Timezone support** — working with timezones and their offsets
- **Object cloning** — safe creation of date copies for operations
- **Flexible output formats** — support for various date representation types
- **Geo integration** — uses geographic settings for localization
- **Standard formats** — output in ISO and other standard formats
- **Change observation** — callback system for tracking changes

## Constructor

### `constructor`

Creates a Datetime instance with specified date, output type, and locale code.

**Parameters:**
- `date?: NumberOrStringOrDate` — date for processing (optional, defaults to current date)
- `type?: GeoDate` — date format type for output (defaults to 'date')
- `code?: string` — country and language code (defaults to Geo.getLocation())

```javascript
import { Datetime } from '@dxtmisha/functional'

// Create with current date
const now = new Datetime()

// Create with specific date
const birthday = new Datetime('1990-05-15')
const timestamp = new Datetime(1234567890000)

// With formatting settings
const fullDate = new Datetime('2023-12-25', 'full', 'en-US')
const timeOnly = new Datetime(new Date(), 'time', 'ru-RU')
```

## Getting Date Information

### `getIntl`

Returns an object for working with formatting.

```javascript
const datetime = new Datetime('2023-10-15', 'date', 'en-US')
const intl = datetime.getIntl()
// Returns GeoIntl instance for working with localization
```

### `getDate`

Returns the Date object.

```javascript
const datetime = new Datetime('2023-10-15')
const dateObj = datetime.getDate()
console.log(dateObj) // Date object with 2023-10-15
```

### `getType`

Returns the data output type.

```javascript
const datetime = new Datetime('2023-10-15', 'datetime')
console.log(datetime.getType()) // 'datetime'
```

### `getHoursType`

Returns the hour format (12 or 24-hour).

```javascript
const datetime = new Datetime()
console.log(datetime.getHoursType()) // '24' or '12'
```

### `getHour24`

Checks if 24-hour time format is used.

```javascript
const datetime = new Datetime()
console.log(datetime.getHour24()) // false by default
```

### `getFirstDayCode`

Returns the first day of the week code.

```javascript
const datetime = new Datetime('2023-10-15', 'date', 'en-US')
console.log(datetime.getFirstDayCode()) // 0 (Sunday) for US locale
// 0 - Sunday, 1 - Monday, 6 - Saturday
```

### `getMaxDay`

Returns the last day of the month.

```javascript
const datetime = new Datetime('2023-02-15') // February
console.log(datetime.getMaxDay()) // 28 for February 2023

const datetime2 = new Datetime('2023-01-15') // January
console.log(datetime2.getMaxDay()) // 31 for January
```

## Working with Date Components

### Getting Components

```javascript
const datetime = new Datetime('2023-10-15 14:30:45')

console.log(datetime.getYear())    // 2023
console.log(datetime.getMonth())   // 10 (months from 1)
console.log(datetime.getDay())     // 15
console.log(datetime.getHour())    // 14
console.log(datetime.getMinute())  // 30
console.log(datetime.getSecond())  // 45
```

### Setting Components

```javascript
const datetime = new Datetime()

datetime
  .setYear(2025)
  .setMonth(3)
  .setDay(20)
  .setHour(15)
  .setMinute(45)
  .setSecond(30)

console.log(datetime.locale()) // Formatted date
```

## Timezones

### `getTimeZoneOffset`

Returns the timezone offset in minutes.

```javascript
const datetime = new Datetime()
const offset = datetime.getTimeZoneOffset()
console.log(offset) // e.g., -180 for UTC+3
```

### `getTimeZone`

Returns the timezone in various formats.

```javascript
const datetime = new Datetime()

console.log(datetime.getTimeZone())        // '+03:00'
console.log(datetime.getTimeZone('hour'))  // '+3'
console.log(datetime.getTimeZone('minute')) // '180'
console.log(datetime.getTimeZone('RFC'))   // '+0300'
```

## Localized Formatting

### `locale`

Main method for formatting dates with locale consideration.

```javascript
const datetime = new Datetime('2023-12-25 15:30:00', 'full', 'en-US')

console.log(datetime.locale())                    // Full date and time
console.log(datetime.locale('date'))              // Date only
console.log(datetime.locale('time'))              // Time only
console.log(datetime.locale('year'))              // Year only
console.log(datetime.locale('month', 'short'))    // Short month name
```

### Specialized Formatting Methods

```javascript
const datetime = new Datetime('2023-12-25', 'date', 'en-US')

console.log(datetime.localeYear())              // '2023'
console.log(datetime.localeMonth())             // 'December'
console.log(datetime.localeMonth('short'))      // 'Dec'
console.log(datetime.localeDay())               // '25'
console.log(datetime.localeHour())              // current hour
console.log(datetime.localeMinute())            // current minute
console.log(datetime.localeSecond())            // current second
```

## Standard Formatting

### `standard`

Returns date in standard format (ISO-like).

```javascript
const datetime = new Datetime('2023-12-25 15:30:45')

console.log(datetime.standard())        // '2023-12-25T15:30:45+03:00'
console.log(datetime.standard(false))   // '2023-12-25T15:30:45' (without timezone)

// Different types
const timeOnly = new Datetime('2023-12-25 15:30:45', 'time')
console.log(timeOnly.standard())        // 'T15:30:45+03:00'

const dateOnly = new Datetime('2023-12-25', 'date')
console.log(dateOnly.standard())        // '2023-12-25'
```

## Date Movement

### Movement by Time Units

```javascript
const datetime = new Datetime('2023-10-15')

// Move by specified number of units
datetime.moveByYear(1)     // +1 year
datetime.moveByMonth(-2)   // -2 months
datetime.moveByDay(10)     // +10 days
datetime.moveByHour(5)     // +5 hours
datetime.moveByMinute(30)  // +30 minutes
datetime.moveBySecond(45)  // +45 seconds

console.log(datetime.locale()) // Resulting date
```

### Movement to Period Boundaries

```javascript
const datetime = new Datetime('2023-07-15')

// Months
datetime.moveMonthFirst()    // January 1st
datetime.moveMonthLast()     // December 31st
datetime.moveMonthNext()     // 1st of next month
datetime.moveMonthPrevious() // 1st of previous month

// Days of month
datetime.moveDayFirst()      // 1st of current month
datetime.moveDayLast()       // last day of current month
datetime.moveDayNext()       // next day
datetime.moveDayPrevious()   // previous day
```

### Working with Weeks

```javascript
const datetime = new Datetime('2023-07-15') // Saturday

// Movement within week
datetime.moveWeekdayFirst()  // Monday of current week
datetime.moveWeekdayLast()   // Sunday of current week

// Movement by weeks
datetime.moveWeekdayNext()     // Monday of next week
datetime.moveWeekdayPrevious() // Monday of previous week

// Weeks in month context
datetime.moveWeekdayFirstByMonth() // first day of first week of month
datetime.moveWeekdayLastByMonth()  // last day of last week of month
```

## Cloning and Creating Variants

### Basic Cloning

```javascript
const original = new Datetime('2023-10-15')

const dateClone = original.clone()        // Date object
const classClone = original.cloneClass()  // Datetime object
```

### Cloning with Movement

```javascript
const datetime = new Datetime('2023-07-15')

// Month cloning
const january = datetime.cloneMonthFirst()    // January of current year
const december = datetime.cloneMonthLast()    // December of current year
const nextMonth = datetime.cloneMonthNext()   // next month
const prevMonth = datetime.cloneMonthPrevious() // previous month

// Day cloning
const firstDay = datetime.cloneDayFirst()     // 1st of month
const lastDay = datetime.cloneDayLast()       // last day of month
const tomorrow = datetime.cloneDayNext()      // tomorrow
const yesterday = datetime.cloneDayPrevious() // yesterday

// Week cloning
const weekStart = datetime.cloneWeekdayFirst()    // start of week
const weekEnd = datetime.cloneWeekdayLast()       // end of week
const nextWeek = datetime.cloneWeekdayNext()      // next week
const prevWeek = datetime.cloneWeekdayPrevious()  // previous week
```

## Settings and Configuration

### `setType`

Changes the data output type.

```javascript
const datetime = new Datetime('2023-10-15')

datetime.setType('full')     // full date and time
datetime.setType('date')     // date only
datetime.setType('time')     // time only
datetime.setType('year')     // year only
```

### `setHour24`

Sets time format (12 or 24 hours).

```javascript
const datetime = new Datetime()

datetime.setHour24(true)   // 24-hour format
datetime.setHour24(false)  // 12-hour format
```

### `setCode`

Changes locale.

```javascript
const datetime = new Datetime()

datetime.setCode('en-US')  // American locale
datetime.setCode('ru-RU')  // Russian locale
datetime.setCode('de-DE')  // German locale
```

### `setWatch`

Sets a function to watch for changes.

```javascript
const datetime = new Datetime()

datetime.setWatch((date, type, hour24) => {
  console.log('Date changed:', date)
  console.log('Type:', type)
  console.log('24-hour format:', hour24)
})

datetime.setYear(2025) // Will trigger the watch function
```

### `setDate`

Completely changes the date.

```javascript
const datetime = new Datetime('2023-10-15')

// Set new date
datetime.setDate('2025-12-25')
datetime.setDate(new Date('2024-06-01'))
datetime.setDate(1672531200000) // timestamp

console.log(datetime.locale()) // New set date
```

## Data Types

### `GeoDate`

Type that defines the date and time display format. Supports various levels of detail from full format to individual components.

**Possible values:**
- `'full'` — full date and time (date + time)
- `'datetime'` — date and time
- `'date'` — date only
- `'year-month'` — year and month
- `'year'` — year only
- `'month'` — month only
- `'day'` — day only
- `'day-month'` — day and month
- `'time'` — time only
- `'hour-minute'` — hours and minutes
- `'hour'` — hours only
- `'minute'` — minutes only
- `'second'` — seconds only

```javascript
// Examples of using different types
const datetime = new Datetime('2023-12-25 15:30:45')

datetime.setType('full')        // 'December 25, 2023 at 3:30:45 PM'
datetime.setType('date')        // 'December 25, 2023'
datetime.setType('time')        // '3:30:45 PM'
datetime.setType('year')        // '2023'
datetime.setType('month')       // 'December'
datetime.setType('day')         // '25'
```

### `GeoFirstDay`

Type that defines the first day of the week in the calendar according to regional settings.

**Possible values:**
- `0` — Sunday (USA, Canada, Japan, etc.)
- `1` — Monday (most European countries, Russia)
- `6` — Saturday (some Middle Eastern countries)

```javascript
const datetime = new Datetime('2023-10-15', 'date', 'en-US')
console.log(datetime.getFirstDayCode()) // 0 (Sunday for USA)

const datetimeRU = new Datetime('2023-10-15', 'date', 'ru-RU')
console.log(datetimeRU.getFirstDayCode()) // 1 (Monday for Russia)
```

### `GeoHours`

Type that defines the time display format — 12-hour or 24-hour.

**Possible values:**
- `'12'` — 12-hour format with AM/PM
- `'24'` — 24-hour format

```javascript
const datetime = new Datetime('2023-12-25 15:30:00')

datetime.setHour24(true)   // 24-hour format
console.log(datetime.getHoursType()) // '24'
console.log(datetime.locale('time')) // '15:30'

datetime.setHour24(false)  // 12-hour format
console.log(datetime.getHoursType()) // '12'
console.log(datetime.locale('time')) // '3:30 PM'
```

### `GeoTimeZoneStyle`

Type that defines the style for displaying timezone information.

**Possible values:**
- `'minute'` — offset in minutes (number)
- `'hour'` — offset in hours with sign
- `'ISO8601'` — ISO 8601 standard (±HH:MM)
- `'RFC'` — RFC format (±HHMM)

```javascript
const datetime = new Datetime()

console.log(datetime.getTimeZone())          // '+03:00' (ISO8601 by default)
console.log(datetime.getTimeZone('hour'))    // '+3'
console.log(datetime.getTimeZone('minute'))  // '180'
console.log(datetime.getTimeZone('RFC'))     // '+0300'
```

## Practical Examples

### Calendar Operations

```javascript
class Calendar {
  constructor(date = new Date()) {
    this.current = new Datetime(date, 'date', 'en-US')
  }

  getCurrentMonth() {
    return {
      year: this.current.getYear(),
      month: this.current.getMonth(),
      monthName: this.current.localeMonth(),
      firstDay: this.current.cloneDayFirst().locale(),
      lastDay: this.current.cloneDayLast().locale()
    }
  }

  nextMonth() {
    this.current.moveMonthNext()
    return this.getCurrentMonth()
  }

  previousMonth() {
    this.current.moveMonthPrevious()
    return this.getCurrentMonth()
  }
}

// Usage
const calendar = new Calendar()
console.log(calendar.getCurrentMonth())
```

### Date Formatting

```javascript
const formatDates = {
  formatForLocales(date, locales) {
    return locales.map(locale => {
      const datetime = new Datetime(date, 'full', locale)
      return {
        locale,
        formatted: datetime.locale(),
        standard: datetime.standard()
      }
    })
  }
}

// Usage
const formatted = formatDates.formatForLocales('2023-12-25', ['en-US', 'ru-RU'])
console.log(formatted)
```

### Business Days

```javascript
class BusinessDays {
  constructor(startDate, locale = 'en-US') {
    this.start = new Datetime(startDate, 'date', locale)
  }

  isWeekend(date) {
    const dayOfWeek = date.getDate().getDay()
    return dayOfWeek === 0 || dayOfWeek === 6
  }

  addBusinessDays(days) {
    const result = this.start.cloneClass()
    let addedDays = 0

    while (addedDays < days) {
      result.moveDayNext()
      if (!this.isWeekend(result)) {
        addedDays++
      }
    }

    return result
  }
}

// Usage
const businessDays = new BusinessDays('2023-10-01')
const futureDate = businessDays.addBusinessDays(10)
console.log('10 business days later:', futureDate.locale())
```

The Datetime class provides a powerful and flexible tool for working with dates in JavaScript applications, supporting localization, manipulation, and formatting according to regional settings and application requirements.

## Internal Methods

### `update` (protected method)

Internal method for updating all values and calling the watch function.

```javascript
// This method is called automatically when the date changes
// It's not available for direct use as it's a protected method
// Calls the watch function if it was set via setWatch()
```

**Note:** This method is automatically called by all date modification methods (`setYear`, `setMonth`, `setDay`, `setHour`, `setMinute`, `setSecond`, `setDate`, `setType`, `setHour24`) to ensure state consistency and notify observers about changes.
