Skip to content

Can Chi

A Vietnamese Can Chi (Thiên Can – Địa Chi) engine that deterministically computes the sexagenary day and hour pillars from a birth datetime — pure calendar arithmetic, no ephemeris. Each branch carries its con giáp zodiac animal (with the Vietnamese Cat and Water Buffalo). The day pillar shares Four Pillars' anchor; the year pillar (tuổi) is omitted because it rolls over at Tết.

from fortune_telling_core import Querent, ReadingRequest
from fortune_telling_core.traditions.can_chi import CAN_CHI_DECK, CAN_CHI_SPREAD, build_engine

request = ReadingRequest(
    deck_id=CAN_CHI_DECK.id,
    spread_id=CAN_CHI_SPREAD.id,
    querent=Querent(
        id="sample",
        display_name="Sample",
        attributes={"birth_datetime": "1984-02-02T12:00:00+07:00"},
    ),
)
reading = build_engine().cast(request)

can_chi

CAN_CHI_DECK module-attribute

CAN_CHI_DECK = Deck(
    id="cc.deck.canchi.v1",
    symbols=(
        *(
            (
                Symbol(
                    id=can.symbol_id,
                    name=can.name,
                    attributes={
                        "kind": "can",
                        "slug": can.slug,
                        "cycle_index": str(can.index),
                        "element": can.element,
                        "polarity": can.polarity,
                    },
                )
            )
            for can in CANS
        ),
        *(
            (
                Symbol(
                    id=chi.symbol_id,
                    name=chi.name,
                    attributes={
                        "kind": "chi",
                        "slug": chi.slug,
                        "cycle_index": str(chi.index),
                        "animal": chi.animal,
                        "animal_vi": chi.animal_vi,
                        "element": chi.element,
                        "polarity": chi.polarity,
                    },
                )
            )
            for chi in CHIS
        ),
    ),
)

CAN_CHI_SPREAD module-attribute

CAN_CHI_SPREAD = Spread(
    id="cc.spread.dayhour.v1",
    name="Can Chi",
    positions=(
        Position(
            "day_can",
            "Day Can",
            "Heavenly stem of the day pillar.",
        ),
        Position(
            "day_chi",
            "Day Chi",
            "Earthly branch of the day pillar.",
        ),
        Position(
            "hour_can",
            "Hour Can",
            "Heavenly stem of the hour pillar.",
        ),
        Position(
            "hour_chi",
            "Hour Chi",
            "Earthly branch of the hour pillar.",
        ),
    ),
)

DayBoundary

Bases: StrEnum

When the Can Chi day pillar rolls over.

The hour of Tý (rat) spans 23:00-01:00. Under LATE_TY the 23:00 hour is treated as the first hour of the next day, advancing the day pillar; under the default MIDNIGHT the day pillar follows the civil calendar date.

MIDNIGHT class-attribute instance-attribute

MIDNIGHT = 'midnight'

LATE_TY class-attribute instance-attribute

LATE_TY = 'late_ty'

CanChiEngine

CanChiEngine(
    *, day_boundary: DayBoundary = DayBoundary.MIDNIGHT
)

Bases: AbstractEngine

Vietnamese Can Chi (Thiên Can - Địa Chi) day and hour pillar engine.

The engine deterministically computes the sexagenary day and hour pillars from a birth datetime using pure calendar arithmetic — no ephemeris — and records each stem and branch with its element, polarity, and con giáp animal. It shares Four Pillars' day anchor, so the two agree on every day.

The year pillar (tuổi con giáp) is intentionally omitted: it rolls over at Tết, the lunar new year, which this dependency-free engine cannot locate without a lunar calendar.

Parameters:

Name Type Description Default
day_boundary DayBoundary

Default day-rollover rule used when the request does not specify day_boundary.

MIDNIGHT
Source code in src/fortune_telling_core/traditions/can_chi/engine.py
def __init__(self, *, day_boundary: DayBoundary = DayBoundary.MIDNIGHT) -> None:
    self.day_boundary = day_boundary

id class-attribute instance-attribute

id = 'canchi.engine'

version class-attribute instance-attribute

version = '0.1.0'

day_boundary instance-attribute

day_boundary = day_boundary

deck

deck(request: ReadingRequest) -> Deck

Return the Can Chi deck.

Parameters:

Name Type Description Default
request ReadingRequest

Reading request whose deck_id must match CAN_CHI_DECK.id.

required

Returns:

Type Description
Deck

The bundled Can Chi deck.

Raises:

Type Description
ValidationError

If the requested deck is unsupported.

Source code in src/fortune_telling_core/traditions/can_chi/engine.py
def deck(self, request: ReadingRequest) -> Deck:
    """Return the Can Chi deck.

    Args:
        request: Reading request whose ``deck_id`` must match
            ``CAN_CHI_DECK.id``.

    Returns:
        The bundled Can Chi deck.

    Raises:
        ValidationError: If the requested deck is unsupported.
    """

    if request.deck_id != CAN_CHI_DECK.id:
        raise ValidationError(f"unsupported Can Chi deck: {request.deck_id}")
    return CAN_CHI_DECK

spread

spread(request: ReadingRequest) -> Spread

Return the Can Chi spread.

Parameters:

Name Type Description Default
request ReadingRequest

Reading request whose spread_id must match CAN_CHI_SPREAD.id.

required

Returns:

Type Description
Spread

The bundled Can Chi spread.

Raises:

Type Description
ValidationError

If the requested spread is unsupported.

Source code in src/fortune_telling_core/traditions/can_chi/engine.py
def spread(self, request: ReadingRequest) -> Spread:
    """Return the Can Chi spread.

    Args:
        request: Reading request whose ``spread_id`` must match
            ``CAN_CHI_SPREAD.id``.

    Returns:
        The bundled Can Chi spread.

    Raises:
        ValidationError: If the requested spread is unsupported.
    """

    if request.spread_id != CAN_CHI_SPREAD.id:
        raise ValidationError(f"unsupported Can Chi spread: {request.spread_id}")
    return CAN_CHI_SPREAD

draw

draw(request: ReadingRequest, rng: Rng) -> Draw

Compute the day and hour Can Chi pillars as a deterministic draw.

Parameters:

Name Type Description Default
request ReadingRequest

Reading request with birth_datetime and optional day_boundary in options or querent attributes.

required
rng Rng

Ignored. The argument is present for Engine compatibility.

required

Returns:

Type Description
Draw

A draw with day and hour stem/branch selections.

Raises:

Type Description
ValidationError

If required birth data or options are invalid.

Source code in src/fortune_telling_core/traditions/can_chi/engine.py
def draw(self, request: ReadingRequest, rng: Rng) -> Draw:
    """Compute the day and hour Can Chi pillars as a deterministic draw.

    Args:
        request: Reading request with ``birth_datetime`` and optional
            ``day_boundary`` in options or querent attributes.
        rng: Ignored. The argument is present for ``Engine`` compatibility.

    Returns:
        A draw with day and hour stem/branch selections.

    Raises:
        ValidationError: If required birth data or options are invalid.
    """

    del rng
    birth = parse_birth_data(request, self.day_boundary)
    chart = compute_chart(birth.birth_datetime, birth.day_boundary)
    return draw_from_chart(chart, birth.day_boundary)

cast

cast(request: ReadingRequest) -> Reading

Compute a Can Chi reading without a caller RNG.

Parameters:

Name Type Description Default
request ReadingRequest

Reading request containing birth data and optional configuration.

required

Returns:

Type Description
Reading

A reading with day and hour pillar placements and summary text.

Raises:

Type Description
ValidationError

If required birth data or options are invalid.

Source code in src/fortune_telling_core/traditions/can_chi/engine.py
def cast(self, request: ReadingRequest) -> Reading:
    """Compute a Can Chi reading without a caller RNG.

    Args:
        request: Reading request containing birth data and optional
            configuration.

    Returns:
        A reading with day and hour pillar placements and summary text.

    Raises:
        ValidationError: If required birth data or options are invalid.
    """

    draw = self.draw(request, _NULL_RNG)
    return self._interpret(request, draw, rng=None)

build_engine

build_engine(
    *, day_boundary: DayBoundary = DayBoundary.MIDNIGHT
) -> CanChiEngine

Create a Can Chi engine.

Parameters:

Name Type Description Default
day_boundary DayBoundary

Default day-rollover rule.

MIDNIGHT

Returns:

Type Description
CanChiEngine

A new CanChiEngine instance.

Source code in src/fortune_telling_core/traditions/can_chi/engine.py
def build_engine(*, day_boundary: DayBoundary = DayBoundary.MIDNIGHT) -> CanChiEngine:
    """Create a Can Chi engine.

    Args:
        day_boundary: Default day-rollover rule.

    Returns:
        A new ``CanChiEngine`` instance.
    """

    return CanChiEngine(day_boundary=day_boundary)