diff --git a/.clang-format b/.clang-format index 463f128..90a197c 100644 --- a/.clang-format +++ b/.clang-format @@ -1,140 +1,189 @@ --- Language: Cpp +# BasedOnStyle: Microsoft DisableFormat: false -# GENERAL INDENTATION -UseTab: Never -TabWidth: 4 -IndentWidth: 4 - AccessModifierOffset: -2 - AlignAfterOpenBracket: BlockIndent AlignArrayOfStructures: Right + AlignConsecutiveAssignments: - Enabled: true - AcrossEmptyLines: true - AcrossComments: false - AlignCompound: false + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false AlignFunctionPointers: false - PadOperators: true + PadOperators: true + +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false + +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + AlignFunctionPointers: false + PadOperators: false + AlignConsecutiveMacros: - Enabled: true - AcrossEmptyLines: true - AcrossComments: false - AlignCompound: false + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false AlignFunctionPointers: false - PadOperators: false + PadOperators: false + +AlignConsecutiveShortCaseStatements: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCaseColons: false + AlignEscapedNewlines: Right -AlignOperands: Align +AlignOperands: Align + AlignTrailingComments: - Kind: Always - OverEmptyLines: 2 + Kind: Always + OverEmptyLines: 2 AllowAllArgumentsOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: true -AllowBreakBeforeNoexceptSpecifier: Always -AllowShortBlocksOnASingleLine: Never +AllowBreakBeforeNoexceptSpecifier: Never +AllowShortBlocksOnASingleLine: Empty AllowShortCaseLabelsOnASingleLine: false -AllowShortCompoundRequirementOnASingleLine: false +AllowShortCompoundRequirementOnASingleLine: true AllowShortEnumsOnASingleLine: true -AllowShortFunctionsOnASingleLine: InlineOnly +AllowShortFunctionsOnASingleLine: None AllowShortIfStatementsOnASingleLine: Never -AllowShortLambdasOnASingleLine: All +AllowShortLambdasOnASingleLine: Empty AllowShortLoopsOnASingleLine: false - -AlwaysBreakAfterReturnType: None +#AllowShortNamespacesOnASingleLine: true AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: true +# AttributeMacros: +# - __capability + BinPackArguments: false -BinPackParameters: false +BinPackParameters: true +BitFieldColonSpacing: Both BreakBeforeBraces: Custom BraceWrapping: - AfterCaseLabel: true - AfterClass: false + AfterCaseLabel: true + AfterClass: false AfterControlStatement: false - AfterEnum: false + AfterEnum: false AfterExternBlock: true - AfterFunction: true - AfterNamespace: true - AfterStruct: false - AfterUnion: false - BeforeCatch: false - BeforeElse: false + AfterFunction: true + AfterNamespace: true + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false BeforeLambdaBody: true - BeforeWhile: false - IndentBraces: false + BeforeWhile: false + IndentBraces: false SplitEmptyFunction: false SplitEmptyRecord: false - SplitEmptyNamespace: true + SplitEmptyNamespace: false + BreakAdjacentStringLiterals: true -BreakAfterAttributes: Never -#BreakAfterReturnType: Automatic +BreakAfterAttributes: Leave +BreakArrays: true BreakBeforeBinaryOperators: None BreakBeforeConceptDeclarations: Always +BreakBeforeInlineASMColon: OnlyMultiline BreakBeforeTernaryOperators: false BreakConstructorInitializers: BeforeColon BreakInheritanceList: AfterColon BreakStringLiterals: true - -ColumnLimit: 95 +ColumnLimit: 95 +CommentPragmas: '^ IWYU pragma:' CompactNamespaces: false ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 -Cpp11BracedListStyle: false - +Cpp11BracedListStyle: true DerivePointerAlignment: false - EmptyLineAfterAccessModifier: Never -EmptyLineBeforeAccessModifier: Always -ExperimentalAutoDetectBinPacking: true - +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false FixNamespaceComments: true -IncludeBlocks: Preserve +# ForEachMacros: +# - foreach +# - Q_FOREACH +# - BOOST_FOREACH +# IfMacros: +# - KJ_IF_MAYBE + +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false + +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IncludeBlocks: Preserve IndentAccessModifiers: false IndentCaseBlocks: false IndentCaseLabels: false IndentExternBlock: NoIndent -IndentGotoLabels: false -IndentPPDirectives: BeforeHash -IndentRequiresClause: false +IndentGotoLabels: true +IndentPPDirectives: AfterHash +IndentRequiresClause: true +IndentWidth: 4 IndentWrappedFunctionNames: false -InsertBraces: false +InsertBraces: false +InsertNewlineAtEOF: false InsertTrailingCommas: None + IntegerLiteralSeparator: - Binary: 0 - Decimal: 3 - Hex: 4 + Binary: 0 + Decimal: 3 + Hex: 4 +KeepEmptyLinesAtTheStartOfBlocks: false +KeepEmptyLinesAtEOF: true LambdaBodyIndentation: Signature - +LineEnding: DeriveLF +MacroBlockBegin: '' +MacroBlockEnd: '' MaxEmptyLinesToKeep: 2 - -NamespaceIndentation: All - +NamespaceIndentation: None PackConstructorInitializers: BinPack PointerAlignment: Left -PPIndentWidth: -1 - +PPIndentWidth: -1 QualifierAlignment: Leave - ReferenceAlignment: Pointer -ReflowComments: true +ReflowComments: true +RemoveBracesLLVM: false RemoveParentheses: Leave RemoveSemicolon: false -#RequiresClausePosition: OwnLineWithBrace +RequiresClausePosition: OwnLine RequiresExpressionIndentation: OuterScope - SeparateDefinitionBlocks: Always -ShortNamespaceLines: 3 -#SortIncludes: -# Enabled: true -# IgnoreCase: false +ShortNamespaceLines: 1 +SkipMacroDefinitionBody: false +SortIncludes: CaseSensitive SortUsingDeclarations: LexicographicNumeric SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false @@ -145,35 +194,67 @@ SpaceBeforeCaseColon: false SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true +SpaceBeforeJsonColon: false + SpaceBeforeParens: Custom SpaceBeforeParensOptions: AfterControlStatements: true AfterForeachMacros: true AfterFunctionDefinitionName: false AfterFunctionDeclarationName: false - AfterIfMacros: true + AfterIfMacros: true AfterOverloadedOperator: false AfterPlacementOperator: true AfterRequiresInClause: false AfterRequiresInExpression: false BeforeNonEmptyParentheses: false + SpaceBeforeRangeBasedForLoopColon: true SpaceBeforeSquareBrackets: false SpaceInEmptyBlock: true SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInContainerLiterals: true -# PENALTIES: -PenaltyReturnTypeOnItsOwnLine: 10000 -PenaltyBreakTemplateDeclaration: 10000 -#PenaltyBreakBeforeMemberAccess: 10000 -PenaltyBreakAssignment: 800 -PenaltyBreakScopeResolution: 500 +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 + +SpacesInParens: Never +SpacesInParensOptions: + InCStyleCasts: false + InConditionalStatements: false + InEmptyParentheses: false + Other: false + +SpacesInSquareBrackets: false +Standard: Latest +TabWidth: 4 +UseTab: Never +VerilogBreakBetweenInstancePorts: true + +# StatementAttributeLikeMacros: +# - Q_EMIT + +# StatementMacros: +# - Q_UNUSED +# - QT_REQUIRE_VERSION + +# WhitespaceSensitiveMacros: +# - BOOST_PP_STRINGIZE +# - CF_SWIFT_NAME +# - NS_SWIFT_NAME +# - PP_STRINGIZE +# - STRINGIZE + +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 10 PenaltyBreakComment: 500 PenaltyBreakFirstLessLess: 120 -PenaltyExcessCharacter: 100 -PenaltyBreakBeforeFirstCallParameter: 10 -PenaltyBreakString: 10 -PenaltyBreakOpenParenthesis: 10 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakScopeResolution: 500 +PenaltyBreakString: 100 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 PenaltyIndentedWhitespace: 0 - -... +PenaltyReturnTypeOnItsOwnLine: 10000 diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d995b5..e418f57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ set(PRJ_CXX_STANDARD 20) project( ${PRJ_NAME} - VERSION 0.0.0.0 + VERSION 0.1.0.0 LANGUAGES C CXX ASM ) @@ -96,7 +96,18 @@ include(linker/config) # Sources add_subdirectory(lib) -add_subdirectory(src) + +include(utility/project_tools) + +# Set library-wide C++ standard +use_project_cxx_standard(GLOBAL_CXX_OPTIONS INTERFACE) + +target_link_options( + GLOBAL_CXX_OPTIONS + + INTERFACE + ${GLOBAL_LINK_FLAGS} +) # C Interface Source if(CFFI_BUILD) diff --git a/README.md b/README.md index 0f2496f..8b403dc 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@
- +

diff --git a/cffi/CMakeLists.txt b/cffi/CMakeLists.txt index 70ebe30..ec9e722 100644 --- a/cffi/CMakeLists.txt +++ b/cffi/CMakeLists.txt @@ -11,6 +11,8 @@ # #==================================== +# TODO: Major changes needed here in due time... + set( PRJ_CFFI_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/include" diff --git a/cffi/include/simplydt/common/c_datetime_defs.h b/cffi/include/simplydt/common/c_datetime_defs.h index a641b9f..e1f284a 100644 --- a/cffi/include/simplydt/common/c_datetime_defs.h +++ b/cffi/include/simplydt/common/c_datetime_defs.h @@ -1,5 +1,5 @@ -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. // Released under the terms of the GNU Affero General Public License version 3. // [ISJTB-CXX-XL20230401-000001] diff --git a/cffi/include/simplydt/common/c_datetime_utils.h b/cffi/include/simplydt/common/c_datetime_utils.h index 773bbd5..4fc2557 100644 --- a/cffi/include/simplydt/common/c_datetime_utils.h +++ b/cffi/include/simplydt/common/c_datetime_utils.h @@ -1,5 +1,5 @@ -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. // Released under the terms of the GNU Affero General Public License version 3. // [ISJTB-CXX-XL20230401-000001] diff --git a/cffi/src/CMakeLists.txt b/cffi/src/CMakeLists.txt index cd7b28d..9a83e1b 100644 --- a/cffi/src/CMakeLists.txt +++ b/cffi/src/CMakeLists.txt @@ -3,6 +3,9 @@ # /cffi/src Directory Script #===================================== +# TODO: Major changes needed here in due time... +# (For now, disregard this entire cffi directory) + set(ORIGINAL_BINARY_NAME "${${PRJ_SCOPE}_MAIN_BINARY_NAME}") set(STATIC_CFFI_BINARY_NAME "${ORIGINAL_BINARY_NAME}_c") @@ -19,13 +22,6 @@ target_include_directories( ${PRJ_CFFI_INCLUDE_DIRS} ) -target_link_libraries( - CFFI_LIBRARY_INTERFACE - - INTERFACE - ${ORIGINAL_BINARY_NAME} -) - target_link_libraries( CFFI_LIBRARY_INTERFACE diff --git a/cffi/src/common/c_datetime_utils.cpp b/cffi/src/common/c_datetime_utils.cpp index 083b1f6..fef3257 100644 --- a/cffi/src/common/c_datetime_utils.cpp +++ b/cffi/src/common/c_datetime_utils.cpp @@ -1,5 +1,5 @@ -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. // Released under the terms of the GNU Affero General Public License version 3. // [ISJTB-CXX-XL20230401-000001] diff --git a/docs/Doxyfile b/docs/Doxyfile index 49bdb9b..c7f91b5 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = "Simply Datetime" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = v0.0.0.0 +PROJECT_NUMBER = v0.1.0.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/docs/prj/diagrams.drawio b/docs/prj/diagrams.drawio new file mode 100644 index 0000000..04d561a --- /dev/null +++ b/docs/prj/diagrams.drawio @@ -0,0 +1,4107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/ref/generate_docs.sh b/docs/ref/generate_docs old mode 100755 new mode 100644 similarity index 100% rename from docs/ref/generate_docs.sh rename to docs/ref/generate_docs diff --git a/include/simplydt/calendar/abstract_calendar.hpp b/include/simplydt/calendar/abstract_calendar.hpp new file mode 100644 index 0000000..4efda67 --- /dev/null +++ b/include/simplydt/calendar/abstract_calendar.hpp @@ -0,0 +1,687 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file abstract_calendar.hpp + * + * @brief + * Base calendar system interface declaration. + */ + + +#ifndef SIMPLYDT_LIB_BASE_CALENDAR_INTERFACE_H_ +#define SIMPLYDT_LIB_BASE_CALENDAR_INTERFACE_H_ + +#include "simplydt/common/simplydt_defs.hpp" +#include "simplydt/common/stl_chrono_defs.hpp" +#include "simplydt/common/stl_chrono_utils.hpp" +#include "simplydt/time/units/time_units.hpp" +#include +#include +#include + +namespace simplydt +{ + +/*! + * @brief + * Base calendar interface. + * + * @details + * This serves as the foundational interface for calendar + * systems in Simply Datetime. It is designed to be agnostic + * of any specific calendar implementation, allowing derived + * calendars to define their own characteristics. Note that + * the assumption is made that the calendar model in question + * utilizes year, month, and day values to describe a date in + * time. An implementation of this type focuses on conducting + * calendar operations on dates of type `Date_Impl` while + * obeying defined systemic calendar rules. Similar to other + * class hierarchies in Simply Datetime, this family uses the + * CRTP design pattern which allows this base to reference the + * derivative. Consequently, the convenience methods defined + * in this base structure depend on the concrete calendars + * public API for them to be well-formed. A derivative is + * expected to present the appropriate static attributes and + * methods, which can be verified by invoking the contract + * enforcement macro (`SIMPLYDT_ENFORCE_CALENDAR_CONTRACT`) + * just after the body of the implementation. Failing to have + * a compliant API can result in substitution errors or + * undefined behavior. Similarly, the provided date + * implementation must also satisfy its API requirements to + * avoid substitution errors or undefined behavior. This type + * is meant to present a stateless API and should not be + * constructable. It must be inherited by a concrete + * implementation that presents the expected (public) static + * API. + */ +template +struct CalendricalSystem { + /*! @brief Calendar system base class. */ + using Base = CalendricalSystem; + /*! @brief Calendar date. */ + using Date = Date_Impl; + /*! @brief Calendar year integer type. */ + using YearInt_t = typename Date::YearInt_t; + /*! @brief Calendar date validation policy. */ + using DatePolicy = typename Date::ValidationPolicy; + /*! @brief Enumeration of calendar months. */ + using Month = Month_Enum; + /*! @brief Enumeration of calendar days of week. */ + using DayOfWeek = DOW_Enum; + + /*! + * @brief + * Tag indicating timepoint should be interpreted + * without local timezone conversion. + * + * @details + * When passed to date conversion functions, this + * tag specifies that the input timepoint should + * be treated as a raw UTC value rather than being + * adjusted for the system's local timezone. This + * bypasses the operating system's timezone + * database and provides direct calendar date + * conversion based solely on the universal time + * count since the epoch. + */ + struct NonLocal { }; + + /*! + * @brief + * Returns name of calendar month by its numeric value. + * + * @details + * Expects a 1-based month number (1 = January ... 12 = + * December). If the month value is invalid for the + * current calendar implementation, the function returns + * a predefined invalid string literal. Month names are + * sourced from the derived calendar's `MONTH_NAMES` array. + * + * @return + * Calendar month name + */ + [[nodiscard]] static constexpr const char* getMonthName(const uint8_t month) noexcept + { + if (month == 0 || !Calendar_Impl::isValidMonth(month)) + return INVALID_LITERAL; + + const uint8_t monthIndex = month - 1; + return Calendar_Impl::MONTH_NAMES[monthIndex]; + } + + /*! + * @brief + * Returns name of calendar month from its enumeration + * representation. + * + * @details + * Accepts a month enumeration constant defined by the + * derived calendar implementation. The enumeration is + * expected to use zero-based indexing (0 = January ... + * 11 = December). If the month value is invalid for the + * current calendar, the function returns a predefined + * invalid string literal. Month names are sourced from + * the derived calendar's `MONTH_NAMES` array. + * + * @return + * Calendar month name + */ + [[nodiscard]] static constexpr const char* getMonthName(const Month month_repr) noexcept + { + const uint8_t monthIndex = static_cast(month_repr); + + if (!Calendar_Impl::isValidMonth(monthIndex + 1)) + return INVALID_LITERAL; + + return Calendar_Impl::MONTH_NAMES[monthIndex]; + } + + /*! + * @brief + * Returns month name of calendar date. + * + * @details + * Extracts month from calendar defined date implementation + * and returns its corresponding month name using the derived + * calendar's `MONTH_NAMES` array. + * + * @return + * Calendar month name + */ + [[nodiscard]] static constexpr const char* getMonthName(const Date date) noexcept + { + const uint8_t monthIndex = date.month() - 1; + return Calendar_Impl::MONTH_NAMES[monthIndex]; + } + + /*! + * @brief + * Returns abbreviated name of a calendar month by its numeric + * value. + * + * @details + * Expects a 1-based month number (1 = January ... 12 = December). + * If the month value is invalid for the current calendar + * implementation, the function returns a predefined invalid + * string literal. Month abbreviations are sourced from the + * derived calendar's `MONTH_ABBREVS` array. + * + * @return + * Abbreviated calendar month name + */ + [[nodiscard]] static constexpr std::string_view getMonthAbbrev(const uint8_t month + ) noexcept + { + if (month == 0 || !Calendar_Impl::isValidMonth(month)) + return std::string_view{INVALID_LITERAL}; + + const uint8_t monthIndex = month - 1; + return Calendar_Impl::MONTH_ABBREVS[monthIndex]; + } + + /*! + * @brief + * Returns abbreviated name of a calendar month from its + * enumeration representation. + * + * @details + * Accepts a month enumeration constant defined by the derived + * calendar implementation. The enumeration is expected to use + * zero-based indexing (0 = January ... 11 = December). If the + * month value is invalid for the current calendar, the function + * returns a predefined invalid string literal. Month abbreviations + * are sourced from the derived calendar's `MONTH_ABBREVS` array. + * + * @return + * Abbreviated calendar month name + */ + [[nodiscard]] static constexpr std::string_view getMonthAbbrev(const Month month_repr + ) noexcept + { + const uint8_t monthIndex = static_cast(month_repr); + + if (!Calendar_Impl::isValidMonth(monthIndex + 1)) + return std::string_view{INVALID_LITERAL}; + + return Calendar_Impl::MONTH_ABBREVS[monthIndex]; + } + + /*! + * @brief + * Returns abbreviated month name of calendar date. + * + * @details + * Extracts month from calendar defined date implementation + * and returns its corresponding abbreviated month name using + * the derived calendar's `MONTH_ABBREVS` array. + * + * @return + * Abbreviated calendar month name + */ + [[nodiscard]] static constexpr std::string_view getMonthAbbrev(const Date date) noexcept + { + const uint8_t monthIndex = date.month() - 1; + return Calendar_Impl::MONTH_ABBREVS[monthIndex]; + } + + /*! + * @brief + * Converts a numeric month value to its corresponding + * enumeration representation. + * + * @details + * Expects a 1-based month number (1 = January ... 12 = + * December). If the value is invalid for the current + * calendar implementation, the function returns a fallback + * enumeration value (0). The returned value uses zero-based + * indexing of the derived calendar's `Month` enumeration. + * + * @return + * Calendar month enum representation + */ + [[nodiscard]] static constexpr Month getMonthEnumRepr(const uint8_t month) noexcept + { + if (month == 0 || !Calendar_Impl::isValidMonth(month)) + return static_cast(0); // TODO: Fallback value not acceptable here... + + const uint8_t monthIndex = month - 1; + return static_cast(monthIndex); + } + + /*! + * @brief + * Converts a calendar date month to its corresponding + * enumeration representation. + * + * @details + * Extracts month from calendar defined date implementation and + * converts it to the corresponding zero-based `Month` + * enumeration defined by the derived calendar implementation. + * + * @return + * Calendar month enum representation + */ + [[nodiscard]] static constexpr Month getMonthEnumRepr(const Date date) noexcept + { + const uint8_t monthIndex = date.month() - 1; + return static_cast(monthIndex); + } + + /*! + * @brief + * Returns name of calendar day-of-week from its enumeration + * representation. + * + * @details + * Accepts a day-of-week enumeration constant defined by + * the derived calendar implementation. The enumeration + * is expected to use zero-based indexing (0 = Sunday ... + * 6 = Saturday). If the day-of-week value is invalid for + * the current calendar, the function returns a predefined + * invalid string literal. Calendar day-of-week names are + * sourced from the derived calendar's `DAY_OF_WEEK_NAMES` + * array. + * + * @return + * Calendar day-of-week name + */ + [[nodiscard]] static constexpr const char* getDayOfWeekName(const DayOfWeek dow_repr + ) noexcept + { + const uint8_t dowIndex = static_cast(dow_repr); + + if (!Calendar_Impl::isValidDOWIndex(dowIndex)) + return INVALID_LITERAL; + + return Calendar_Impl::DAY_OF_WEEK_NAMES[dowIndex]; + } + + /*! + * @brief + * Returns calendar date day-of-week name. + * + * @details + * Determines day-of-week index of the provided calendar + * date by calling the derived calendar implementation + * `getDayOfWeekIndex()` method. Returns the corresponding + * name from the calendar defined `DAY_OF_WEEK_NAMES` + * array, or a predefined invalid string literal if the + * date is not valid. + * + * @return + * Calendar day-of-week name + */ + [[nodiscard]] static constexpr const char* getDayOfWeekName(const Date date) noexcept + { + const uint8_t dowIndex = Calendar_Impl::getDayOfWeekIndex(date); + return Calendar_Impl::DAY_OF_WEEK_NAMES[dowIndex]; + } + + /*! + * @brief + * Returns abbreviated name of calendar day-of-week from its + * enumeration representation. + * + * @details + * Accepts a day-of-week enumeration constant defined by + * the derived calendar implementation. The enumeration is + * expected to use zero-based indexing (0 = Sunday ... 6 = + * Saturday). If the day-of-week value is invalid for the + * current calendar, the function returns a predefined + * invalid string literal. Day-of-week abbreviations are + * sourced from the derived calendar's `DAY_OF_WEEK_ABBREVS` + * array. + * + * @return + * Abbreviated calendar day-of-week name + */ + [[nodiscard]] static constexpr std::string_view getDayOfWeekAbbrev(const DayOfWeek dow_repr + ) noexcept + { + const uint8_t dowIndex = static_cast(dow_repr); + + if (!Calendar_Impl::isValidDOWIndex(dowIndex)) + return std::string_view{INVALID_LITERAL}; + + return Calendar_Impl::DAY_OF_WEEK_ABBREVS[dowIndex]; + } + + /*! + * @brief + * Returns abbreviated name of calendar day-of-week from its + * enumeration representation. + * + * @details + * Determines day-of-week index of the provided calendar + * date by calling the derived calendar implementation + * `getDayOfWeekIndex()` method. Returns the corresponding + * name from the calendar defined `DAY_OF_WEEK_ABBREVS` + * array, or a predefined invalid string literal if the + * date is not valid. + * + * @return + * Abbreviated calendar day-of-week name + */ + [[nodiscard]] static constexpr std::string_view getDayOfWeekAbbrev(const Date date + ) noexcept + { + if (!Calendar_Impl::isValidDate(date)) + return std::string_view{INVALID_LITERAL}; + + const uint8_t dowIndex = Calendar_Impl::getDayOfWeekIndex(date); + return Calendar_Impl::DAY_OF_WEEK_ABBREVS[dowIndex]; + } + + /*! + * @brief + * Converts a calendar date to its corresponding day-of-week + * enumeration representation. + * + * @details + * Validates the given `Date` instance and determines its + * day-of-week index using the derived calendar's + * `getDayOfWeekIndex()` method. Returns the corresponding + * day-of-week enumeration representation using the + * `DayOfWeek` enumeration defined by the derived calendar. + * + * @return + * Calendar day-of-week enum representation + */ + [[nodiscard]] static constexpr DayOfWeek getDayOfWeekEnumRepr(const Date date) noexcept + { + const uint8_t dowIndex = Calendar_Impl::getDayOfWeekIndex(date); + return static_cast(dowIndex); + } + + /*! + * @brief + * Create calendar date using a year, month, and day value. + * + * @brief + * Constructs a calendar defined date implementation using + * the provided date values. Checks validity of date values + * beforehand using the derived calendars `isValidDate()` + * method. The method returns a default constructed calendar + * date if the provided date values were invalid for the + * current calendar. + * + * @return + * Calendar date + */ + [[nodiscard]] inline static constexpr Date getDate( + const YearInt_t year, const uint8_t month, const uint8_t day + ) noexcept + { + return Date{year, month, day}; + } + + /*! + * @brief + * Create calendar date using a year, month, and day value. + * + * @brief + * Constructs a calendar defined date implementation using + * the provided date values. Checks validity of date values + * beforehand using the derived calendars `isValidDate()` + * method. The method returns a default constructed calendar + * date if the provided date values were invalid for the + * current calendar. The month enumeration value is converted + * to a 1-based numerical month value for calculations. + * + * @return + * Calendar date + */ + [[nodiscard]] static constexpr Date getDate( + const YearInt_t year, const Month month, const uint8_t day + ) noexcept + { + const uint8_t numericMonth = static_cast(month) + 1; + return Date{year, numericMonth, day}; + } + + /*! + * @brief + * Create calendar date using local system clock. + * + * @details + * Interprets the given `stl::SystemTimePoint` as local time + * by converting it to a Unix timestamp and then populating + * a `std::tm` structure using the C++ standard library backed + * `stl::deriveLocalDateTimeFromTimestamp()` method. The + * resulting year, month, and day fields are used to construct + * and return the calendar defined date implementation. If the + * local date conversion fails a default constructed calendar + * date is returned. + * + * @return + * Calendar date + */ + [[nodiscard]] static Date getDate(const stl::SystemTimePoint time_point) noexcept + { + const stl::UnixTimestamp secsSinceEpoch = stl::SystemClock::to_time_t(time_point); + stl::CalendarDateTime dateBuffer{}; + + if (!stl::deriveLocalDateTimeFromTimestamp(&secsSinceEpoch, &dateBuffer)) + return Date{}; // Failed to interpret local date + + return Date{ + static_cast( + dateBuffer.tm_year + 1'900 + ), // tm_year measures years since 1900 + static_cast( + dateBuffer.tm_mon + 1 + ), // tm_mon measures months since January + static_cast(dateBuffer.tm_mday) + }; + } + + /*! + * @brief + * Create calendar date using system clock. + * + * @details + * When the `NonLocal` tag struct is supplied, the provided + * timepoint is interpreted without the assistance of the + * host OS (no time-zone applied). The OS has access to the + * necessary system settings that influence local time + * interpretation and must be consulted to get the systems + * true date (time-zone applied). + * + * @return + * Calendar date + */ + [[nodiscard]] static Date getDate( + const stl::SystemTimePoint time_point, const NonLocal + ) noexcept + { + const stl::UnixTimestamp secsSinceEpoch = stl::SystemClock::to_time_t(time_point); + return Calendar_Impl::fromUnixTimestamp(secsSinceEpoch); + } + + /*! + * @brief + * Returns next calendar date. + * + * @details + * Increments the provided date by 1 day. + * + * @return + * Calendar date + */ + [[nodiscard]] inline static constexpr Date getNextDate(const Date from_date) noexcept + { + return from_date + Days{1}; + } + + /*! + * @brief + * Finds next calendar date on specified day-of-week. + * + * @details + * This function determines the first calendar date that + * falls on the specified day of the week strictly after + * the provided `from_date`. The calculation uses the + * day-of-week index supplied by the derived calendar + * implementation and advances by at least one full week. + * + * @return + * Calendar date + */ + [[nodiscard]] static constexpr Date getNextDate( + const Date from_date, const DayOfWeek dow_repr + ) noexcept + { + constexpr uint8_t daysInWeek = + static_cast(Calendar_Impl::DAY_OF_WEEK_NAMES.size()); + const int8_t fromDate_dow = + static_cast(Calendar_Impl::getDayOfWeekIndex(from_date)); + const int8_t dowOffset = static_cast(dow_repr) - fromDate_dow; + return from_date + Days{dowOffset + daysInWeek}; + } + + /*! + * @brief + * Finds next calendar date within specified month. + * + * @details + * This function calculates the next date in the given + * `month_repr` relative to the provided `from_date`. If + * the target month has not yet occurred in the current + * year, the result is the first day of that month within + * the same year. If the target month has already passed, + * the result is the first day of that month in the + * following year. If the `from_date` is already within + * the target month, the function advances to the very + * next day, unless the date is the last day of the month, + * in which case it rolls over to the first day of the + * same month in the next year. + * + * @return + * Calendar date + */ + [[nodiscard]] static constexpr Date getNextDate( + const Date from_date, const Month month_repr + ) noexcept + { + const uint8_t fromNumericMonth = from_date.month(); + const uint8_t toNumericMonth = static_cast(month_repr) + 1; + + if (fromNumericMonth != toNumericMonth) { + if (fromNumericMonth > toNumericMonth) { + const YearInt_t nextYear = from_date.year() + 1; + return Date{nextYear, toNumericMonth, 1}; + } else { // fromNumericMonth < toNumericMonth + return Date{from_date.year(), toNumericMonth, 1}; + } + } + + const uint8_t daysInMonth = + Calendar_Impl::getDaysInMonth(from_date.year(), fromNumericMonth); + + if (from_date.day() != daysInMonth) + return from_date + Days{1}; + + const YearInt_t nextYear = from_date.year() + 1; + return Date{nextYear, fromNumericMonth, 1}; + } + + /*! + * @brief + * Returns last calendar date. + * + * @details + * Decrements the provided date by 1 day. + * + * @return + * Calendar date + */ + [[nodiscard]] inline static constexpr Date getLastDate(const Date from_date) noexcept + { + return from_date - Days{1}; + } + + /*! + * @brief + * Finds last calendar date on provided day-of-week. + * + * @details + * This function determines the first calendar date that + * falls on the specified day of the week strictly before + * the provided `from_date`. The calculation uses the + * day-of-week index supplied by the derived calendar + * implementation and decreases by at least one full week. + * + * @return + * Calendar date + */ + [[nodiscard]] static constexpr Date getLastDate( + const Date from_date, const DayOfWeek dow_repr + ) noexcept + { + constexpr uint8_t daysInWeek = + static_cast(Calendar_Impl::DAY_OF_WEEK_NAMES.size()); + const int8_t fromDate_dow = + static_cast(Calendar_Impl::getDayOfWeekIndex(from_date)); + const int8_t dowOffset = fromDate_dow - static_cast(dow_repr); + return from_date - Days{dowOffset + daysInWeek}; + } + + /*! + * @brief + * Finds last calendar date within specified month. + * + * @details + * This function calculates the last date in the given + * `month_repr` relative to the provided `from_date`. If + * the target month has not yet occurred in the current + * year, the result will be the last day of that month + * in the previous year. If the target month has already + * passed, the result is the last day of that month in + * the same year. If `from_date` is already within the + * target month, the function returns the day + * immediately preceding it, unless `from_date` is the + * first day of the month, in which case it rolls back + * to the last day of the same month in the previous year. + * + * @return + * Calendar date + */ + [[nodiscard]] static constexpr Date getLastDate( + const Date from_date, const Month month_repr + ) noexcept + { + const uint8_t fromNumericMonth = from_date.month(); + const uint8_t toNumericMonth = static_cast(month_repr) + 1; + uint8_t daysInMonth = Calendar_Impl::getDaysInMonth(from_date.year(), toNumericMonth); + + if (fromNumericMonth != toNumericMonth) { + if (fromNumericMonth > toNumericMonth) { + return Date{from_date.year(), toNumericMonth, daysInMonth}; + } else { // fromNumericMonth < toNumericMonth + const YearInt_t previousYear = from_date.year() - 1; + daysInMonth = Calendar_Impl::getDaysInMonth(previousYear, toNumericMonth); + return Date{previousYear, toNumericMonth, daysInMonth}; + } + } + + if (from_date.day() != 1) + return from_date - Days{1}; + + const YearInt_t previousYear = from_date.year() - 1; + daysInMonth = Calendar_Impl::getDaysInMonth(previousYear, toNumericMonth); + return Date{previousYear, toNumericMonth, daysInMonth}; + } + + private: + CalendricalSystem() = delete; + ~CalendricalSystem() = delete; + friend Calendar_Impl; +}; + +} // namespace simplydt + +#endif // SIMPLYDT_LIB_BASE_CALENDAR_INTERFACE_H_ diff --git a/include/simplydt/calendar/concepts/calendar_api_contract.hpp b/include/simplydt/calendar/concepts/calendar_api_contract.hpp new file mode 100644 index 0000000..fe2ff98 --- /dev/null +++ b/include/simplydt/calendar/concepts/calendar_api_contract.hpp @@ -0,0 +1,65 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file calendar_api_contract.hpp + * + * @brief + * Conceptual calendar implementation contract. + */ + + +#ifndef SIMPLYDT_LIB_CALENDAR_CONTRACT_CONCEPT_H_ +#define SIMPLYDT_LIB_CALENDAR_CONTRACT_CONCEPT_H_ + +#include "simplydt/calendar/concepts/calendar_concepts.hpp" +#include "simplydt/calendar/concepts/date_api_contract.hpp" + +namespace simplydt::concepts +{ + +/*! + * @brief + * Concept of a type that meets the criteria to be + * considered a useable calendar implementation. + * + * @details + * A valid calendar type must provide contextual + * nested types for library integration and utilize + * a date type that satisfies the date contract. + * It must include characteristic members defining + * calendar properties, name arrays for month and + * day identification, and comprehensive structure + * validation and query methods. The implementation + * must support date queries, standard date + * conversion operations, and calendar naming + * utilities, through static members. + */ +template +concept contract_abiding_calendar = requires { + requires calendar::has_contextual_nested_types; + requires contract_abiding_date; + requires calendar::has_characteristic_members; + requires calendar::has_calendar_name_arrays; + requires calendar::has_structure_validation_methods; + requires calendar::has_structure_query_methods; + requires calendar::has_date_query_methods; + requires calendar::has_standard_date_conversion_methods; + requires calendar::has_name_methods; +}; + +#ifndef SIMPLYDT_ENFORCE_CALENDAR_CONTRACT +/*! @brief Macro for asserting calendar implementation interface contract. */ +# define SIMPLYDT_ENFORCE_CALENDAR_CONTRACT(Class) \ + static_assert( \ + simplydt::concepts::contract_abiding_calendar, \ + #Class " implementation does not fulfill the public API contract." \ + ) +#endif + +} // namespace simplydt::concepts + +#endif // SIMPLYDT_LIB_CALENDAR_CONTRACT_CONCEPT_H_ diff --git a/include/simplydt/calendar/concepts/calendar_concepts.hpp b/include/simplydt/calendar/concepts/calendar_concepts.hpp new file mode 100644 index 0000000..2b60275 --- /dev/null +++ b/include/simplydt/calendar/concepts/calendar_concepts.hpp @@ -0,0 +1,293 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file calendar_concepts.hpp + * + * @brief + * Agnostic calendar concepts. + */ + + +#ifndef SIMPLYDT_LIB_CALENDAR_CONCEPTS_H_ +#define SIMPLYDT_LIB_CALENDAR_CONCEPTS_H_ + +#include "simplydt/calendar/concepts/date_concepts.hpp" +#include "simplydt/common/calendar_defs.hpp" +#include "simplydt/common/stl_chrono_defs.hpp" +#include + +/*! + * @namespace simplydt::concepts::calendar + * + * @brief + * Calendar concepts. + */ +namespace simplydt::concepts::calendar +{ + +template +concept has_contextual_nested_types = requires { + typename Calendar_Impl::YearInt_t; + typename Calendar_Impl::Date; + typename Calendar_Impl::Month; + typename Calendar_Impl::DayOfWeek; + typename Calendar_Impl::DatePolicy; + typename Calendar_Impl::NonLocal; +}; + +template +concept has_characteristic_members = requires { + { Calendar_Impl::calendar } -> std::same_as; + { Calendar_Impl::isSolarCalendar } -> std::same_as; + { Calendar_Impl::isLunarCalendar } -> std::same_as; + { Calendar_Impl::isLunisolarCalendar } -> std::same_as; +}; + +template +concept has_calendar_name_arrays = requires { + { + Calendar_Impl::MONTH_NAMES + } -> std::same_as>>&>; + + { + Calendar_Impl::MONTH_ABBREVS + } -> std::same_as>>&>; + + { + Calendar_Impl::DAY_OF_WEEK_NAMES + } -> std::same_as>>&>; + + { + Calendar_Impl::DAY_OF_WEEK_ABBREVS + } -> std::same_as>>&>; +}; + +template +concept has_structure_validation_methods = requires { + { + Calendar_Impl::isValidYear(std::declval()) + } -> std::same_as; + + { Calendar_Impl::isValidMonth(std::declval()) } -> std::same_as; + { Calendar_Impl::isValidDay(std::declval()) } -> std::same_as; + { Calendar_Impl::isValidDOWIndex(std::declval()) } -> std::same_as; + { Calendar_Impl::isValidWeekIndex(std::declval()) } -> std::same_as; + + { + Calendar_Impl::isValidDate( + std::declval(), + std::declval(), + std::declval() + ) + } -> std::same_as; +}; + +template +concept has_structure_query_methods = requires { + { + Calendar_Impl::getDaysInYear(std::declval()) + } -> std::same_as; + + { + Calendar_Impl::getDaysInMonth( + std::declval(), std::declval() + ) + } -> std::same_as; + + { + Calendar_Impl::getWeeksInMonth( + std::declval(), std::declval() + ) + } -> std::same_as; + + { + Calendar_Impl::getWeeksMonthSpans( + std::declval(), std::declval() + ) + } -> std::same_as; + + { + Calendar_Impl::getWeekIndex( + std::declval(), + std::declval(), + std::declval() + ) + } -> std::same_as; + + { + Calendar_Impl::getDayOfWeekIndex( + std::declval(), + std::declval(), + std::declval() + ) + } -> std::same_as; + + { + Calendar_Impl::getMonthEnumRepr(std::declval()) + } -> std::same_as; + + { + Calendar_Impl::getMonthEnumRepr(std::declval()) + } -> std::same_as; + + { + Calendar_Impl::getDayOfWeekEnumRepr(std::declval()) + } -> std::same_as; +}; + +template +concept has_date_query_methods = requires { + { + Calendar_Impl::getDate( + std::declval(), + std::declval(), + std::declval() + ) + } -> std::same_as; + + { + Calendar_Impl::getDate( + std::declval(), + std::declval(), + std::declval() + ) + } -> std::same_as; + + { + Calendar_Impl::getDate(std::declval()) + } -> std::same_as; + + { + Calendar_Impl::getDate( + std::declval(), typename Calendar_Impl::NonLocal{} + ) + } -> std::same_as; + + { + Calendar_Impl::getNextDate(std::declval()) + } -> std::same_as; + + { + Calendar_Impl::getNextDate( + std::declval(), + std::declval() + ) + } -> std::same_as; + + { + Calendar_Impl::getNextDate( + std::declval(), + std::declval() + ) + } -> std::same_as; + + { + Calendar_Impl::getLastDate(std::declval()) + } -> std::same_as; + + { + Calendar_Impl::getLastDate( + std::declval(), + std::declval() + ) + } -> std::same_as; + + { + Calendar_Impl::getLastDate( + std::declval(), + std::declval() + ) + } -> std::same_as; + + // TODO: Add `Calendar_Impl::getWeek()` at some point... + // (Requires generic `WeekDates` container alias) +}; + +template +concept has_standard_date_conversion_methods = requires { + { + Calendar_Impl::toDaysSinceEpoch( + std::declval(), + std::declval(), + std::declval() + ) + } -> std::same_as; + + { + Calendar_Impl::fromDaysSinceEpoch(std::declval()) + } -> std::same_as; + + { + Calendar_Impl::toUnixTimestamp( + std::declval(), + std::declval(), + std::declval() + ) + } -> std::same_as; + + { + Calendar_Impl::fromUnixTimestamp(std::declval()) + } -> std::same_as; + + { + Calendar_Impl::fromUnixTimestamp(std::declval()) + } -> std::same_as; +}; + +template +concept has_name_methods = requires { + { Calendar_Impl::getMonthName(std::declval()) } -> std::same_as; + + { + Calendar_Impl::getMonthName(std::declval()) + } -> std::same_as; + + { + Calendar_Impl::getMonthName(std::declval()) + } -> std::same_as; + + { + Calendar_Impl::getMonthAbbrev(std::declval()) + } -> std::same_as; + + { + Calendar_Impl::getMonthAbbrev(std::declval()) + } -> std::same_as; + + { + Calendar_Impl::getMonthAbbrev(std::declval()) + } -> std::same_as; + + { + Calendar_Impl::getDayOfWeekName(std::declval()) + } -> std::same_as; + + { + Calendar_Impl::getDayOfWeekName(std::declval()) + } -> std::same_as; + + { + Calendar_Impl::getDayOfWeekAbbrev(std::declval()) + } -> std::same_as; + + { + Calendar_Impl::getDayOfWeekAbbrev(std::declval()) + } -> std::same_as; +}; + +} // namespace simplydt::concepts::calendar + +#endif // SIMPLYDT_LIB_CALENDAR_CONCEPTS_H_ diff --git a/include/simplydt/calendar/concepts/date_api_contract.hpp b/include/simplydt/calendar/concepts/date_api_contract.hpp new file mode 100644 index 0000000..049d067 --- /dev/null +++ b/include/simplydt/calendar/concepts/date_api_contract.hpp @@ -0,0 +1,66 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file date_api_contract.hpp + * + * @brief + * Conceptual calendar date implementation contract. + */ + + +#ifndef SIMPLYDT_LIB_CALENDAR_DATE_CONTRACT_CONCEPT_H_ +#define SIMPLYDT_LIB_CALENDAR_DATE_CONTRACT_CONCEPT_H_ + +#include "simplydt/calendar/concepts/date_concepts.hpp" + +namespace simplydt::concepts +{ + +/*! + * @brief + * Concept of a type that meets the criteria to be + * considered a useable calendar date implementation. + * + * @details + * A valid date type must provide contextual nested + * types (for library internal-use), integer-based + * year representation, and component accessors for + * date parts. It must support logical and arithmetic + * operators, basic state and sequential evaluation + * methods, as well as stream output and string + * conversion utilities. In addition, it must satisfy + * fundamental C++ type requirements including default + * initialization, copyability, and destructibility. + */ +template +concept contract_abiding_date = requires { + requires date::has_contextual_nested_types; + requires std::is_integral_v; + requires date::has_date_component_methods; + requires date::has_logical_operators; + requires date::has_arithmetic_operators; + requires date::has_basic_state_methods; + requires date::has_sequential_evaluation_methods; + requires date::is_stream_out_compatible; + requires date::has_date_string_methods; + requires std::default_initializable; + requires std::copyable; + requires std::destructible; +}; + +} // namespace simplydt::concepts + +#ifndef SIMPLYDT_ENFORCE_DATE_CONTRACT +/*! @brief Macro for asserting date interface implementation contract. */ +# define SIMPLYDT_ENFORCE_DATE_CONTRACT(Class) \ + static_assert( \ + simplydt::concepts::contract_abiding_date, \ + #Class " implementation does not fulfill the public API contract." \ + ) +#endif + +#endif // SIMPLYDT_LIB_CALENDAR_DATE_CONTRACT_CONCEPT_H_ diff --git a/include/simplydt/calendar/concepts/date_concepts.hpp b/include/simplydt/calendar/concepts/date_concepts.hpp new file mode 100644 index 0000000..ea281e6 --- /dev/null +++ b/include/simplydt/calendar/concepts/date_concepts.hpp @@ -0,0 +1,106 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file date_concepts.hpp + * + * @brief + * Agnostic calendar date concepts. + */ + + +#ifndef SIMPLYDT_LIB_CALENDAR_DATE_CONCEPTS_H_ +#define SIMPLYDT_LIB_CALENDAR_DATE_CONCEPTS_H_ + +#include "simplydt/time/units/time_units.hpp" +#include +#include +#include + +/*! + * @namespace simplydt::concepts + * + * @brief + * Simply Datetime library concepts. + */ +namespace simplydt::concepts +{ } + +/*! + * @namespace simplydt::concepts::date + * + * @brief + * Calendar date concepts. + */ +namespace simplydt::concepts::date +{ + +template +concept has_date_component_methods = requires(const Date_Impl& d) { + { d.year() } -> std::same_as; + { d.month() } -> std::same_as; + { d.day() } -> std::same_as; +}; + +template +concept has_logical_operators = requires(const Date_Impl& d) { + { d == d } -> std::same_as; + { d < d } -> std::same_as; + { d > d } -> std::same_as; + { d <= d } -> std::same_as; + { d >= d } -> std::same_as; +}; + +template +concept has_arithmetic_operators = requires(Date_Impl& d, simplydt::Days days) { + { d + days } -> std::same_as; + { d - days } -> std::same_as; + { d - d } -> std::same_as; + { d += days } -> std::same_as; + { d -= days } -> std::same_as; + { ++d } -> std::same_as; + { d++ } -> std::same_as; + { --d } -> std::same_as; + { d-- } -> std::same_as; +}; + +template +concept has_basic_state_methods = requires(const Date_Impl& d) { + { d.isZero() } -> std::same_as; + { d.underlying() } -> std::same_as; + { d.units() } -> std::same_as; +}; + +template +concept is_stream_out_compatible = requires(std::ostream& os, const Date_Impl& d) { + { os << d } -> std::convertible_to; +}; + +template +concept has_sequential_evaluation_methods = requires(const Date_Impl& d) { + { d.isBefore(d) } -> std::same_as; + { d.isAfter(d) } -> std::same_as; + { d.isBetween(d, d) } -> std::same_as; + { d.daysUntil(d) } -> std::same_as; +}; + +template +concept has_date_string_methods = requires(const Date_Impl& d) { + //_// { d.monthStr() } -> std::same_as; + //_// { d.monthAbbrev() } -> std::same_as; + { d.toStr() } -> std::same_as; +}; + +template +concept has_contextual_nested_types = requires { + typename Date_Impl::Repr_Type; + typename Date_Impl::YearInt_t; + typename Date_Impl::ValidationPolicy; +}; + +} // namespace simplydt::concepts::date + +#endif // SIMPLYDT_LIB_CALENDAR_DATE_CONCEPTS_H_ diff --git a/include/simplydt/calendar/date/abstract_date.hpp b/include/simplydt/calendar/date/abstract_date.hpp new file mode 100644 index 0000000..8219e0e --- /dev/null +++ b/include/simplydt/calendar/date/abstract_date.hpp @@ -0,0 +1,356 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file abstract_date.hpp + * + * @brief + * Base serial calendar date interface declaration. + */ + + +#ifndef SIMPLYDT_LIB_BASE_SERIAL_CALENDAR_DATE_INTERFACE_H_ +#define SIMPLYDT_LIB_BASE_SERIAL_CALENDAR_DATE_INTERFACE_H_ + +#include "simplydt/time/units/time_units.hpp" +#include +#include + +namespace simplydt +{ + +/*! + * @brief + * Base calendar date interface. + * + * @details + * This is the foundational interface for serial calendar + * dates in Simply Datetime. It is agnostic of any specific + * calendar and allows derivatives to define their own + * implementation. Note that the assumption is made that + * the calendar model in question utilizes year, month, + * and day values to describe a date in time. This type + * is not meant to have calendar knowledge, its purpose + * is to point to a valid date on the calendar it is + * designed for. Simply Datetime relies on the object + * invariant that any constructed instance of this type + * represents a valid calendar date. Each implementation is + * responsible for enforcing this invariant to prevent bugs + * and undefined behavior. This structure timekeeps dates + * using a serial count of days relative to some epoch date. + * The provided underlying serial date representation type + * `Repr_T` is initialized in this base class but can also + * be accessed by the derived calendar date implementation. + * The class hierarchy implements the CRTP design pattern + * which allows this base to reference the derivative. + * Consequently, the convenience methods defined in this + * base structure depend on the concrete calendar dates + * public API for them to be well-formed. A derivative is + * expected to present the appropriate attributes and + * methods, which can be verified by invoking the contract + * enforcement macro (`SIMPLYDT_ENFORCE_DATE_CONTRACT`) + * just after the body of the implementation. Failing to + * have a compliant API can result in substitution errors + * or undefined behavior. This is not a self-constructable + * type, it must be inherited by a concrete implementation + * that presents the expected public API. + */ +template +struct SerialCalendarDate { + /*! @brief Calendar date base class. */ + using Base = SerialCalendarDate; + /*! @brief Calendar date validation policy. */ + using ValidationPolicy = Validation_Policy; + /*! @brief Underlying serial date type. */ + using Repr_Type = Repr_T; + /*! @brief Year integer type. */ + using YearInt_t = Year_T; + + /*! @brief Puts human-readable date string in output stream. */ + friend inline std::ostream& operator<<(std::ostream& os, const Base& serial_date) noexcept + { + os << serial_date.derivedImpl().toStr(); + return os; + } + + /*! @brief Evaluates equivalence of calendar dates. */ + [[nodiscard]] constexpr bool operator==(const Base& serial_date) const noexcept + { + return this->serialDays == serial_date.serialDays; + } + + /*! @brief Determines if left-hand side is sequentially before right-hand side. */ + [[nodiscard]] constexpr bool operator<(const Base& serial_date) const noexcept + { + return this->serialDays < serial_date.serialDays; + } + + /*! @brief Determines if left-hand side is sequentially after right-hand side. */ + [[nodiscard]] constexpr bool operator>(const Base& serial_date) const noexcept + { + return this->serialDays > serial_date.serialDays; + } + + [[nodiscard]] constexpr bool operator<=(const Base& serial_date) const noexcept + { + return this->serialDays <= serial_date.serialDays; + } + + [[nodiscard]] constexpr bool operator>=(const Base& serial_date) const noexcept + { + return this->serialDays >= serial_date.serialDays; + } + + /*! @brief Returns this calendar date with provided amount of days added. */ + [[nodiscard]] constexpr Date_Impl operator+(const Days days) const noexcept + { + Date_Impl displacedDate{this->derivedImpl()}; + displacedDate.serialDays += days.count(); + return displacedDate; + // TODO: This method is not safe nor complete... + // ('days' can underflow or overflow 'serialDays') + } + + /*! @brief Returns this calendar date with provided amount of days subtracted. */ + [[nodiscard]] constexpr Date_Impl operator-(const Days days) const noexcept + { + Date_Impl displacedDate{this->derivedImpl()}; + displacedDate.serialDays -= days.count(); + return displacedDate; + // TODO: This method is not safe nor complete... + // ('days' can underflow or overflow 'serialDays') + } + + /*! @brief Calculates number of days between calendar dates. */ + [[nodiscard]] constexpr Days operator-(const Date_Impl date) const noexcept + { + return Days{this->serialDays - date.serialDays}; + } + + /*! + * @brief + * Increments calendar date one day. + * + * @return + * Reference to self + */ + Date_Impl& operator++() noexcept + { + this->serialDays += 1; + return static_cast(*this); + // TODO: This method is not safe nor complete... + // ('this->serialDays' can overflow) + } + + /*! + * @brief + * Increments calendar date one day. + * + * @return + * Copy of previous calendar date + */ + [[nodiscard]] Date_Impl operator++(int) noexcept + { + Date_Impl previousDate{this->derivedImpl()}; + this->serialDays += 1; + return previousDate; + // TODO: This method is not safe nor complete... + // ('this->serialDays' can overflow) + } + + /*! @brief Adds provided amount of days to calendar date. */ + Date_Impl& operator+=(const Days days) noexcept + { + this->serialDays += days.count(); + return static_cast(*this); + // TODO: This method is not safe nor complete... + // ('days' can underflow or overflow 'this->serialDays') + } + + /*! + * @brief + * Decrements calendar date one day. + * + * @return + * Reference to self + */ + Date_Impl& operator--() noexcept + { + this->serialDays -= 1; + return static_cast(*this); + // TODO: This method is not safe nor complete... + // ('this->serialDays' can underflow) + } + + /*! + * @brief + * Decrements calendar date one day. + * + * @return + * Copy of previous calendar date + */ + [[nodiscard]] Date_Impl operator--(int) noexcept + { + Date_Impl previousDate{this->derivedImpl()}; + this->serialDays -= 1; + return previousDate; + // TODO: This method is not safe nor complete... + // ('this->serialDays' can underflow) + } + + /*! @brief Subtracts provided amount of days from calendar date. */ + Date_Impl& operator-=(const Days days) noexcept + { + this->serialDays -= days.count(); + return static_cast(*this); + // TODO: This method is not safe nor complete... + // ('days' can underflow or overflow 'this->serialDays') + } + + /*! + * @brief + * Determines if calendar date is epoch date. + * + * @return + * True if serial day count is zero + */ + [[nodiscard]] constexpr bool isZero() const noexcept + { + return this->serialDays == 0; + } + + /*! + * @brief + * Determines if date is sequentially before provided + * date. + * + * @return + * True if this date occurs before provided + */ + [[nodiscard]] constexpr bool isBefore(const Date_Impl date) const noexcept + { + return this->serialDays < date.serialDays; + } + + /*! + * @brief + * Determines if date is sequentially after provided + * date. + * + * @return + * True if this date occurs after provided + */ + [[nodiscard]] constexpr bool isAfter(const Date_Impl date) const noexcept + { + return this->serialDays > date.serialDays; + } + + /*! + * @brief + * Checks if this date falls within a specified + * date range. + * + * @details + * This function compares the serial day count of + * the current date instance against two provided + * dates, expressed in the same serial-day format. + * The comparison is inclusive, meaning the + * function returns true if this date is equal to + * either boundary date or lies strictly between + * them. + * + * @return + * True if date lies within inclusive date range + */ + [[nodiscard]] constexpr bool isBetween( + const Date_Impl start_date, const Date_Impl end_date + ) const noexcept + { + return start_date.serialDays <= this->serialDays && + this->serialDays <= end_date.serialDays; + } + + /*! + * @brief + * Calculates day difference between dates. + * + * @details + * This function calculates the signed number of + * days from the current date instance to the + * specified `date`. A positive result indicates + * that the given `date` occurs after this date, + * while a negative result indicates it occurs + * before. + * + * @return + * Days from this date to provided + */ + [[nodiscard]] constexpr Days daysUntil(const Date_Impl date) const noexcept + { + return Days{date.serialDays - this->serialDays}; + } + + /*! + * @brief + * Returns serial calendar date as explicit units + * of precision. + * + * @details + * This function exposes the underlying serial day + * count of the current date instance, wrapped in + * `Days` units. The value represents the total + * number of days elapsed since the calendar's + * defined epoch date. + * + * @return + * Serial day count + */ + [[nodiscard]] constexpr Days units() const noexcept + { + return Days{this->serialDays}; + } + + /*! + * @brief + * Returns constant reference to underlying serial + * day count. + * + * @return + * Constant reference to serial day count + */ + [[nodiscard]] constexpr const Repr_Type& underlying() const noexcept + { + return this->serialDays; + } + + private: + Repr_Type serialDays; ///< Serial day count + + /*! @brief Construct calendar date with serial day count. */ + constexpr SerialCalendarDate(const Repr_Type serialDayCount) noexcept + : serialDays{serialDayCount} + { } + + ~SerialCalendarDate() = default; + friend Date_Impl; + + /*! + * @brief + * Returns constant reference to this concrete derivative. + * + * @note + * Do not call this from the derived class, YOU are the + * `derivedImpl()` (a.k.a `this`) + */ + [[nodiscard]] constexpr const Date_Impl& derivedImpl() const noexcept + { + return static_cast(*this); + } +}; + +} // namespace simplydt + +#endif // SIMPLYDT_LIB_BASE_SERIAL_CALENDAR_DATE_INTERFACE_H_ diff --git a/include/simplydt/calendar/gregorian/date_validation.hpp b/include/simplydt/calendar/gregorian/date_validation.hpp new file mode 100644 index 0000000..150bd64 --- /dev/null +++ b/include/simplydt/calendar/gregorian/date_validation.hpp @@ -0,0 +1,162 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file date_validation.hpp + * + * @brief + * Gregorian calendar date validation policy declaration. + */ + + +#ifndef SIMPLYDT_LIB_GREGORIAN_DATE_VALIDATION_POLICY_H_ +#define SIMPLYDT_LIB_GREGORIAN_DATE_VALIDATION_POLICY_H_ + +#include "simplydt/calendar/gregorian/gregorian_defs.hpp" +#include + +namespace simplydt::gregorian +{ + +/*! + * @brief + * Gregorian calendar date validation policy. + * + * @details + * This is the Gregorian calendar date validator, which is + * responsible for determining if a given set of date params + * describe a real date on the Gregorian calendar. This + * structure serves no purpose beyond validating potential + * Gregorian calendar dates and only maintains functions + * necessarry for validation. + */ +struct DateValidationPolicy { + /*! + * @brief + * Determines if provided year is a leap year. + * + * @details + * This function checks if the specified year qualifies + * as a leap year under Gregorian calendar rules. A year + * is considered a leap year if it is divisible by 4, + * except for years divisible by 100 unless it is also + * divisible by 400. + * + * @return + * True if leap year + */ + [[nodiscard]] inline static constexpr bool isLeapYear(const Year_Type year) noexcept + { + return (year % 4) == 0 && (year % 100 != 0 || year % YEARS_IN_ERA == 0); + } + + /*! + * @brief + * Evaluates support of year value. + * + * @details + * This function returns true if the given year falls + * within the inclusive bounds defined by `YEAR_MINIMUM` + * and `YEAR_MAXIMUM` for the Gregorian calendar system. + * + * @return + * True if supported year + */ + [[nodiscard]] inline static constexpr bool isValidYear(const Year_Type year) noexcept + { + return year >= YEAR_MINIMUM && year <= YEAR_MAXIMUM; + } + + /*! + * @brief + * Checks if a month value is within valid range. + * + * @details + * This function returns true if the given month falls + * within the inclusive bounds defined by + * `MIN_MONTH_OF_YEAR` and `MAX_MONTH_OF_YEAR` for the + * Gregorian calendar system. + * + * @return + * True if valid numerical month + */ + [[nodiscard]] inline static constexpr bool isValidMonth(const uint8_t month) noexcept + { + return month >= MIN_MONTH_OF_YEAR && month <= MAX_MONTH_OF_YEAR; + } + + /*! + * @brief + * Calculates total number of days in a given month of + * a specified year. + * + * @details + * This function handles the varying lengths of months + * in the Gregorian calendar, including the special + * case of February during a leap year. The function + * first validates the year and month; if either is + * invalid, it returns 0 to indicate an unsupported or + * invalid date value. + * + * @return + * Total days in month + */ + [[nodiscard]] static constexpr uint8_t getDaysInMonth( + const Year_Type year, const uint8_t month + ) noexcept + { + if (!isValidYear(year) || !isValidMonth(month)) + return 0; // Unsupported or invalid + + switch (month) { + case February: + switch (isLeapYear(year)) { + case true: + return 29; + default: + return 28; + } + + case April: + case June: + case September: + case November: + return 30; + + // January, March, May, July, August, October, December + default: + return 31; + } + } + + /*! + * @brief + * Checks if given date values form a valid Gregorian + * date. + * + * @details + * This function returns true when the provided date + * parameters describe a real date on the Gregorian + * calendar. This is achieved by comparing the dates + * day to the total number of days in the month. If + * the provided year is not supported, the function + * returns false. + * + * @return + * True if date exists on calendar + */ + [[nodiscard]] static constexpr bool isValidDate( + const Year_Type year, const uint8_t month, const uint8_t day + ) noexcept + { + const uint8_t monthTotalDays = getDaysInMonth(year, month); + return monthTotalDays != 0 && day != 0 && day <= monthTotalDays; + } +}; + +} // namespace simplydt::gregorian + +#endif // SIMPLYDT_LIB_GREGORIAN_DATE_VALIDATION_POLICY_H_ diff --git a/include/simplydt/calendar/gregorian/gregorian_calendar.hpp b/include/simplydt/calendar/gregorian/gregorian_calendar.hpp new file mode 100644 index 0000000..b3ffb9b --- /dev/null +++ b/include/simplydt/calendar/gregorian/gregorian_calendar.hpp @@ -0,0 +1,952 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file gregorian_calendar.hpp + * + * @brief + * Gregorian calendar declaration. + */ + + +#ifndef SIMPLYDT_LIB_GREGORIAN_CALENDAR_H_ +#define SIMPLYDT_LIB_GREGORIAN_CALENDAR_H_ + +#include "simplydt/calendar/abstract_calendar.hpp" +#include "simplydt/calendar/concepts/calendar_api_contract.hpp" +#include "simplydt/calendar/gregorian/gregorian_date.hpp" + +namespace simplydt::gregorian +{ + +/*! + * @brief + * Gregorian calendar system. + * + * @details + * TODO: INCOMPLETE COMMENT!!! + */ +struct GregorianCalendar : + public CalendricalSystem { + /*! @brief Array of dates in one calendar week. */ + using WeekDates = std::array; + + /*! + * @brief + * Identifies calendar system represented by + * this implementation. + */ + static constexpr CalendarSystem calendar = CalendarSystem::GREGORIAN; + + /*! + * @brief + * Indicates whether calendar system is + * solar-based. + * + * @details + * This constant specifies that the Gregorian + * calendar system follows a solar model, where + * date progression is based on the Earth's + * orbit around the Sun. + */ + static constexpr bool isSolarCalendar = true; + + /*! + * @brief + * Indicates whether calendar system is + * lunar-based. + * + * @details + * This constant specifies that the Gregorian + * calendar system does not follow a lunar + * model, which bases months on the phases of + * the Moon. + */ + static constexpr bool isLunarCalendar = false; + + /*! + * @brief + * Indicates whether calendar system is + * lunisolar-based. + * + * @details + * This constant specifies that the Gregorian + * calendar system does not follow a lunisolar + * model, which combines solar and lunar cycles + * to structure months and years. + */ + static constexpr bool isLunisolarCalendar = false; + + /*! + * @brief + * Full names of Gregorian calendar months. + */ + static constexpr inline const std::array& MONTH_NAMES = + Months; + + /*! + * @brief + * Abbreviated names of Gregorian calendar + * months. + */ + static constexpr inline const std::array& MONTH_ABBREVS = + MonthAbbrevs; + + /*! + * @brief + * Full names of Gregorian calendar + * days-of-week. + */ + static constexpr inline const std::array& DAY_OF_WEEK_NAMES = + DaysOfWeek; + + /*! + * @brief + * Abbreviated names of Gregorian calendar + * days-of-week. + */ + static constexpr inline const std::array& + DAY_OF_WEEK_ABBREVS = DayOfWeekAbbrevs; + + /*! + * @brief + * Evaluate support of year value. + * + * @details + * This function returns true if the given year + * falls within the inclusive bounds defined by + * `YEAR_MINIMUM` and `YEAR_MAXIMUM` for the + * Gregorian calendar system. + * + * @return + * True if supported year value + */ + [[nodiscard]] static constexpr bool isValidYear(const YearInt_t year) noexcept + { + return DatePolicy::isValidYear(year); + } + + /*! + * @brief + * Determines whether a given year is a leap year. + * + * @details + * This function checks if the specified year + * qualifies as a leap year under Gregorian + * calendar rules. A year is considered a leap + * year if it is divisible by 4, except for + * years divisible by 100 unless it is also + * divisible by 400. + * + * @return + * True if leap year + */ + [[nodiscard]] static constexpr bool isLeapYear(const YearInt_t year) noexcept + { + return DatePolicy::isLeapYear(year); + } + + /*! + * @brief + * Determines whether a date is within a leap + * year. + * + * @return + * True if leap year + */ + [[nodiscard]] static constexpr bool isLeapYear(const Date date) noexcept + { + return DatePolicy::isLeapYear(date.year()); + } + + /*! + * @brief + * Returns total number of days in a given + * calendar year. + * + * @details + * Determines whether the specified year is a + * leap year and returns `DAYS_IN_LEAP_YEAR` or + * `DAYS_IN_YEAR` accordingly. If the year is + * unsupported the function returns 0. + * + * @return + * Total days in year + */ + [[nodiscard]] static constexpr uint16_t getDaysInYear(const YearInt_t year) noexcept + { + if (!DatePolicy::isValidYear(year)) + return 0; // Unsupported year + + return DatePolicy::isLeapYear(year) ? DAYS_IN_LEAP_YEAR : DAYS_IN_YEAR; + } + + /*! + * @brief + * Returns total number of days in a given + * calendar year. + * + * @details + * Extracts the year from the provided `Date` + * to determine the total number of days in + * the year. + * + * @return + * Total days in year + */ + [[nodiscard]] static constexpr uint16_t getDaysInYear(const Date date) noexcept + { + return DatePolicy::isLeapYear(date.year()) ? DAYS_IN_LEAP_YEAR : DAYS_IN_YEAR; + } + + /*! + * @brief + * Checks if numerical month value is within + * valid range. + * + * @details + * This function returns true if the given + * month falls within the inclusive bounds + * defined by `MIN_MONTH_OF_YEAR` and + * `MAX_MONTH_OF_YEAR` for the Gregorian + * calendar system. + * + * @return + * True if valid numerical month + */ + [[nodiscard]] static constexpr bool isValidMonth(const uint8_t month) noexcept + { + return DatePolicy::isValidMonth(month); + } + + /*! + * @brief + * Returns total number of days in a given + * month of a specified year. + * + * @details + * This function handles the varying lengths of + * months in the Gregorian calendar, including + * the special case of February during a leap + * year. The function first validates the year + * and month; if either is invalid, it returns 0 + * to indicate an unsupported or invalid date + * component. + * + * @return + * Total days in month + */ + [[nodiscard]] static constexpr uint8_t getDaysInMonth( + const YearInt_t year, const uint8_t month + ) noexcept + { + return DatePolicy::getDaysInMonth(year, month); + } + + /*! + * @brief + * Returns total number of days within month of + * a given date. + * + * @details + * Extracts the year and month from the provided + * `Date` to determine the total number of days + * in the dates month. + * + * @return + * Total days in month + */ + [[nodiscard]] static constexpr uint8_t getDaysInMonth(const Date date) noexcept + { + const DateTuple dateComponents = hinnant::fromDaysSinceEpoch(date.underlying()); + + return DatePolicy::getDaysInMonth( + std::get<0>(dateComponents), // Year + std::get<1>(dateComponents) // Month + ); + } + + /*! + * @brief + * Checks if day value is within valid range. + * + * @details + * This function returns true if the given day + * falls within the inclusive bounds defined by + * `MIN_DAY_OF_MONTH` and `MAX_DAY_OF_MONTH` for + * the Gregorian calendar system. + * + * @return + * True if valid day value + */ + [[nodiscard]] inline static constexpr bool isValidDay(const uint8_t day) noexcept + { + return day >= MIN_DAY_OF_MONTH && day <= MAX_DAY_OF_MONTH; + } + + /*! + * @brief + * Checks if week index is within valid range. + * + * @details + * Checks whether the given week index represents + * a valid calendar week, accounting for the fact + * that weeks are aligned to calendar boundaries + * rather than strictly contained within a single + * year. A week index is considered valid if it + * falls within the complete set of weeks that + * contain days from the current year, which may + * include partial weeks from adjacent years. + * This accommodates cases where the first week + * contains days from the previous December or + * the last week contains days from the following + * January. + * + * @return + * True if valid calendar week index + */ + [[nodiscard]] inline static constexpr bool isValidWeekIndex(const uint8_t week_index + ) noexcept + { + return week_index <= WEEKS_IN_YEAR; + } + + /*! + * @brief + * Checks if day-of-week index is within valid + * range. + * + * @details + * Accepts a zero-based day-of-week index + * (0 = Sunday ... 6 = Saturday). Returns true + * only if the index is less than `DAYS_IN_WEEK`. + * + * @return + * True if valid day-of-week index + */ + [[nodiscard]] inline static constexpr bool isValidDOWIndex(const uint8_t dow_index + ) noexcept + { + return dow_index < DAYS_IN_WEEK; + } + + /*! + * @brief + * Checks if given year, month, and day + * combination form a valid calendar date. + * + * @details + * This function returns true when the provided + * date parameters describe a real date on the + * Gregorian calendar. This is achieved by + * comparing the dates day to the total number + * of days in the month. If the provided year + * is not supported, the function returns false. + * + * @return + * True if date exists on calendar + */ + [[nodiscard]] static constexpr bool isValidDate( + const YearInt_t year, const uint8_t month, const uint8_t day + ) noexcept + { + return DatePolicy::isValidDate(year, month, day); + } + + /*! + * @brief + * Calculates day-of-week index of provided + * calendar date. + * + * @details + * Implements Tomohiko Sakamoto's algorithm to + * determine the day of the week for the + * specified year, month, and day combination. + * Returns a zero-based day-of-week index + * (0 = Sunday ... 6 = Saturday), or + * `INVALID_DOW_INDEX` if the provided date + * does not exist on the calendar. + * + * @return + * Day-of-week index + */ + [[nodiscard]] static constexpr uint8_t getDayOfWeekIndex( + YearInt_t year, uint8_t month, uint8_t day + ) noexcept + { + if (!DatePolicy::isValidDate(year, month, day)) + return INVALID_DOW_INDEX; // NOTE: This has to go... + + // CREDITS: Tomohiko Sakamoto + // Day-of-week index algorithm + year -= (month < March); // Extra days from leap year + // only affect March and later + const uint8_t monthIndex = month - 1; + const int index = + ((year + year / 4 - year / 100 + year / YEARS_IN_ERA + + sakamoto::MONTH_KEY[monthIndex] + day) % + DAYS_IN_WEEK); + + return static_cast(index); + } + + /*! + * @brief + * Calculates day-of-week index for a given + * calendar date. + * + * @details + * Extracts the year, month, and day from the + * given `Date` instance to compute the + * zero-based day-of-week index. + * + * @return + * Day-of-week index + */ + [[nodiscard]] static constexpr uint8_t getDayOfWeekIndex(const Date date) noexcept + { + const DateTuple dateComponents = hinnant::fromDaysSinceEpoch(date.underlying()); + + return getDayOfWeekIndex( + std::get<0>(dateComponents), // Year + std::get<1>(dateComponents), // Month + std::get<2>(dateComponents) // Day + ); + } + + /*! + * @brief + * Number of full 7-day weeks contained in + * specified month. + * + * @details + * Calculates how many full seven-day calendar + * weeks are in the provided month. Fractional + * week information is truncated (*floor*) and + * results are not dependent on current day of + * the week. Returns 0 if the month is invalid + * or if the provided year is unsupported. + * + * @return + * Number of full 7-day weeks in month + */ + [[nodiscard]] static constexpr uint8_t getWeeksInMonth( + const YearInt_t year, const uint8_t month + ) noexcept + { + const uint8_t monthTotalDays = DatePolicy::getDaysInMonth(year, month); + + if (monthTotalDays == 0) + return 0; // Unsupported year or invalid month + + uint8_t disqualifiedDays = ( + (SATURDAY - getDayOfWeekIndex(year, month, 1)) + + (getDayOfWeekIndex(year, month, monthTotalDays)) + 2 + ); + const uint8_t fullWeeks = static_cast( + (monthTotalDays - disqualifiedDays) / DAYS_IN_WEEK + ); + return fullWeeks; + } + + /*! + * @brief + * Number of full 7-day weeks contained in + * specified month. + * + * @details + * Extracts the year and month components from + * the provided `Date` instance to calculate + * how many full seven-day calendar weeks are + * in the provided month. Fractional week + * information is truncated. + * + * @return + * Number of full 7-day weeks in month + */ + [[nodiscard]] static constexpr uint8_t getWeeksInMonth(const Date date) noexcept + { + const DateTuple dateComponents = hinnant::fromDaysSinceEpoch(date.underlying()); + + return getWeeksInMonth( + std::get<0>(dateComponents), // Year + std::get<1>(dateComponents) // Month + ); + } + + /*! + * @brief + * Determines number of weeks a specified month + * spans over the calendar. + * + * @details + * Calculates how many full or partial weeks are + * needed to contain all days of the specified + * month in a standard calendar grid. The result + * depends on the day of the week the month starts + * on and the total number of days in the month. + * Returns 0 if the month is invalid or if the + * provided year is unsupported. + * + * @return + * Number of weeks month spans + */ + [[nodiscard]] static constexpr uint8_t getWeeksMonthSpans( + const YearInt_t year, const uint8_t month + ) noexcept + { + const uint8_t monthTotalDays = DatePolicy::getDaysInMonth(year, month); + + if (monthTotalDays == 0) + return 0; // Unsupported or invalid + + const uint8_t firstOfMonthDowIndex = getDayOfWeekIndex(year, month, 1); + const uint8_t monthCells = firstOfMonthDowIndex + monthTotalDays; + return (monthCells + 6) / DAYS_IN_WEEK; + } + + /*! + * @brief + * Determines number of weeks a specified month + * spans over the calendar. + * + * @details + * Extracts year and month components from the + * provided `Date` instance to calculates how + * many full or partial weeks are needed to + * contain all days of the specified month in + * a standard calendar grid. + * + * @return + * Number of weeks month spans + */ + [[nodiscard]] static constexpr uint8_t getWeeksMonthSpans(const Date date) noexcept + { + const DateTuple dateComponents = hinnant::fromDaysSinceEpoch(date.underlying()); + + return getWeeksMonthSpans( + std::get<0>(dateComponents), // Year + std::get<1>(dateComponents) // Month + ); + } + + /*! + * @brief + * Determines zero-based index of week for a + * specified date within its year. + * + * @details + * This function calculates which week of the year + * [0–52] the given Gregorian calendar date falls + * into. Weeks are defined relative to Sundays, + * with week 0 beginning on the first Sunday before + * or on January 1 of the given year. The weeks are + * calendar aligned. Consequently, the week index + * can range from [0-52] (inclusive) because the + * potential partial week(s) at the beginning and + * end of the year are taken into account. The index + * is obtained by computing the difference in days + * between the target date and that first Sunday, + * then dividing by the number of days in a week. If + * the provided year, month, or day values do not + * form a valid Gregorian date, the function returns + * index 0 as a fallback. + * + * @return + * Index of week within year [0 - 52] + */ + [[nodiscard]] static constexpr uint8_t getWeekIndex( + YearInt_t year, uint8_t month, uint8_t day + ) noexcept + { + if (!DatePolicy::isValidDate(year, month, day)) + return 0; // NOTE: Unexpected behavior? + + const int8_t sundayDiff = SUNDAY - getDayOfWeekIndex(year, January, 1); + const Days firstSunday{toDaysSinceEpoch(year, January, 1) + Days{sundayDiff}}; + const Days serialDate{toDaysSinceEpoch(year, month, day)}; + return static_cast((serialDate - firstSunday).count() / DAYS_IN_WEEK); + } + + /*! + * @brief + * Determines zero-based index of week for a + * specified date within its year. + * + * @details + * Extracts year, month, and day components from + * the provided `Date` instance to calculate which + * week of the year [0–52] the given Gregorian + * calendar date falls into. + * + * @return + * Index of week within year [0 - 52] + */ + [[nodiscard]] static constexpr uint8_t getWeekIndex(const Date date) noexcept + { + const DateTuple dateComponents = hinnant::fromDaysSinceEpoch(date.underlying()); + + return getWeekIndex( + std::get<0>(dateComponents), // Year + std::get<1>(dateComponents), // Month + std::get<2>(dateComponents) // Day + ); + } + + /*! + * @brief + * Converts a calendar date to serial number of + * days since Unix epoch. + * + * @details + * Uses Howard Hinnant’s civil date algorithm to + * convert a year, month, day combination into a + * signed day count relative to the Unix epoch + * (1970-01-01 = day 0). The result can be + * negative for dates before the epoch. + * + * @return + * Days since January 1, 1970 + */ + [[nodiscard]] static constexpr Days toDaysSinceEpoch( + YearInt_t year, uint8_t month, uint8_t day + ) noexcept + { + if (!DatePolicy::isValidDate(year, month, day)) + return Days{0}; // Epoch date + + return Days{static_cast(hinnant::toDaysSinceEpoch(year, month, day))}; + } + + /*! + * @brief + * Converts a calendar date to serial number of + * days since Unix epoch. + * + * @details + * Returns serial date count in explicit units of + * precision (days) using the provided `Date` + * instances `units()` function. + * + * @return + * Days since January 1, 1970 + */ + [[nodiscard]] inline static constexpr Days toDaysSinceEpoch(const Date date) noexcept + { + return date.units(); + } + + /*! + * @brief + * Converts a serial count of days since Unix + * epoch to a calendar date. + * + * @details + * Uses Howard Hinnant’s civil date algorithm + * to convert a signed day count relative to + * the Unix epoch (1970-01-01 = day 0) into a + * year, month, day combination. Returns a + * `GregorianDate` representing the calculated + * civil date. + * + * @return + * Gregorian calendar date + */ + [[nodiscard]] static constexpr Date fromDaysSinceEpoch(const Days serial_days) noexcept + { + DateTuple dateComponents = hinnant::fromDaysSinceEpoch(serial_days.count()); + + return Date{ + std::get<0>(dateComponents), // Year + std::get<1>(dateComponents), // Month + std::get<2>(dateComponents) // Day + }; + } + + /*! + * @brief + * Converts a calendar date to a Unix timestamp + * (seconds since epoch). + * + * @details + * Uses `toDaysSinceEpoch()` to calculate the + * number of days since the Unix epoch + * (1970-01-01) and multiplies by the number + * of seconds in a day to obtain the equivalent + * timestamp in seconds. The returned value is + * stored in `stl::UnixTimestamp` and may be + * negative for dates before the epoch. + * + * @return + * Unix timestamp + */ + [[nodiscard]] static constexpr stl::UnixTimestamp toUnixTimestamp( + const YearInt_t year, const uint8_t month, const uint8_t day + ) noexcept + { + return static_cast( + Seconds{toDaysSinceEpoch(year, month, day)}.count() + ); + } + + /*! + * @brief + * Converts a calendar date to a Unix timestamp + * (seconds since epoch). + * + * @details + * Extracts year, month, and day components from + * the provided `Date` instance to calculate the + * number of days since the Unix epoch (1970-01-01). + * + * @return + * Unix timestamp + */ + [[nodiscard]] static constexpr stl::UnixTimestamp toUnixTimestamp(const Date date) noexcept + { + const DateTuple dateComponents = hinnant::fromDaysSinceEpoch(date.underlying()); + + return toUnixTimestamp( + std::get<0>(dateComponents), // Year + std::get<1>(dateComponents), // Month + std::get<2>(dateComponents) // Day + ); + } + + /*! + * @brief + * Converts a Unix timestamp (seconds since + * epoch) to a calendar date. + * + * @details + * Divides the given `stl::UnixTimestamp` by the + * number of seconds in a day to convert seconds + * to whole days since the Unix epoch (1970-01-01), + * then calls `fromDaysSinceEpoch()` to obtain the + * corresponding calendar date. + * + * @return + * Gregorian calendar date + */ + [[nodiscard]] static constexpr Date fromUnixTimestamp(const stl::UnixTimestamp& timestamp + ) noexcept + { + return fromDaysSinceEpoch(Days{static_cast(timestamp / SECONDS_IN_DAY)}); + } + + /*! + * @brief + * Converts a serial count of seconds to + * a calendar date. + * + * @details + * Converts the provided serial seconds count + * timestamp to days, which is then interpreted + * using Howard Hinnants algorithm. + * + * @return + * Gregorian calendar date + */ + [[nodiscard]] static constexpr Date fromUnixTimestamp(const Seconds serial_secs) noexcept + { + return fromDaysSinceEpoch(duration_cast(serial_secs)); + } + + /*! + * @brief + * Finds next weekday [Monday – Friday] after a + * given date. + * + * @details + * This function advances the provided `from_date` + * to the next date that falls on a weekday + * (Monday through Friday). If the current date is + * Friday, the result is the following Monday + * (skipping the weekend). If the current date is + * Saturday or Sunday, the function advances to + * the upcoming Monday. For all other weekdays + * (Monday–Thursday), the result is simply the + * next calendar day. + * + * @return + * Next date between [Monday - Friday] + */ + [[nodiscard]] static constexpr Date getNextWeekday(const Date from_date) noexcept + { + const DayOfWeek fromDow = static_cast(getDayOfWeekIndex(from_date)); + + switch (fromDow) { + case FRIDAY: + return from_date + Days{3}; + + case SATURDAY: + return from_date + Days{2}; + + default: // SUNDAY - THURSDAY + return from_date + Days{1}; + } + } + + /*! + * @brief + * Finds most recent weekday [Monday – Friday] + * before a given date. + * + * @details + * This function rolls the provided `from_date` + * back to the last date that falls on a weekday + * (Monday through Friday). If the current date is + * Monday, the result is the previous Friday. If + * the current date is Sunday, the result is the + * previous Friday as well. For all other days + * (Tuesday through Saturday), the result is simply + * the preceding calendar day, provided it falls + * within the weekday range. + * + * @return + * Last date between [Monday - Friday] + */ + [[nodiscard]] static constexpr Date getLastWeekday(const Date from_date) noexcept + { + const DayOfWeek fromDow = static_cast(getDayOfWeekIndex(from_date)); + + switch (fromDow) { + case SUNDAY: + return from_date - Days{2}; + + case MONDAY: + return from_date - Days{3}; + + default: // TUESDAY - SATURDAY + return from_date - Days{1}; + } + } + + /*! + * @brief + * Finds next Saturday following a provided date. + * + * @details + * This function advances the provided `from_date` + * to the next occurrence of Saturday in the + * Gregorian calendar. If the given date already + * falls on a Saturday, the function returns the + * Saturday of the following week. For all other + * days, the function computes the offset to the + * upcoming Saturday and advances accordingly. + * + * @return + * Next Saturday date + */ + [[nodiscard]] static constexpr Date getNextWeekend(const Date from_date) noexcept + { + const uint8_t dowIndex = getDayOfWeekIndex(from_date); + return from_date + Days{((SATURDAY - dowIndex + DAYS_IN_WEEK - 1) % 7) + 1}; + } + + /*! + * @brief + * Finds last Saturday before provided date. + * + * @details + * This function decreases the provided `from_date` + * to the last occurrence of Saturday in the + * Gregorian calendar. If the given date already + * falls on a Saturday, the function returns the + * Saturday of the prior week. For all other + * days, the function computes the offset to the + * previous Saturday and decreases accordingly. + * + * @return + * Last Saturday date + */ + [[nodiscard]] static constexpr Date getLastWeekend(const Date from_date) noexcept + { + const uint8_t dowDiff = SATURDAY - getDayOfWeekIndex(from_date); + return from_date - Days{DAYS_IN_WEEK - dowDiff}; + } + + /*! + * @brief + * Finds all dates associated with provided week + * within a year. + * + * @details + * Gathers all 7 dates of a specified week within a + * year into an array and returns the results. + * Weeks are defined relative to Sundays, with week + * 0 beginning on the first Sunday before or on + * January 1 of the given year. The weeks are + * calendar aligned. Consequently, a request for the + * first and last week of a given year may result in + * dates from the previous or next year. If the + * provided year value is not supported or an invalid + * week index is provided, the function returns an + * array of epoch dates. + * + * @return + * Array of calendar week dates + */ + [[nodiscard]] static constexpr WeekDates getWeek( + const YearInt_t year, const uint8_t week_index + ) noexcept + { + WeekDates week{}; + + if (!isValidWeekIndex(week_index) || !DatePolicy::isValidYear(year)) + return week; // Invalid week index or unsupported year + + const Days serialStart{ + toDaysSinceEpoch(year, January, 1) + Days{week_index * DAYS_IN_WEEK} + }; + const DayOfWeek fromDow = static_cast(getDayOfWeekIndex(year, January, 1)); + + for (uint8_t dowIndex = SUNDAY; dowIndex < DAYS_IN_WEEK; dowIndex++) { + const int8_t dowDiff = dowIndex - fromDow; + week[dowIndex] = fromDaysSinceEpoch(Days{serialStart + Days{dowDiff}}); + } + + return week; + } + + /*! + * @brief + * Finds all dates associated with week containing + * provided date. + * + * @details + * Determines the full calendar week (Sunday through + * Saturday) that the provided date falls within by + * aligning to the date’s weekday and filling in + * surrounding dates. + * + * @return + * Array of calendar week dates + */ + [[nodiscard]] static constexpr WeekDates getWeek(const Date date) noexcept + { + const Days serialStart{toDaysSinceEpoch(date)}; + const DayOfWeek fromDow = static_cast(getDayOfWeekIndex(date)); + WeekDates week{}; + + for (uint8_t dowIndex = SUNDAY; dowIndex < DAYS_IN_WEEK; dowIndex++) { + const int8_t dowDiff = dowIndex - fromDow; + week[dowIndex] = fromDaysSinceEpoch(Days{serialStart + Days{dowDiff}}); + } + + return week; + } + + private: + GregorianCalendar() = delete; + ~GregorianCalendar() = delete; +}; + +SIMPLYDT_ENFORCE_CALENDAR_CONTRACT(GregorianCalendar); + +} // namespace simplydt::gregorian + +#endif // SIMPLYDT_LIB_GREGORIAN_CALENDAR_H_ diff --git a/include/simplydt/calendar/gregorian/gregorian_date.hpp b/include/simplydt/calendar/gregorian/gregorian_date.hpp new file mode 100644 index 0000000..090545c --- /dev/null +++ b/include/simplydt/calendar/gregorian/gregorian_date.hpp @@ -0,0 +1,191 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file gregorian_date.hpp + * + * @brief + * Gregorian calendar date declaration. + */ + + +#ifndef SIMPLYDT_LIB_SERIAL_GREGORIAN_CALENDAR_DATE_H_ +#define SIMPLYDT_LIB_SERIAL_GREGORIAN_CALENDAR_DATE_H_ + +#include "simplydt/calendar/concepts/date_api_contract.hpp" +#include "simplydt/calendar/date/abstract_date.hpp" +#include "simplydt/calendar/gregorian/date_validation.hpp" +#include "simplydt/calendar/gregorian/gregorian_defs.hpp" +#include "simplydt/calendar/gregorian/hinnant_algorithms.hpp" +#include "simplydt/common/string_utils.hpp" + +namespace simplydt::gregorian +{ + +/*! + * @brief + * Gregorian calendar date. + * + * @details + * TODO: INCOMPLETE COMMENT!!! + */ +struct GregorianDate : + public SerialCalendarDate { + /*! @brief Epoch date in serial days. */ + static constexpr Repr_Type SERIAL_EPOCH = 0; + + /*! + * @brief + * Intercepts invalid date constructor values. + * + * @details + * This function verifies that the provided year, + * month, and day values form a valid Gregorian + * calendar date according to the associated + * `ValidationPolicy`. If the date is valid, it is + * converted into its corresponding serial day + * representation relative to the Unix epoch + * (January 1, 1970). If the date is invalid, the + * function returns the Unix epoch (0) as a default. + * + * @return + * Provided date in serial days if valid, epoch + * otherwise + */ + static constexpr Repr_Type useDefaultIfInvalid( + const YearInt_t year, const uint8_t month, const uint8_t day + ) noexcept + { + if (!ValidationPolicy::isValidDate(year, month, day)) + return SERIAL_EPOCH; + + return hinnant::toDaysSinceEpoch(year, month, day); + } + + /*! + * @brief + * Construct Gregorian calendar date using year, + * month, and day values. + */ + constexpr GregorianDate( + const YearInt_t year, const uint8_t month, const uint8_t day + ) noexcept + : SerialCalendarDate{ + useDefaultIfInvalid(year, month, day) + } + { } + + /*! + * @brief + * Construct default Gregorian calendar date + * (epoch date). + */ + constexpr GregorianDate() noexcept + : SerialCalendarDate{ + SERIAL_EPOCH + } + { } + + ~GregorianDate() = default; + + /*! + * @brief + * Date year component. + * + * @details + * This function derives the year value from the + * internal serial day count using algorithms from + * Howard Hinnant. The calculation decomposes the + * serial day count into its era, year-of-era, + * day-of-era, and month-prime components. These + * values are then combined to yield the correct + * Gregorian calendar year. + * + * @return + * Year of Gregorian calendar date + */ + [[nodiscard]] constexpr YearInt_t year() const noexcept + { + const int era = hinnant::eraFromSerialDays(this->serialDays); + const unsigned yoe = hinnant::yearOfEraFromSerialDays(this->serialDays); + const unsigned doy = + (hinnant::dayOfEraFromSerialDays(this->serialDays) - + (DAYS_IN_YEAR * yoe + yoe / 4 - yoe / 100)); + const unsigned mp = hinnant::monthPrime(doy); + return static_cast(era * YEARS_IN_ERA + yoe + (mp >= 10)); + } + + /*! + * @brief + * Date month component. + * + * @details + * This function derives the month value from the + * internal serial day count using algorithms from + * Howard Hinnant. The calculation decomposes the + * serial day count into its day-of-year and + * month-prime components. These values are then + * combined to yield the correct numeric Gregorian + * calendar month. + * + * @return + * Month of Gregorian calendar date + */ + [[nodiscard]] constexpr uint8_t month() const noexcept + { + const unsigned doy = hinnant::dayOfYearFromSerialDays(this->serialDays); + const unsigned mp = hinnant::monthPrime(doy); + return static_cast(mp + (mp < 10 ? 3 : -9)); + } + + /*! + * @brief + * Date day component. + * + * @details + * This function derives the day value from the + * internal serial day count using algorithms from + * Howard Hinnant. The calculation decomposes the + * serial day count into its day-of-year and + * month-prime components. These values are then + * combined to yield the correct day of month. + * + * @return + * Day of Gregorian calendar date + */ + [[nodiscard]] constexpr uint8_t day() const noexcept + { + const unsigned doy = hinnant::dayOfYearFromSerialDays(this->serialDays); + const unsigned mp = hinnant::monthPrime(doy); + return static_cast(doy - (153 * mp + 2) / 5 + 1); + } + + /*! + * @brief + * Compose string representation of Gregorian + * calendar date. + * + * @return + * Gregorian calendar date as string + */ + [[nodiscard]] std::string toStr() const noexcept + { + const char delimiter = '-'; + std::string dateStr; + dateStr.reserve(12); + const DateTuple dateValues = hinnant::fromDaysSinceEpoch(this->serialDays); + dateStr += (std::to_string(std::get<0>(dateValues)) + delimiter); + dateStr += (toDoubleDigitStr(std::get<1>(dateValues)) + delimiter); + dateStr += toDoubleDigitStr(std::get<2>(dateValues)); + return dateStr; + } +}; + +SIMPLYDT_ENFORCE_DATE_CONTRACT(GregorianDate); + +} // namespace simplydt::gregorian + +#endif // SIMPLYDT_LIB_SERIAL_GREGORIAN_CALENDAR_DATE_H_ diff --git a/include/simplydt/calendar/gregorian/gregorian_defs.hpp b/include/simplydt/calendar/gregorian/gregorian_defs.hpp new file mode 100644 index 0000000..0a9b0a6 --- /dev/null +++ b/include/simplydt/calendar/gregorian/gregorian_defs.hpp @@ -0,0 +1,403 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file gregorian_defs.hpp + * + * @brief + * Gregorian calendar system definitions. + */ + + +#ifndef SIMPLYDT_LIB_GREGORIAN_CALENDAR_DEFINITIONS_H_ +#define SIMPLYDT_LIB_GREGORIAN_CALENDAR_DEFINITIONS_H_ + +#include "simplydt/common/calendar_defs.hpp" +#include +#include + +/*! + * @namespace simplydt::gregorian + * + * @brief + * Standard civil calendar system. + */ +namespace simplydt::gregorian +{ + +/*! + * @brief + * Gregorian year integer type. + * + * @note + * The underlying type used to represent Gregorian + * calendar years imposes a limit on the range of + * representable dates. + */ +using Year_Type = uint16_t; + +/*! + * @brief + * Gregorian calendar date values tuple. + * + * @details + * A specialized tuple type containing the three + * fundamental components of a Gregorian calendar + * date: year, month, and day. The tuple structure + * provides a lightweight container for date + * storage while maintaining clear separation of + * the individual components. + */ +using DateTuple = CalendarDateTuple; + +/*! + * @brief + * Minimum supported year value. + * + * @details + * This is the minimum supported year for the + * Gregorian calendar in Simply Datetime. + * This limit was choosen because it aligns with + * Microsoft's FILETIME structure which measures + * 100-nanosecond intervals since January 1, 1601. + * This allows compatibility with Windows NT + * systems and historical time point capabilities. + */ +constexpr Year_Type YEAR_MINIMUM = 1'601; + +/*! + * @brief + * Maximum supported year value. + * + * @details + * This is the maximum supported year value for the + * Gregorian calendar in Simply Datetime. + */ +constexpr Year_Type YEAR_MAXIMUM = 2'038; // TODO: Find real limitation... + +/*! @brief The month of January (1). */ +constexpr uint8_t January = 1; + +/*! @brief The month of February (2). */ +constexpr uint8_t February = 2; + +/*! @brief The month of March (3). */ +constexpr uint8_t March = 3; + +/*! @brief The month of April (4). */ +constexpr uint8_t April = 4; + +/*! @brief The month of May (5). */ +constexpr uint8_t May = 5; + +/*! @brief The month of June (6). */ +constexpr uint8_t June = 6; + +/*! @brief The month of July (7). */ +constexpr uint8_t July = 7; + +/*! @brief The month of August (8). */ +constexpr uint8_t August = 8; + +/*! @brief The month of September (9). */ +constexpr uint8_t September = 9; + +/*! @brief The month of October (10). */ +constexpr uint8_t October = 10; + +/*! @brief The month of November (11). */ +constexpr uint8_t November = 11; + +/*! @brief The month of December (12). */ +constexpr uint8_t December = 12; + +/*! + * @brief + * Abbreviation length for Gregorian calendar name + * literals. + * + * @details + * Details length of abbreviated Gregorian calendar + * day-of-week and month names. + */ +constexpr int ABBREV_LENGTH = 3; + +/*! + * @brief + * Minimum day of Gregorian month. + */ +constexpr uint8_t MIN_DAY_OF_MONTH = 1; + +/*! + * @brief + * Maximum day of Gregorian month. + */ +constexpr uint8_t MAX_DAY_OF_MONTH = 31; + +/*! + * @brief + * Minimum month of Gregorian year. + */ +constexpr uint8_t MIN_MONTH_OF_YEAR = January; + +/*! + * @brief + * Maximum month of Gregorian year. + */ +constexpr uint8_t MAX_MONTH_OF_YEAR = December; + +/*! + * @brief + * Total number of days in one calendar week. + */ +constexpr uint8_t DAYS_IN_WEEK = 7; + +/*! + * @brief + * Total number of days in one common calendar + * year. + */ +constexpr uint16_t DAYS_IN_YEAR = 365; + +/*! + * @brief + * Total number of days in one calendar leap + * year. + */ +constexpr uint16_t DAYS_IN_LEAP_YEAR = 366; + +/*! + * @brief + * Total number of calendar years in one Gregorian + * era. + */ +constexpr Year_Type YEARS_IN_ERA = 400; + +/*! + * @brief + * Total number of days in one Gregorian era. + * + * @details + * Represents the number of days in a 400-year + * Gregorian cycle. The patterns of the Gregorian + * calendar repeat themselves over an era, + * consequently the number of days in a single era + * are the same for all others. This value is + * calculated by multiplying the number of days in + * a year by the number of years in one era, which + * is then increased by an additional 97 days to + * account for leap days within the era. + */ +constexpr uint32_t DAYS_IN_ERA = YEARS_IN_ERA * DAYS_IN_YEAR + 97; + +/*! + * @brief + * Average amount of days per calendar year. + */ +constexpr inline float AVG_DAYS_IN_YEAR = 365.25; + +/*! + * @brief + * Whole number of seven-day periods in a + * calendar year. + */ +constexpr uint8_t WEEKS_IN_YEAR = 52; + +/*! + * @brief + * Total number of months in one calendar year. + */ +constexpr uint8_t MONTHS_IN_YEAR = 12; + +/*! + * @brief + * Enumeration of Gregorian calendar months. + */ +enum Month : uint8_t { + JANUARY, ///< January (1) + FEBRUARY, ///< February (2) + MARCH, ///< March (3) + APRIL, ///< April (4) + MAY, ///< May (5) + JUNE, ///< June (6) + JULY, ///< July (7) + AUGUST, ///< August (8) + SEPTEMBER, ///< September (9) + OCTOBER, ///< October (10) + NOVEMBER, ///< November (11) + DECEMBER ///< December (12) +}; + +/*! + * @brief + * Enumeration of Gregorian calendar days of week. + */ +enum DayOfWeek : uint8_t { + SUNDAY, ///< Sunday + MONDAY, ///< Monday + TUESDAY, ///< Tuesday + WEDNESDAY, ///< Wednesday + THURSDAY, ///< Thursday + FRIDAY, ///< Friday + SATURDAY ///< Saturday +}; + +/*! + * @brief + * Array of Gregorian calendar month name literals. + */ +inline constexpr std::array Months = { + "January", ///< Index 0 + "February", ///< Index 1 + "March", ///< Index 2 + "April", ///< Index 3 + "May", ///< Index 4 + "June", ///< Index 5 + "July", ///< Index 6 + "August", ///< Index 7 + "September", ///< Index 8 + "October", ///< Index 9 + "November", ///< Index 10 + "December" ///< Index 11 +}; + +/*! + * @brief + * Array of abbreviated Gregorian calendar month + * names. + */ +inline constexpr std::array MonthAbbrevs = { + std::string_view(Months[JANUARY], ABBREV_LENGTH), ///< Jan + std::string_view(Months[FEBRUARY], ABBREV_LENGTH), ///< Feb + std::string_view(Months[MARCH], ABBREV_LENGTH), ///< Mar + std::string_view(Months[APRIL], ABBREV_LENGTH), ///< Apr + std::string_view(Months[MAY], ABBREV_LENGTH), ///< May + std::string_view(Months[JUNE], ABBREV_LENGTH), ///< Jun + std::string_view(Months[JULY], ABBREV_LENGTH), ///< Jul + std::string_view(Months[AUGUST], ABBREV_LENGTH), ///< Aug + std::string_view(Months[SEPTEMBER], ABBREV_LENGTH), ///< Sep + std::string_view(Months[OCTOBER], ABBREV_LENGTH), ///< Oct + std::string_view(Months[NOVEMBER], ABBREV_LENGTH), ///< Nov + std::string_view(Months[DECEMBER], ABBREV_LENGTH) ///< Dec +}; + +/*! + * @brief + * Array of Gregorian calendar day-of-week literals. + */ +inline constexpr std::array DaysOfWeek = { + "Sunday", ///< Index 0 + "Monday", ///< Index 1 + "Tuesday", ///< Index 2 + "Wednesday", ///< Index 3 + "Thursday", ///< Index 4 + "Friday", ///< Index 5 + "Saturday" ///< Index 6 +}; + +/*! + * @brief + * Array of abbreviated Gregorian calendar day-of-week + * literals. + */ +inline constexpr std::array DayOfWeekAbbrevs = { + std::string_view(DaysOfWeek[SUNDAY], ABBREV_LENGTH), ///< Sun + std::string_view(DaysOfWeek[MONDAY], ABBREV_LENGTH), ///< Mon + std::string_view(DaysOfWeek[TUESDAY], ABBREV_LENGTH), ///< Tue + std::string_view(DaysOfWeek[WEDNESDAY], ABBREV_LENGTH), ///< Wed + std::string_view(DaysOfWeek[THURSDAY], ABBREV_LENGTH), ///< Thu + std::string_view(DaysOfWeek[FRIDAY], ABBREV_LENGTH), ///< Fri + std::string_view(DaysOfWeek[SATURDAY], ABBREV_LENGTH) ///< Sat +}; + +/*! + * @brief + * Invalid date day-of-week index. + * + * @details + * Used to signal invalid dates that seek a day-of-week + * index. + */ +constexpr uint8_t INVALID_DOW_INDEX = 255; + +} // namespace simplydt::gregorian + +/*! + * @brief + * Tomohiko Sakamoto's Algorithm. + * + * @details + * Algorithm for computing day-of-week indecies of + * Gregorian calendar dates by Tomohiko Sakamoto. + */ +namespace simplydt::sakamoto +{ + +/*! + * @brief + * Month offset lookup table for Tomohiko Sakamoto's + * algorithm. + * + * @details + * Each value is a precomputed offset added in the + * algorithm's summation. The offsets compactly encode + * month length variations and leap year alignment, + * avoiding branches when calculating the day-of-week. + */ +inline constexpr uint8_t MONTH_KEY[gregorian::MONTHS_IN_YEAR] = { + 0, ///< January offset + 3, ///< February offset + 2, ///< March offset + 5, ///< April offset + 0, ///< May offset + 3, ///< June offset + 5, ///< July offset + 1, ///< August offset + 4, ///< September offset + 6, ///< October offset + 2, ///< November offset + 4 ///< December offset +}; + +} // namespace simplydt::sakamoto + +/*! + * @brief + * Howard Hinnant's Algorithm. + * + * @note + * https://howardhinnant.github.io/date_algorithms.html + * for more details. + * + * @details + * Algorithms sourced from Howard Hinnant for the + * Gregorian calendar. + */ +namespace simplydt::hinnant +{ + +/*! + * @brief + * Days from 1970-01-01 to 0000-03-01. + * + * @details + * This value shifts the epoch from 1970-01-01 + * to 0000-03-01 (March 1 of year 0) when + * converting to civil date components or + * vice-versa when calculating a serial count + * of days since the Unix epoch. This is crucial + * because it aligns the algorithm with all known + * implementations of `std::chrono::system_clock`, + * which count seconds from 1970-01-01, neglecting + * leap seconds. This value is what makes the + * serial date 0 equivalent to 1970-01-01 instead + * of 0000-03-01. + */ +constexpr int32_t EPOCH_SHIFT = 719'468; + +} // namespace simplydt::hinnant + +#endif // SIMPLYDT_LIB_GREGORIAN_CALENDAR_DEFINITIONS_H_ diff --git a/include/simplydt/calendar/gregorian/hinnant_algorithms.hpp b/include/simplydt/calendar/gregorian/hinnant_algorithms.hpp new file mode 100644 index 0000000..ce71196 --- /dev/null +++ b/include/simplydt/calendar/gregorian/hinnant_algorithms.hpp @@ -0,0 +1,216 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file hinnant_algorithms.hpp + * + * @brief + * Gregorian calendar helper algorithms. + */ + + +#ifndef SIMPLYDT_LIB_HOWARD_HINNANT_GREGORIAN_ALGORITHMS_H_ +#define SIMPLYDT_LIB_HOWARD_HINNANT_GREGORIAN_ALGORITHMS_H_ + +#include "simplydt/calendar/gregorian/gregorian_defs.hpp" +#include "simplydt/common/stl_chrono_defs.hpp" + +namespace simplydt::hinnant +{ + +/*! + * @brief + * Converts a calendar date to serial number + * of days since Unix epoch. + * + * @details + * Uses Howard Hinnant’s civil date algorithm + * to convert a year, month, day combination + * into a signed day count relative to the + * Unix epoch (1970-01-01 = day 0). The result + * can be negative for dates before the epoch. + * + * @return + * Days since January 1, 1970 + */ +[[nodiscard]] constexpr int32_t toDaysSinceEpoch( + gregorian::Year_Type year, uint8_t month, uint8_t day +) noexcept +{ + // CREDITS: Howard Hinnant [Mr. Chrono] - (Ripple Labs) + // Convert {year, month, day} triple into a serial count of days. + using namespace simplydt::gregorian; + year -= month <= February; + const int era = year / YEARS_IN_ERA; + const unsigned yoe = static_cast(year - era * YEARS_IN_ERA); + const unsigned doy = (153 * (month + (month > February ? -3 : 9)) + 2) / 5 + day - 1; + const unsigned doe = yoe * DAYS_IN_YEAR + yoe / 4 - yoe / 100 + doy; + return static_cast(era * DAYS_IN_ERA + static_cast(doe) - EPOCH_SHIFT); +} + +/*! + * @brief + * Calculates 400-year era index from serial + * number of days. + * + * @details + * Adjusts the input day count by the epoch + * shift to align with the March-based year + * system used in the algorithm. Then + * computes which 400-year Gregorian cycle + * (era) the date falls within. The + * calculation handles negative day counts + * (dates before the algorithm's epoch) by + * subtracting one less than a full era to + * ensure proper era boundary crossing. + * + * @return + * Year era index + */ +[[nodiscard]] constexpr int eraFromSerialDays(int32_t serial_days) noexcept +{ + using namespace simplydt::gregorian; + serial_days += EPOCH_SHIFT; + return (serial_days >= 0 ? serial_days : serial_days - 146'096) / DAYS_IN_ERA; +} + +/*! + * @brief + * Calculates index of day within current + * 400-year era. + * + * @details + * First determines which 400-year era contains + * the specified date using `eraFromSerialDays()`, + * then calculates the relative day position + * within that era. The result represents the + * number of days elapsed since the start of the + * era (March 1st of the era's first year) to + * the specified date. The result can range from + * [0 - 146,096] inclusive. + * + * @return + * Day offset within current era [0 - 146,096] + */ +[[nodiscard]] constexpr unsigned dayOfEraFromSerialDays(int32_t serial_days) noexcept +{ + using namespace simplydt::gregorian; + const int era = eraFromSerialDays(serial_days); + serial_days += EPOCH_SHIFT; + return static_cast(serial_days - era * DAYS_IN_ERA); +} + +/*! + * @brief + * Calculates year index within current 400-year + * era. + * + * @details + * Uses the day-of-era value to compute the year + * index within the 400-year Gregorian cycle. The + * calculation accounts for leap year patterns by + * subtracting/excluding partial periods. These + * complex adjustment handles the irregular leap + * year distribution across centuries. The result + * can range from [0 - 399] inclusive. + * + * @return + * Year offset within current era [0 - 399] + */ +[[nodiscard]] constexpr unsigned yearOfEraFromSerialDays(int32_t serial_days) noexcept +{ + using namespace simplydt::gregorian; + const unsigned doe = dayOfEraFromSerialDays(serial_days); + return (doe - doe / 1'460 + doe / 36'524 - doe / 146'096) / DAYS_IN_YEAR; +} + +/*! + * @brief + * Calculates day index within current year. + * + * @details + * Computes the number of days that have elapsed + * since the start of the current year. First + * determines the year offset within the era, + * then subtracts the cumulative days from the + * beginning of the era up to the start of the + * current year. The calculation accounts for + * leap years by including the appropriate + * number of leap days accumulated up to the + * current year within the era. The result can + * range from [0 - 365] inclusive. + * + * @return + * Day offset within current year [0 - 365] + */ +[[nodiscard]] constexpr unsigned dayOfYearFromSerialDays(int32_t serial_days) noexcept +{ + using namespace simplydt::gregorian; + const unsigned yoe = yearOfEraFromSerialDays(serial_days); + return dayOfEraFromSerialDays(serial_days) - (DAYS_IN_YEAR * yoe + yoe / 4 - yoe / 100); +} + +/*! + * @brief + * Calculates month index within a March-based + * year system. + * + * @details + * Uses integer arithmetic to convert a day of + * year value into a month index where the year + * starts in March. This March-based numbering + * system simplifies leap day handling since + * February becomes the last month of the year + * (March = 0, April = 1, ..., February = 11). + * The formula employs a linear approximation + * that distributes days across months using + * fixed-point mathematics for efficient + * computation without branching. + * + * @return + * March-based month index + */ +[[nodiscard]] inline constexpr unsigned monthPrime(const unsigned dayOfYear) noexcept +{ + return (5 * dayOfYear + 2) / 153; +} + +/*! + * @brief + * Converts a serial count of days since Unix + * epoch to calendar date values. + * + * @details + * Uses Howard Hinnant’s civil date algorithm + * to convert a signed day count relative to + * the Unix epoch (1970-01-01 = day 0) into a + * year, month, day combination. Returns a + * `DateTuple` representing the calculated + * civil date in y-m-d order. + * + * @return + * Calendar date values tuple + */ +[[nodiscard]] constexpr gregorian::DateTuple fromDaysSinceEpoch(int32_t serial_days) noexcept +{ + // CREDITS: Howard Hinnant [Mr. Chrono] - (Ripple Labs) + // Convert a serial count of days into a {year, month, day} triple. + using namespace simplydt::gregorian; + serial_days += EPOCH_SHIFT; + const int era = (serial_days >= 0 ? serial_days : serial_days - 146'096) / DAYS_IN_ERA; + const unsigned doe = static_cast(serial_days - era * DAYS_IN_ERA); + const unsigned yoe = (doe - doe / 1'460 + doe / 36'524 - doe / 146'096) / DAYS_IN_YEAR; + const Year_Type y = static_cast(yoe) + era * YEARS_IN_ERA; + const unsigned doy = doe - (DAYS_IN_YEAR * yoe + yoe / 4 - yoe / 100); + const unsigned mp = (5 * doy + 2) / 153; + const uint8_t d = static_cast(doy - (153 * mp + 2) / 5 + 1); + const uint8_t m = static_cast(mp + (mp < 10 ? 3 : -9)); + return DateTuple{y + (m <= February), m, d}; +} + +} // namespace simplydt::hinnant + +#endif // SIMPLYDT_LIB_HOWARD_HINNANT_GREGORIAN_ALGORITHMS_H_ diff --git a/include/simplydt/common/calendar_defs.hpp b/include/simplydt/common/calendar_defs.hpp new file mode 100644 index 0000000..9470f69 --- /dev/null +++ b/include/simplydt/common/calendar_defs.hpp @@ -0,0 +1,60 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file calendar_defs.hpp + * + * @brief + * General calendar-agnostic definitions. + */ + + +#ifndef SIMPLYDT_LIB_GENERAL_CALENDAR_DEFINITIONS_H_ +#define SIMPLYDT_LIB_GENERAL_CALENDAR_DEFINITIONS_H_ + +#include "simplydt/common/simplydt_defs.hpp" +#include +#include + +namespace simplydt +{ + +/*! + * @brief + * Enumeration of available calendar systems. + */ +enum CalendarSystem : uint8_t { + JULIAN, ///< Proleptic solar calendar + GREGORIAN, ///< Civil solar calendar +}; + +/*! + * @brief + * Enumeration of individual calendar components. + */ +enum class CalendarComponent : uint8_t { + DAY = DatetimeComponent::DAY, ///< Calendar day component + MONTH = DatetimeComponent::MONTH, ///< Calendar month component + YEAR = DatetimeComponent::YEAR ///< Calendar year component +}; + +/*! + * @brief + * Calendar year, month, and day value tuple. + * + * @details + * This alias defines a generic date container + * independent of any specific calendar system. + * It is primarily used as a lightweight value + * type for passing or storing complete date + * information without additional calendar logic. + */ +template +using CalendarDateTuple = std::tuple; + +} // namespace simplydt + +#endif // SIMPLYDT_LIB_GENERAL_CALENDAR_DEFINITIONS_H_ diff --git a/include/simplydt/common/general_defs.hpp b/include/simplydt/common/general_defs.hpp deleted file mode 100644 index a7ae6ef..0000000 --- a/include/simplydt/common/general_defs.hpp +++ /dev/null @@ -1,54 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -/*! - * @file general_defs.hpp - * - * @note - * This file really shouldn't exist given it's - * very broad nature. However, at the moment I - * am unsure where to situate some of the ideas - * in this document. The definitions are likely - * to later be moved to another translation unit. - * - * @brief - * General common Simply Datetime library - * definitions. - */ - -// NOTE: Should this be called 'api_defs.hpp'? - - -#ifndef SIMPLYDT_LIB_COMMON_DEFINITIONS_H_ -#define SIMPLYDT_LIB_COMMON_DEFINITIONS_H_ - -#include - -namespace simplydt -{ - - /*! - * @brief - * Enumeration of broken-down calendar components. - */ - enum CalendarComponent : uint8_t { - SECOND, ///< Datetime second component - MINUTE, ///< Datetime minute component - HOUR, ///< Datetime hour component - DAY, ///< Datetime day component - MONTH, ///< Datetime month component - YEAR ///< Datetime year component - }; - - /*! - * @brief - * Invalid literal. - */ - inline const char* INVALID_LITERAL = "Invalid"; - -} // namespace simplydt - -#endif // SIMPLYDT_LIB_COMMON_DEFINITIONS_H_ diff --git a/include/simplydt/common/simplydt_defs.hpp b/include/simplydt/common/simplydt_defs.hpp new file mode 100644 index 0000000..ff81efd --- /dev/null +++ b/include/simplydt/common/simplydt_defs.hpp @@ -0,0 +1,62 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file simplydt_defs.hpp + * + * @brief + * General Simply Datetime library definitions and + * macros. + */ + + +#ifndef SIMPLYDT_LIB_GENERAL_DEFINITIONS_H_ +#define SIMPLYDT_LIB_GENERAL_DEFINITIONS_H_ + +#include + +namespace simplydt +{ + +#if defined(_WIN32) || defined(_WIN64) +/*! @brief Simply Datetime on Windows platform. */ +# define SIMPLYDT_WIN32 +#elif defined(__unix__) || defined(__unix) || defined(__APPLE__) +/*! @brief Simply Datetime on POSIX compliant platform. */ +# define SIMPLYDT_POSIX +# if defined(__APPLE__) +/*! @brief Simply Datetime on Apple platform. */ +# define SIMPLYDT_APPLE +# elif defined(__unix__) || defined(__unix) +/*! @brief Simply Datetime on Linux platform. */ +# define SIMPLYDT_LINUX +# endif +#endif + +/*! + * @brief + * Enumeration of individual datetime components. + */ +enum DatetimeComponent : uint8_t { + NANOSECOND = 1 << 0, ///< Time nanosecond component + MILLISECOND = 1 << 1, ///< Time millisecond component + SECOND = 1 << 2, ///< Time second component + MINUTE = 1 << 3, ///< Time minute component + HOUR = 1 << 4, ///< Time hour component + DAY = 1 << 5, ///< Date day component + MONTH = 1 << 6, ///< Date month component + YEAR = 1 << 7 ///< Date year component +}; + +/*! + * @brief + * Invalid literal. + */ +constexpr const char* INVALID_LITERAL = "invalid"; + +} // namespace simplydt + +#endif // SIMPLYDT_LIB_GENERAL_DEFINITIONS_H_ diff --git a/include/simplydt/common/stl_chrono_defs.hpp b/include/simplydt/common/stl_chrono_defs.hpp index ee65c42..2e7237f 100644 --- a/include/simplydt/common/stl_chrono_defs.hpp +++ b/include/simplydt/common/stl_chrono_defs.hpp @@ -1,5 +1,5 @@ -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. // Released under the terms of the GNU Affero General Public License version 3. // [ISJTB-CXX-XL20230401-000001] @@ -8,11 +8,15 @@ * @file stl_chrono_defs.hpp * * @brief - * STL date and time library aliases. + * Type aliases for standard C++ date and time constructs. * * @details - * Type aliases for Standard Template Library date and time - * symbols. + * This header defines convenient type aliases for + * commonly used C++ Standard Library date and time + * types, such as clocks, time points, durations, + * and calendar representations. These aliases simplify + * usage and ensure consistency throughout the Simply + * Datetime library. */ @@ -31,50 +35,65 @@ namespace simplydt::stl { - /*! - * @brief - * System-wide real time wall-clock wrapper. - */ - using SystemClock = std::chrono::system_clock; +/*! + * @brief + * System-wide real time wall-clock wrapper. + */ +using SystemClock = std::chrono::system_clock; - /*! - * @brief - * Point in time derived from system clock. - */ - using SystemTimePoint = std::chrono::time_point; +/*! + * @brief + * Point in time derived from system clock. + */ +using SystemTimePoint = std::chrono::time_point; - /*! - * @brief - * Interval of time. - */ - using SystemDuration = SystemClock::duration; +/*! + * @brief + * Interval of time. + */ +using SystemDuration = SystemClock::duration; - /*! - * @brief - * Time point in seconds since Unix epoch. - * - * @details - * The `std::time_t` type is implementation-defined. - * Simply Datetime does not use this type because it - * needs guarantee that the type representing seconds - * since the Unix epoch (January 1, 1970 00:00:00.000) - * is a 64-bit signed integer. This is to mitigate the - * '2038' year overflow. - */ - using UnixTimestamp = int64_t; +/*! + * @brief + * Seconds since Unix epoch. + * + * @details + * The `std::time_t` type is implementation-defined. + * Simply Datetime does not explicitly use this type + * because it needs guarantee that the type used to + * represent seconds since the Unix epoch (January 1, + * 1970 00:00:00.000) is a 64-bit signed integer. + * This is to mitigate the '2038' year overflow + * internally. Simply Datetime provides a safe method + * for converting this type to standard `std::tm`. + */ +using UnixTimestamp = int64_t; + +/*! + * @brief + * Broken-down calendar date and time structure. + * + * @details + * This is a C-style struct that stores individual + * components of a calendar time point and contains + * two key fields to be mindful of: `tm_mon` which + * measures the number of months ***since*** January, + * and `tm_year` which measures the number of years + * ***since*** 1900. + */ +using CalendarDateTime = std::tm; - /*! - * @brief - * Broken-down calendar datetime component struct. - * - * @details - * This type is a C-style struct that stores individual - * components of a calendar time point and contains two - * key fields to be mindful of: `tm_mon` which measures - * the number of months ***since*** January, and `tm_year` - * which measures the number of years ***since*** 1900. - */ - using CalendarDateTime = std::tm; +/*! + * @brief + * Broken-down calendar date structure. + * + * @details + * A type alias for `std::chrono::year_month_day` + * that provides a structured representation of a + * calendar date with explicit year, month, and day + * fields. + */ +using CalendarDate = std::chrono::year_month_day; } // namespace simplydt::stl diff --git a/include/simplydt/common/stl_chrono_utils.hpp b/include/simplydt/common/stl_chrono_utils.hpp index 303d991..5153613 100644 --- a/include/simplydt/common/stl_chrono_utils.hpp +++ b/include/simplydt/common/stl_chrono_utils.hpp @@ -1,5 +1,5 @@ -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. // Released under the terms of the GNU Affero General Public License version 3. // [ISJTB-CXX-XL20230401-000001] @@ -8,57 +8,46 @@ * @file stl_chrono_utils.hpp * * @brief - * STL date and time library utilities. + * Utilities for standard C++ date and time related + * constructs. */ #ifndef SIMPLYDT_LIB_STL_CHRONO_UTILITIES_H_ #define SIMPLYDT_LIB_STL_CHRONO_UTILITIES_H_ +#include "simplydt/common/simplydt_defs.hpp" #include "simplydt/common/stl_chrono_defs.hpp" namespace simplydt::stl { - /*! - * @brief - * Get system wall-clock timestamp. - * - * @return - * Current system clock time point - */ - [[nodiscard]] inline SystemTimePoint getNowSystemTime() noexcept - { - return SystemClock::now(); - } - - /*! - * @brief - * Populate calendar component structure using a system - * clock timestamp. - * - * @details - * Wraps platform-dependent call to interpret a system - * clock time point and populate a calendar component - * structure. The underlying calls used, `localtime_s` - * for Windows and `localtime_r` for POSIX compliant - * systems, are thread-safe. - * - * @return - * True on success - */ - [[nodiscard]] inline bool deriveLocalDateTimeFromTimestamp( - const UnixTimestamp* timestamp, CalendarDateTime* out_tm - ) - { -#if defined(_WIN32) || defined(_WIN64) - // Windows system - return localtime_s(out_tm, timestamp) == 0; -#elif defined(__unix__) || defined(__unix) || defined(__APPLE__) - // POSIX compliant system - return localtime_r(timestamp, out_tm) != nullptr; +/*! + * @brief + * Populate calendar component structure using a system + * clock timestamp. + * + * @details + * Wraps platform-dependent call to interpret a system + * clock time point and populate a calendar component + * structure. The underlying calls used, `localtime_s` + * for Windows and `localtime_r` for POSIX compliant + * systems, are thread-safe and both apply the systems + * local timezone to the populated calendar struct. + * + * @return + * True on success + */ +[[nodiscard]] inline bool deriveLocalDateTimeFromTimestamp( + const UnixTimestamp* timestamp, CalendarDateTime* out_tm +) +{ +#if defined(SIMPLYDT_WIN32) + return localtime_s(out_tm, timestamp) == 0; +#elif defined(SIMPLYDT_POSIX) + return localtime_r(timestamp, out_tm) != nullptr; #endif - } +} } // namespace simplydt::stl diff --git a/include/simplydt/common/string_utils.hpp b/include/simplydt/common/string_utils.hpp index 347b7a7..b7432ad 100644 --- a/include/simplydt/common/string_utils.hpp +++ b/include/simplydt/common/string_utils.hpp @@ -1,5 +1,5 @@ -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. // Released under the terms of the GNU Affero General Public License version 3. // [ISJTB-CXX-XL20230401-000001] @@ -20,28 +20,29 @@ namespace simplydt { - /*! - * @brief - * Convert integer value to a double digit string. - * - * @details - * This method takes an integer value of arbitrary type - * and converts it to a string that only consist of two - * digits. Values less than 10 are prefixed with a '0', - * and values greater than 99 are truncated to their - * last two digits using modulus division. - * - * @return - * Double-digit string - */ - template - [[nodiscard]] inline std::string toDoubleDigitStr(const Int_T integer) noexcept - { - if (integer < 10) - return (std::string{ "0" } + std::to_string(integer)); - else - return std::to_string(integer % 100); - } +/*! + * @brief + * Convert integer value to a double digit string. + * + * @details + * This method takes an integer value of arbitrary type + * and converts it to a string that only consist of two + * digits. Values less than 10 are prefixed with a '0', + * and values greater than 99 are truncated to their + * last two digits using modulus division. + * + * @return + * Double-digit string + */ +template + requires std::is_integral_v +[[nodiscard]] inline std::string toDoubleDigitStr(const Int_T integer) noexcept +{ + if (integer < 10) + return (std::string{"0"} + std::to_string(integer)); + else + return std::to_string(integer % 100); +} } // namespace simplydt diff --git a/include/simplydt/common/time_defs.hpp b/include/simplydt/common/time_defs.hpp new file mode 100644 index 0000000..82f9f0d --- /dev/null +++ b/include/simplydt/common/time_defs.hpp @@ -0,0 +1,101 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file time_defs.hpp + * + * @brief + * General time definitions. + */ + + +#ifndef SIMPLYDT_LIB_GENERAL_TIME_DEFINITIONS_H_ +#define SIMPLYDT_LIB_GENERAL_TIME_DEFINITIONS_H_ + +#include "simplydt/common/simplydt_defs.hpp" +#include +#include + +namespace simplydt +{ + +/*! + * @brief + * Enumeration of available time standards. + */ +enum TimeStandard : uint8_t { + UTC, ///< Universal Coordinated Time + TAI, ///< International Atomic Time +}; + +/*! + * @brief + * Time representation type. + * + * @details + * The underlying type used to represent temporal + * time. Optimal for second precision. + */ +using Time_t = uint32_t; + +/*! @brief Total number of seconds in one day. */ +constexpr uint32_t SECONDS_IN_DAY = 86'400; + +/*! @brief Total number of seconds in one hour. */ +constexpr uint16_t SECONDS_IN_HOUR = 3'600; + +/*! @brief Total number of seconds in one minute. */ +constexpr uint8_t SECONDS_IN_MINUTE = 60; + +/*! @brief Total number of minutes in one day. */ +constexpr uint16_t MINUTES_IN_DAY = 1'440; + +/*! @brief Total number of minutes in one hour. */ +constexpr uint8_t MINUTES_IN_HOUR = 60; + +/*! @brief Total number of hours in one day. */ +constexpr uint8_t HOURS_IN_DAY = 24; + +/*! @brief Midnight in serial seconds (00:00:00 AM). */ +constexpr Time_t MIDNIGHT = 0; + +/*! @brief Noon in serial seconds (12:00:00 PM). */ +constexpr Time_t NOON = SECONDS_IN_DAY / 2; + +/*! @brief End of day in serial seconds (23:59:59 PM) */ +constexpr Time_t TIME_MAX = SECONDS_IN_DAY - 1; + +/*! + * @brief + * Enumeration of individual time components. + */ +enum class TimeComponent : uint8_t { + NANOSECOND = DatetimeComponent::NANOSECOND, ///< Time nanosecond component + MILLISECOND = DatetimeComponent::MILLISECOND, ///< Time millisecond component + SECOND = DatetimeComponent::SECOND, ///< Time second component + MINUTE = DatetimeComponent::MINUTE, ///< Time minute component + HOUR = DatetimeComponent::HOUR ///< Time hour component +}; + +/*! + * @brief + * Binary 12-hour clock meridiem indicators. + */ +enum MeridiemPhase : uint8_t { + AM, ///< Anti meridiem (before midday) + PM ///< Post meridiem (after midday) +}; + +/*! + * @brief + * Binary meridiem indicator literals for 12-hour + * clocks. + */ +inline constexpr std::array MeridiemPhases = {"AM", "PM"}; + +} // namespace simplydt + +#endif // SIMPLYDT_LIB_GENERAL_TIME_DEFINITIONS_H_ diff --git a/include/simplydt/coord_universal_time/time_flags.hpp b/include/simplydt/coord_universal_time/time_flags.hpp deleted file mode 100644 index 0ab200e..0000000 --- a/include/simplydt/coord_universal_time/time_flags.hpp +++ /dev/null @@ -1,116 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -/*! - * @file time_flags.hpp - * - * @brief - * Coordinated Universal Time flags. - * - * @details - * Coordinated Universal Time characteristic flags. - */ - -#ifndef SIMPLYDT_LIB_UTC_TIME_FLAGS_H_ -#define SIMPLYDT_LIB_UTC_TIME_FLAGS_H_ - -#include - -namespace simplydt::utc -{ - - /*! - * @brief - * Coordinated Universal Time flags. - * - * @details - * These flags describe structural characteristics - * of a Coordinated Universal Time point that can - * be derived from the Coordinated Universal Time - * system independently. - */ - enum class TimeFlag : uint32_t { - NONE = 0, ///< No time flags - INVALID = 1 << 1, ///< Invalid time - - DAY_Q1 = 1 << 21, ///< First quarter of day (00:00:00 - 05:59:59) - DAY_Q2 = 1 << 22, ///< Second quarter of day (06:00:00 - 11:59:59) - DAY_Q3 = 1 << 23, ///< Third quarter of day (12:00:00 - 17:59:59) - DAY_Q4 = 1 << 24, ///< Fourth quarter of day (18:00:00 - 23:59:59) - - START_OF_DAY = 1 << 25, ///< First hour of day (00:00:00 - 00:59:59) - MIDDLE_OF_DAY = 1 << 26, ///< Mid hour of day (12:00:00 - 12:59:59) - END_OF_DAY = 1 << 27, ///< Final hour of day (23:00:00 - 23:59:59) - - EARLY_HOUR = 1 << 28, ///< First 20 minutes of the hour (XX:00:00 - XX:19:59) - MID_HOUR = 1 << 29, ///< Median 20 minutes of the hour (XX:20:00 - XX:39:59) - LATE_HOUR = 1 << 30 ///< Final 20 minutes of the hour (XX:40:00 - XX:59:59) - }; - - /*! - * @brief - * Combine time flag values using bitwise OR. - * - * @details - * Returns a `TimeFlag` with bits set where either - * input flag has a bit set. - * - * @return - * Bit combined time flag - */ - [[nodiscard]] inline TimeFlag operator|(const TimeFlag lhs, const TimeFlag rhs) noexcept - { - return static_cast(static_cast(lhs) | static_cast(rhs)); - } - - /*! - * @brief - * Computes bitwise AND of time flags. - * - * @details - * Returns a `TimeFlag` with bits set only where both - * input flags have bits set. - * - * @return - * Bit filtered time flag - */ - [[nodiscard]] inline TimeFlag operator&(const TimeFlag lhs, const TimeFlag rhs) noexcept - { - return static_cast(static_cast(lhs) & static_cast(rhs)); - } - - /*! - * @brief - * Computes bitwise complement of time flag. - * - * @details - * Returns a `TimeFlag` with all bits flipped relative - * to the input. - * - * @return - * Bit negated time flag - */ - [[nodiscard]] inline TimeFlag operator~(TimeFlag flag) noexcept - { - return static_cast(~static_cast(flag)); - } - - /*! - * @brief - * Combine time flag values in-place using bitwise OR. - * - * @return - * Reference to left-hand side time flag - */ - [[nodiscard]] inline TimeFlag& operator|=(TimeFlag& lhs, const TimeFlag rhs) noexcept - { - lhs = lhs | rhs; - return lhs; - } - -} // namespace simplydt::utc - -#endif // SIMPLYDT_LIB_UTC_TIME_FLAGS_H_ \ No newline at end of file diff --git a/include/simplydt/coord_universal_time/utc_defs.hpp b/include/simplydt/coord_universal_time/utc_defs.hpp deleted file mode 100644 index 7c91892..0000000 --- a/include/simplydt/coord_universal_time/utc_defs.hpp +++ /dev/null @@ -1,65 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -/*! - * @file julian_defs.hpp - * - * @brief - * Coordinated Universal Time system definitions. - */ - -#ifndef SIMPLYDT_LIB_UTC_SYSTEM_DEFINITIONS_H_ -#define SIMPLYDT_LIB_UTC_SYSTEM_DEFINITIONS_H_ - -#include -#include - -/*! - * @namespace simplydt::utc - * - * @brief - * Standard Coordinated Universal Time system. - */ -namespace simplydt::utc -{ - - /*! - * @brief - * Binary meridiem indicator literals for 12-hour - * clock. - */ - const std::array MeridiemPhases = { "AM", "PM" }; - - /*! - * @brief - * Binary 12-hour clock meridiem indicators. - */ - enum MeridiemPhase : uint8_t { - AM, ///< Anti meridiem (before midday) - PM ///< Post meridiem (after midday) - }; - - /*! - * @brief - * Maximum number of seconds in a minute. - */ - constexpr uint8_t MAX_SECONDS_IN_MINUTE = 59; - - /*! - * @brief - * Maximum number of minutes in a hour. - */ - constexpr uint8_t MAX_MINUTES_IN_HOUR = 59; - - /*! - * @brief - * Maximum number of hours in a day. - */ - constexpr uint8_t MAX_HOURS_IN_DAY = 23; - -} // namespace simplydt::utc - -#endif // SIMPLYDT_LIB_UTC_SYSTEM_DEFINITIONS_H_ diff --git a/include/simplydt/coord_universal_time/utc_time.hpp b/include/simplydt/coord_universal_time/utc_time.hpp deleted file mode 100644 index 390fe4f..0000000 --- a/include/simplydt/coord_universal_time/utc_time.hpp +++ /dev/null @@ -1,227 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -/*! - * @file utc_time.hpp - * - * @brief - * Standalone Coordinated Universal Time declaration. - */ - -#ifndef SIMPLYDT_LIB_UTC_TIME_STRUCT_H_ -#define SIMPLYDT_LIB_UTC_TIME_STRUCT_H_ - -#include "simplydt/common/general_defs.hpp" -#include "simplydt/common/string_utils.hpp" -#include "simplydt/coord_universal_time/utc_defs.hpp" -#include - -namespace simplydt::utc -{ - - /*! - * @brief - * Broken-down UTC time. - */ - class Time { - - public: - /*! - * @brief - * Underlying type used to represent UTC time. - */ - using Underlying_T = uint32_t; - - /*! - * @brief - * Factor used to store and retrieve hour values. - */ - static constexpr Underlying_T HOUR_FACTOR = 10'000; - - /*! - * @brief - * Factor used to store and retrieve minute values. - */ - static constexpr Underlying_T MINUTE_FACTOR = 100; - - /*! - * @brief - * Default UTC time (midnight). - */ - static constexpr Underlying_T DEFAULT_TIME = 0; - - /*! - * @brief - * Store encoded UTC time at provided integer address. - * - * @return - * True on success - */ - static bool encodeUTCTimeIntoInteger( - Underlying_T* integer, - const uint8_t& hour, - const uint8_t& minute, - const uint8_t& second - ) noexcept; - - /*! - * @brief - * Retrieve hour component from encoded UTC time integer. - * - * @return - * UTC time hour component - */ - static uint8_t extractEncodedHour(const Underlying_T* time) noexcept; - - /*! - * @brief - * Retrieve minute component from encoded UTC time integer. - * - * @return - * UTC time minute component - */ - static uint8_t extractEncodedMinute(const Underlying_T* time) noexcept; - - /*! - * @brief - * Retrieve second component from encoded UTC time integer. - * - * @return - * UTC time second component - */ - static uint8_t extractEncodedSecond(const Underlying_T* time) noexcept; - - /*! - * @brief - * Construct UTC time using hour, minute, and second - * values. - */ - Time(const uint8_t hour, const uint8_t minute, const uint8_t second) noexcept; - - /*! - * @brief - * Construct UTC time using hour and minute values; - * assume 0 seconds. - */ - Time(const uint8_t hour, const uint8_t minute) noexcept; - - /*! - * @brief - * Construct UTC time from another. - */ - Time(const Time& time) noexcept; - - /*! - * @brief - * Construct default UTC time. - */ - Time() noexcept; - - ~Time() noexcept = default; - - friend inline std::ostream& operator<<(std::ostream& os, const Time time) noexcept - { - os << time.toStr(); - return os; - } - - /*! @brief Evaluate equivalence of UTC times. */ - [[nodiscard]] bool operator==(const Time time) const noexcept; - - /*! @brief Determine if left-hand side is sequentially before right-hand side. */ - [[nodiscard]] bool operator<(const Time time) const noexcept; - - /*! @brief Determine if left-hand side is sequentially after right-hand side. */ - [[nodiscard]] bool operator>(const Time time) const noexcept; - - [[nodiscard]] bool operator<=(const Time time) const noexcept; - - [[nodiscard]] bool operator>=(const Time time) const noexcept; - - /*! - * @brief - * Returns requested time component. - * - * @return - * Individual time component value - */ - [[nodiscard]] uint16_t operator[](const CalendarComponent component) const noexcept; - - /*! - * @brief - * Time hour component. - * - * @return - * Hour of UTC time - */ - [[nodiscard]] uint8_t hour() const noexcept; - - /*! - * @brief - * Time meridiem phase indicator literal. - * - * @return - * "AM" or "PM" - */ - [[nodiscard]] std::string hourPhaseLiteral() const noexcept; - - /*! - * @brief - * Time minute component. - * - * @return - * Minute of UTC time - */ - [[nodiscard]] uint8_t minute() const noexcept; - - /*! - * @brief - * Time second component. - * - * @return - * Second of UTC time - */ - [[nodiscard]] uint8_t second() const noexcept; - - /*! - * @brief - * Returns requested UTC time component. - * - * @details - * If the requested calendar component is not a - * time component the method returns 0. - * - * @return - * Individual time component value - */ - [[nodiscard]] uint16_t getComponent(const CalendarComponent component) const noexcept; - - /*! - * @brief - * Compose string representation of UTC time. - * - * @return - * UTC time as string - */ - [[nodiscard]] std::string toStr() const noexcept; - - /*! - * @brief - * Returns raw underlying representation of UTC - * time. - * - * @return - * UTC time as integer - */ - [[nodiscard]] Underlying_T underlying() const noexcept; - - private: - Underlying_T time; ///< UTC time - }; - -} // namespace simplydt::utc - -#endif // SIMPLYDT_LIB_UTC_TIME_STRUCT_H_ diff --git a/include/simplydt/dap/concepts/method_call.hpp b/include/simplydt/dap/concepts/method_call.hpp deleted file mode 100644 index ddc3f11..0000000 --- a/include/simplydt/dap/concepts/method_call.hpp +++ /dev/null @@ -1,71 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -/*! - * @file method_call.hpp - * - * @brief - * Method overload entity concepts. - */ - -#ifndef SIMPLYDT_LIB_DAP_METHOD_CALL_STRUCT_CONCEPTS_H_ -#define SIMPLYDT_LIB_DAP_METHOD_CALL_STRUCT_CONCEPTS_H_ - -#include "simplydt/dap/type_traits/method_args.hpp" -#include "simplydt/dap/type_traits/method_call.hpp" -#include - -// TODO: Implement custom setup for the macro below -// in this file. If you included this file it -// means you'll have access to both the macro -// and the struct used to evaluate the return -// types. - -/*! @brief Macro for asserting equality of two types. */ -#define ASSERT_RETURN_TYPES_EQ(TypeA, TypeB) \ - static_assert(std::is_same_v, \ - "Overload return type does not match base!") - -namespace simplydt::concepts -{ - - /*! - * @brief - * Concept of a properly structured method overload - * struct. - * - * @details - * This concepts details a contract-abiding overload - * struct that can be invoked by DAP template methods. - * To be "contract-abiding" in this context means that - * type T Inherits from `dap::Overload`, defines a - * static method `T::call(...)`, defines the nested - * types `ReturnType` and `MethodArgs`, and have the - * mentioned `MethodArgs` type be an instantiation of - * the `dap::Arguments<...>` template. This concept - * imposes no constraints on the overload return and - * argument types. - */ - template - concept valid_template_overload = requires { - // Require 'T::MethodArgs' alias be defined - // AND Require 'T::MethodArgs' be of type dap::Arguments - requires type_traits::is_method_args_struct_v; - - // Require T inherit from dap::Overload - requires type_traits::is_overload_derived_v; - - // Require 'T::ReturnType' alias be defined - // AND Require static 'T::call(...)' method be defined - requires type_traits::has_static_call_signature_v< - T, - typename T::ReturnType, - typename T::MethodArgs::Types>; - }; - -} // namespace simplydt::concepts - -#endif // SIMPLYDT_LIB_DAP_METHOD_CALL_STRUCT_CONCEPTS_H_ diff --git a/include/simplydt/dap/concepts/repr_types.hpp b/include/simplydt/dap/concepts/repr_types.hpp deleted file mode 100644 index 2c4aae3..0000000 --- a/include/simplydt/dap/concepts/repr_types.hpp +++ /dev/null @@ -1,61 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -/*! - * @file repr_types.hpp - * - * @brief - * Datetime Abstraction Protocol underlying - * representation type concepts. - */ - -#ifndef SIMPLYDT_LIB_DAP_UNDERLYING_TYPES_CONCEPTS_H_ -#define SIMPLYDT_LIB_DAP_UNDERLYING_TYPES_CONCEPTS_H_ - -#include -#include - -namespace simplydt::concepts -{ - - /*! - * @brief - * Concept of a type that can be relatively - * compared to other objects of same type. - */ - template - concept comparable_type = requires (T instance) { - { instance == instance } -> std::same_as; - { instance < instance } -> std::same_as; - { instance <= instance } -> std::same_as; - { instance > instance } -> std::same_as; - { instance >= instance } -> std::same_as; - }; - - /*! - * @brief - * Concept of a useable underlying type for - * a DAP driver. - */ - template - concept useable_underlying_type = requires { - // T must not be a pointer - requires !std::is_pointer_v; - - // T must not have const qualification - requires !std::is_const_v; - - requires std::is_copy_constructible_v; - requires std::is_move_constructible_v; - requires std::is_assignable_v; - - // Require T have relative comparison operators - requires comparable_type; - }; - -} - -#endif // SIMPLYDT_LIB_DAP_UNDERLYING_TYPES_CONCEPTS_H_ diff --git a/include/simplydt/dap/driver/abstract_tag.hpp b/include/simplydt/dap/driver/abstract_tag.hpp deleted file mode 100644 index 76c2a37..0000000 --- a/include/simplydt/dap/driver/abstract_tag.hpp +++ /dev/null @@ -1,35 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -/*! - * @file abstract_tag.hpp - * - * @brief - * Abstract Datetime Abstraction Protocol driver - * tag declaration. - */ - -#ifndef SIMPLYDT_LIB_ABSTRACT_DAP_DRIVER_TAG_H_ -#define SIMPLYDT_LIB_ABSTRACT_DAP_DRIVER_TAG_H_ - -/*! - * @namespace simplydt::dap - * - * @brief - * Datetime Abstraction Protocol. - */ -namespace simplydt::dap -{ - - /*! - * @brief - * Abstract DAP driver tag. - */ - struct AbstractDAPDriver { }; - -} // namespace simplydt::dap - -#endif // SIMPLYDT_LIB_ABSTRACT_DAP_DRIVER_TAG_H_ diff --git a/include/simplydt/dap/driver/concrete_tag.hpp b/include/simplydt/dap/driver/concrete_tag.hpp deleted file mode 100644 index 3a53f89..0000000 --- a/include/simplydt/dap/driver/concrete_tag.hpp +++ /dev/null @@ -1,29 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -/*! - * @file concrete_tag.hpp - * - * @brief - * Concrete Datetime Abstraction Protocol driver - * tag declaration. - */ - -#ifndef SIMPLYDT_LIB_CONCRETE_DAP_DRIVER_TAG_H_ -#define SIMPLYDT_LIB_CONCRETE_DAP_DRIVER_TAG_H_ - -namespace simplydt::dap -{ - - /*! - * @brief - * Concrete DAP driver tag. - */ - struct DAPDriver { }; - -} // namespace simplydt::dap - -#endif // SIMPLYDT_LIB_CONCRETE_DAP_DRIVER_TAG_H_ diff --git a/include/simplydt/dap/driver/duration/abstract_driver.hpp b/include/simplydt/dap/driver/duration/abstract_driver.hpp deleted file mode 100644 index 02b9b4a..0000000 --- a/include/simplydt/dap/driver/duration/abstract_driver.hpp +++ /dev/null @@ -1,164 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -/*! - * @file abstract_driver.hpp - * - * @brief - * Abstract duration driver interface. - */ - -#ifndef SIMPLYDT_LIB_ABSTRACT_DURATION_DRIVER_H_ -#define SIMPLYDT_LIB_ABSTRACT_DURATION_DRIVER_H_ - -#include "simplydt/dap/concepts/method_call.hpp" -#include "simplydt/dap/driver/abstract_tag.hpp" -#include "simplydt/dap/concepts/repr_types.hpp" -#include - -namespace simplydt::dap -{ - - /*! - * @brief - * Duration driver interface abstraction. - * - * @details - * This type cannot be instantiated on its own since - * the interface it presents is abstract. It is meant - * to be inherited by another class which will then - * present concrete implementations of the functionality - * outlined here. The presence of a method definition - * in this base class alone does not automatically - * enforce a requirement on the derivatives; that is - * achieved using concepts. The API extended by this - * class is protected and only accessible by derived - * entities. The protected API uses static methods that - * follow the "pointer-to-self" convention. On the - * contrary, the API extended by a derivative is expected - * to be public and typically with use of member methods. - */ - template - class AbstractDurationDriver : public AbstractDAPDriver { - - public: - /*! - * @brief - * Underlying duration representation type. - */ - using Underlying_T = Duration_T; - - /*! - * @brief - * Type alias of base duration abstraction. - */ - using Driver = AbstractDurationDriver; - - protected: - AbstractDurationDriver() noexcept { } - - ~AbstractDurationDriver() noexcept = default; - - /*! @brief Overload base functionality using provided implementation. */ - template - static bool isDefaultValue(const Duration_T& selfImpl) - { - ASSERT_RETURN_TYPES_EQ(bool, typename Overload_Impl::ReturnType); - return Overload_Impl::call(selfImpl); - } - - /*! @brief Overload base functionality using provided implementation. */ - template - static bool isNegativeValue(const Duration_T& selfImpl) - { - ASSERT_RETURN_TYPES_EQ(bool, typename Overload_Impl::ReturnType); - return Overload_Impl::call(selfImpl); - } - - /*! @brief Overload base functionality using provided implementation. */ - template - static int32_t getElapsedDays(const Duration_T& selfImpl) - { - ASSERT_RETURN_TYPES_EQ(int32_t, typename Overload_Impl::ReturnType); - return Overload_Impl::call(selfImpl); - } - - /*! @brief Overload base functionality using provided implementation. */ - template - static uint32_t getAbsoluteElapsedDays(const Duration_T& selfImpl) - { - ASSERT_RETURN_TYPES_EQ(uint32_t, typename Overload_Impl::ReturnType); - return Overload_Impl::call(selfImpl); - } - - /*! @brief Overload base functionality using provided implementation. */ - template - static int8_t getHoursComponent(const Duration_T& selfImpl) - { - ASSERT_RETURN_TYPES_EQ(int8_t, typename Overload_Impl::ReturnType); - return Overload_Impl::call(selfImpl); - } - - /*! @brief Overload base functionality using provided implementation. */ - template - static uint8_t getAbsoluteHours(const Duration_T& selfImpl) - { - ASSERT_RETURN_TYPES_EQ(uint8_t, typename Overload_Impl::ReturnType); - return Overload_Impl::call(selfImpl); - } - - /*! @brief Overload base functionality using provided implementation. */ - template - static int8_t getMinutesComponent(const Duration_T& selfImpl) - { - ASSERT_RETURN_TYPES_EQ(int8_t, typename Overload_Impl::ReturnType); - return Overload_Impl::call(selfImpl); - } - - /*! @brief Overload base functionality using provided implementation. */ - template - static uint8_t getAbsoluteMinutes(const Duration_T& selfImpl) - { - ASSERT_RETURN_TYPES_EQ(uint8_t, typename Overload_Impl::ReturnType); - return Overload_Impl::call(selfImpl); - } - - /*! @brief Overload base functionality using provided implementation. */ - template - static int8_t getSecondsComponent(const Duration_T& selfImpl) - { - ASSERT_RETURN_TYPES_EQ(int8_t, typename Overload_Impl::ReturnType); - return Overload_Impl::call(selfImpl); - } - - /*! @brief Overload base functionality using provided implementation. */ - template - static uint8_t getAbsoluteSeconds(const Duration_T& selfImpl) - { - ASSERT_RETURN_TYPES_EQ(uint8_t, typename Overload_Impl::ReturnType); - return Overload_Impl::call(selfImpl); - } - - /*! @brief Overload base functionality using provided implementation. */ - template - static Duration_T calculateDisplace(const Duration_T& selfImpl, const Duration_T& durationImpl) - { - ASSERT_RETURN_TYPES_EQ(Duration_T, typename Overload_Impl::ReturnType); - return Overload_Impl::call(selfImpl, durationImpl); - } - - /*! @brief Overload base functionality using provided implementation. */ - template - static void displace(Duration_T& selfImpl, const Duration_T& durationImpl) - { - ASSERT_RETURN_TYPES_EQ(void, typename Overload_Impl::ReturnType); - return Overload_Impl::call(selfImpl, durationImpl); - } - }; - -} // namespace simplydt::dap - -#endif // SIMPLYDT_LIB_ABSTRACT_DURATION_DRIVER_H_ diff --git a/include/simplydt/dap/overload/arguments.hpp b/include/simplydt/dap/overload/arguments.hpp deleted file mode 100644 index db7551e..0000000 --- a/include/simplydt/dap/overload/arguments.hpp +++ /dev/null @@ -1,34 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -/*! - * @file arguments.hpp - * - * @brief - * Method arguments entity declaration. - */ - -#ifndef SIMPLYDT_LIB_METHOD_ARGUMENTS_STRUCT_H_ -#define SIMPLYDT_LIB_METHOD_ARGUMENTS_STRUCT_H_ - -#include - -namespace simplydt::dap -{ - - /*! - * @brief - * Method call arguments. - */ - template - struct Arguments { - /*! @brief Method argument types tuple. */ - using Types = std::tuple; - }; - -} // namespace simplydt::dap - -#endif // SIMPLYDT_LIB_METHOD_ARGUMENTS_STRUCT_H_ diff --git a/include/simplydt/dap/overload/method.hpp b/include/simplydt/dap/overload/method.hpp deleted file mode 100644 index 103474f..0000000 --- a/include/simplydt/dap/overload/method.hpp +++ /dev/null @@ -1,46 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -/*! - * @file method.hpp - * - * @brief - * Method overload entity declaration. - */ - -#ifndef SIMPLYDT_LIB_METHOD_OVERLOAD_STRUCT_H_ -#define SIMPLYDT_LIB_METHOD_OVERLOAD_STRUCT_H_ - -#include "simplydt/dap/overload/arguments.hpp" - -namespace simplydt::dap -{ - - /*! - * @brief - * Method overload implementation struct. - * - * @details - * This type is meant to be inherited by other - * structs which will represent the overloaded - * implementation of some template method. The - * template methods invoke the implementation - * detailed by a derivative of this type through - * an implicit contract between the two. - * Typically the methods implemented by this - * type are static, but in most cases there is - * no performance difference if they are member - * methods. - */ - template - struct Overload { - /*! @brief Overload method argument types. */ - using MethodArgs = Arguments; - }; - -} // namespace simplydt::dap - -#endif // SIMPLYDT_LIB_METHOD_OVERLOAD_STRUCT_H_ diff --git a/include/simplydt/dap/type_traits/method_args.hpp b/include/simplydt/dap/type_traits/method_args.hpp deleted file mode 100644 index 3f3455d..0000000 --- a/include/simplydt/dap/type_traits/method_args.hpp +++ /dev/null @@ -1,46 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -/*! - * @file method_args.hpp - * - * @brief - * Method arguments entity type traits. - */ - -#ifndef SIMPLYDT_LIB_METHOD_ARGUMENT_TRAITS_H_ -#define SIMPLYDT_LIB_METHOD_ARGUMENT_TRAITS_H_ - -#include "simplydt/dap/overload/arguments.hpp" -#include - -namespace simplydt::type_traits -{ - // TYPE TRAIT : is_method_args_struct - - /*! - * @brief - * Determine if type T is a method arguments struct. - */ - template - struct is_method_args_struct; - - template - struct is_method_args_struct : std::false_type {}; - - template - struct is_method_args_struct> : std::true_type {}; - - /*! - * @brief - * Determine if type T is a method arguments struct. - */ - template - inline constexpr bool is_method_args_struct_v = is_method_args_struct::value; - -} // namespace simplydt::type_traits - -#endif // SIMPLYDT_LIB_METHOD_ARGUMENT_TRAITS_H_ diff --git a/include/simplydt/dap/type_traits/method_call.hpp b/include/simplydt/dap/type_traits/method_call.hpp deleted file mode 100644 index fa82db8..0000000 --- a/include/simplydt/dap/type_traits/method_call.hpp +++ /dev/null @@ -1,109 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -/*! - * @file method_call.hpp - * - * @brief - * Method overload entity type traits. - */ - -#ifndef SIMPLYDT_LIB_DAP_METHOD_CALL_STRUCT_TRAITS_H_ -#define SIMPLYDT_LIB_DAP_METHOD_CALL_STRUCT_TRAITS_H_ - -#include "simplydt/dap/overload/method.hpp" -#include -#include -#include - -namespace simplydt::type_traits -{ - // TYPE TRAIT : has_static_call_signature - - /*! - * @brief - * Determine if type T has an appropriate static - * 'call()' method defined. - */ - template - struct has_static_call_signature; - - /*! - * @brief - * Check that type T has a static method named 'call' - * that respects return and argument types specified. - * - * @details - * This helper trait does not invoke the `T::call()` - * method for verification. Instead it uses - * `std::declval` which simulates the call with the - * provided parameter types in an unevaluated context - * to determine the legality of the expression. - */ - template - struct has_static_call_signature_impl : std::bool_constant < requires { - { - T::call(std::declval()...) - } -> std::convertible_to; - } > { }; - - template - struct has_static_call_signature> { - static constexpr bool - value = has_static_call_signature_impl::value; - }; - - template - struct has_static_call_signature { - static constexpr bool value = false; - }; - - /*! - * @brief - * Determine if type T has an appropriate static - * 'call()' method defined. - */ - template - inline constexpr bool - has_static_call_signature_v = has_static_call_signature:: - value; - -} // namespace simplydt::type_traits - -namespace simplydt::type_traits -{ - // TYPE TRAIT : is_overload_derived - - /*! - * @brief - * Determine if type T inherits from method overload - * struct. - */ - template - struct is_overload_derived; - - template - struct is_overload_derived> { - static constexpr bool value = std::is_base_of_v, T>; - }; - - template - struct is_overload_derived { - static constexpr bool value = false; - }; - - /*! - * @brief - * Determine if type T inherits from method overload - * struct. - */ - template - inline constexpr bool - is_overload_derived_v = is_overload_derived::value; - -} // namespace simplydt::type_traits - -#endif // SIMPLYDT_LIB_DAP_METHOD_CALL_STRUCT_TRAITS_H_ diff --git a/include/simplydt/gregorian_calendar/date_flags.hpp b/include/simplydt/gregorian_calendar/date_flags.hpp deleted file mode 100644 index 21b3fc4..0000000 --- a/include/simplydt/gregorian_calendar/date_flags.hpp +++ /dev/null @@ -1,128 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -/*! - * @file date_flags.hpp - * - * @brief - * Gregorian calendar date flags. - * - * @details - * Gregorian calendar date characteristic flags. - */ - -#ifndef SIMPLYDT_LIB_GREGORIAN_DATE_FLAGS_H_ -#define SIMPLYDT_LIB_GREGORIAN_DATE_FLAGS_H_ - -#include - -namespace simplydt::gregorian -{ - - /*! - * @brief - * Gregorian calendar date flags. - * - * @details - * These flags describe structural characteristics - * of a Gregorian date that can be derived from the - * Gregorian calendar system independently. - */ - enum class DateFlag : uint32_t { - NONE = 0, ///< No date flags - INVALID = 1 << 0, ///< Invalid date - - CALENDAR_Q1 = 1 << 2, ///< First quarter of calendar year (Jan-Mar) - CALENDAR_Q2 = 1 << 3, ///< Second quarter of calendar year (Apr-Jun) - CALENDAR_Q3 = 1 << 4, ///< Third quarter of calendar year (Jul-Sep) - CALENDAR_Q4 = 1 << 5, ///< Fourth quarter of calendar year (Oct-Dec) - - START_OF_YEAR = 1 << 6, ///< January - MIDDLE_OF_YEAR = 1 << 7, ///< June - END_OF_YEAR = 1 << 8, ///< December - - LEAP_YEAR = 1 << 9, ///< Leap year - - START_OF_MONTH = 1 << 10, ///< First day of month - MIDDLE_OF_MONTH = 1 << 11, ///< Median day of month - END_OF_MONTH = 1 << 12, ///< Last day of month - - WEEKDAY = 1 << 13, ///< Weekday date (Mon-Fri) - WEEKEND = 1 << 14, ///< Weekend date (Sat/Sun) - - START_OF_WEEK = 1 << 15, ///< Beginning of week (Sun) - START_OF_WEEKDAYS = 1 << 16, ///< Beginning of weekdays (Mon) - MIDDLE_OF_WEEK = 1 << 17, ///< Wednesday - END_OF_WEEKDAYS = 1 << 18, ///< End of weekdays (Fri) - END_OF_WEEK = 1 << 19, ///< End of week (Sat) - - LEAP_DAY = 1 << 20 ///< February 29th - }; - - /*! - * @brief - * Combine date flag values using bitwise OR. - * - * @details - * Returns a `DateFlag` with bits set where either - * input flag has a bit set. - * - * @return - * Bit combined date flag - */ - [[nodiscard]] inline DateFlag operator|(const DateFlag lhs, const DateFlag rhs) noexcept - { - return static_cast(static_cast(lhs) | static_cast(rhs)); - } - - /*! - * @brief - * Computes bitwise AND of date flags. - * - * @details - * Returns a `DateFlag` with bits set only where both - * input flags have bits set. - * - * @return - * Bit filtered date flag - */ - [[nodiscard]] inline DateFlag operator&(const DateFlag lhs, const DateFlag rhs) noexcept - { - return static_cast(static_cast(lhs) & static_cast(rhs)); - } - - /*! - * @brief - * Computes bitwise complement of date flag. - * - * @details - * Returns a `DateFlag` with all bits flipped relative - * to the input. - * - * @return - * Bit negated date flag - */ - [[nodiscard]] inline DateFlag operator~(DateFlag flag) noexcept - { - return static_cast(~static_cast(flag)); - } - - /*! - * @brief - * Combine date flag values in-place using bitwise OR. - * - * @return - * Reference to left-hand side date flag - */ - [[nodiscard]] inline DateFlag& operator|=(DateFlag& lhs, const DateFlag rhs) noexcept - { - lhs = lhs | rhs; - return lhs; - } - -} // namespace simplydt::gregorian - -#endif // SIMPLYDT_LIB_GREGORIAN_DATE_FLAGS_H_ diff --git a/include/simplydt/gregorian_calendar/gregorian_date.hpp b/include/simplydt/gregorian_calendar/gregorian_date.hpp deleted file mode 100644 index 2e39820..0000000 --- a/include/simplydt/gregorian_calendar/gregorian_date.hpp +++ /dev/null @@ -1,255 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -/*! - * @file gregorian_date.hpp - * - * @brief - * Standalone Gregorian calendar date declaration. - */ - -#ifndef SIMPLYDT_LIB_GREGORIAN_DATE_STRUCT_H_ -#define SIMPLYDT_LIB_GREGORIAN_DATE_STRUCT_H_ - -#include "simplydt/common/general_defs.hpp" -#include "simplydt/common/string_utils.hpp" -#include "simplydt/gregorian_calendar/gregorian_defs.hpp" -#include - -namespace simplydt::gregorian -{ - - /*! - * @brief - * Broken-down Gregorian calendar date. - * - * @details - * This type is meant to serve as a broken-down - * form of Gregorian calendar dates and only - * serves this purpose. This type does not - * validate the date values it is constructed - * with beyond disregarding nonsensical values. - * Such values are the kind that could obviously - * never be a date (i.e, August 32nd, 2015). - * However, keep in mind that just because this - * type accepts a date you provide does not - * necessarily imply that date exists on the - * real-world calendar. - */ - class Date { - - public: - /*! - * @brief - * Underlying type used to represent Gregorian - * calendar dates. - */ - using Underlying_T = uint32_t; - - /*! - * @brief - * Factor used to store and retrieve year values. - */ - static constexpr Underlying_T YEAR_FACTOR = 10'000; - - /*! - * @brief - * Factor used to store and retrieve month values. - */ - static constexpr Underlying_T MONTH_FACTOR = 100; - - /*! - * @brief - * Default Gregorian calendar date. - */ - static constexpr Underlying_T DEFAULT_DATE = ((1'970 * YEAR_FACTOR) + 101); - - /*! - * @brief - * Store encoded Gregorian calendar date at provided - * integer address. - * - * @return - * True on success - */ - static bool encodeGregorianDateIntoInteger( - Underlying_T* integer, - const YearInt_T& year, - const uint8_t& month, - const uint8_t& day - ) noexcept; - - /*! - * @brief - * Retrieve year component from encoded Gregorian - * date integer. - * - * @return - * Gregorian date year component - */ - static YearInt_T extractEncodedYear(const Underlying_T* date) noexcept; - - /*! - * @brief - * Retrieve month component from encoded Gregorian - * date integer. - * - * @return - * Gregorian date month component - */ - static uint8_t extractEncodedMonth(const Underlying_T* date) noexcept; - - /*! - * @brief - * Retrieve day component from encoded Gregorian - * date integer. - * - * @return - * Gregorian date day component - */ - static uint8_t extractEncodedDay(const Underlying_T* date) noexcept; - - /*! - * @brief - * Construct Gregorian date using year, month, - * and day values. - */ - Date(const YearInt_T year, const uint8_t month, const uint8_t day) noexcept; - - /*! - * @brief - * Construct Gregorian date using year and month - * values; assume first of month. - */ - Date(const YearInt_T year, const uint8_t month) noexcept; - - /*! - * @brief - * Construct Gregorian date from another. - */ - Date(const Date& date) noexcept; - - /*! - * @brief - * Construct default Gregorian date. - */ - Date() noexcept; - - ~Date() noexcept = default; - - friend inline std::ostream& operator<<(std::ostream& os, const Date date) noexcept - { - os << date.toStr(); - return os; - } - - /*! @brief Evaluate equivalence of Gregorian dates. */ - [[nodiscard]] bool operator==(const Date date) const noexcept; - - /*! @brief Determine if left-hand side is sequentially before right-hand side. */ - [[nodiscard]] bool operator<(const Date date) const noexcept; - - /*! @brief Determine if left-hand side is sequentially after right-hand side. */ - [[nodiscard]] bool operator>(const Date date) const noexcept; - - [[nodiscard]] bool operator<=(const Date date) const noexcept; - - [[nodiscard]] bool operator>=(const Date date) const noexcept; - - /*! - * @brief - * Returns requested date component. - * - * @return - * Individual date component value - */ - [[nodiscard]] uint16_t operator[](const CalendarComponent component) const noexcept; - - /*! - * @brief - * Date year component. - * - * @return - * Year of Gregorian calendar date - */ - [[nodiscard]] YearInt_T year() const noexcept; - - /*! - * @brief - * Date month component. - * - * @return - * Month of Gregorian calendar date - */ - [[nodiscard]] uint8_t month() const noexcept; - - /*! - * @brief - * Month name. - * - * @return - * Month literal - */ - [[nodiscard]] std::string monthLiteral() const noexcept; - - /*! - * @brief - * Abbreviated month name. - * - * @return - * Abbreviated month literal - */ - [[nodiscard]] std::string monthAbbreviation() const noexcept; - - /*! - * @brief - * Date day component. - * - * @return - * Day of Gregorian calendar date - */ - [[nodiscard]] uint8_t day() const noexcept; - - /*! - * @brief - * Returns requested Gregorian date component. - * - * @details - * If the requested calendar component is not a - * date component the method returns 0. - * - * @return - * Individual date component value - */ - [[nodiscard]] uint16_t getComponent(const CalendarComponent component) const noexcept; - - /*! - * @brief - * Compose string representation of Gregorian - * calendar date. - * - * @return - * Gregorian calendar date as string - */ - [[nodiscard]] std::string toStr() const noexcept; - - /*! - * @brief - * Returns raw underlying representation of - * Gregorian calendar date. - * - * @return - * Gregorian calendar date as integer - */ - [[nodiscard]] Underlying_T underlying() const noexcept; - - private: - Underlying_T date; ///< Calendar date - }; - -} // namespace simplydt::gregorian - -#endif // SIMPLYDT_LIB_GREGORIAN_DATE_STRUCT_H_ diff --git a/include/simplydt/gregorian_calendar/gregorian_defs.hpp b/include/simplydt/gregorian_calendar/gregorian_defs.hpp deleted file mode 100644 index 115e7ff..0000000 --- a/include/simplydt/gregorian_calendar/gregorian_defs.hpp +++ /dev/null @@ -1,168 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -/*! - * @file gregorian_defs.hpp - * - * @brief - * Gregorian calendar system definitions. - */ - -#ifndef SIMPLYDT_LIB_GREGORIAN_CALENDAR_DEFINITIONS_H_ -#define SIMPLYDT_LIB_GREGORIAN_CALENDAR_DEFINITIONS_H_ - -#include -#include -#include - -/*! - * @namespace simplydt::gregorian - * - * @brief - * Standard civil calendar system. - */ -namespace simplydt::gregorian -{ - - /*! - * @brief - * Gregorian year integer type. - * - * @note - * The underlying type used to represent Gregorian - * calendar years imposes a limit on the range of - * representable dates. - */ - using YearInt_T = uint16_t; - - /*! - * @brief - * Enumeration of Gregorian calendar months. - */ - enum Month : uint8_t { - JANUARY, ///< January (1) - FEBRUARY, ///< February (2) - MARCH, ///< March (3) - APRIL, ///< April (4) - MAY, ///< May (5) - JUNE, ///< June (6) - JULY, ///< July (7) - AUGUST, ///< August (8) - SEPTEMBER, ///< September (9) - OCTOBER, ///< October (10) - NOVEMBER, ///< November (11) - DECEMBER ///< December (12) - }; - - /*! - * @brief - * Enumeration of Gregorian calendar days of week. - */ - enum DayOfWeek : uint8_t { - SUNDAY, ///< Sunday - MONDAY, ///< Monday - TUESDAY, ///< Tuesday - WEDNESDAY, ///< Wednesday - THURSDAY, ///< Thursday - FRIDAY, ///< Friday - SATURDAY ///< Saturday - }; - - /*! - * @brief - * Array of Gregorian calendar month name literals. - */ - constexpr std::array Months = { - "January", ///< Index 0 - "February", ///< Index 1 - "March", ///< Index 2 - "April", ///< Index 3 - "May", ///< Index 4 - "June", ///< Index 5 - "July", ///< Index 6 - "August", ///< Index 7 - "September", ///< Index 8 - "October", ///< Index 9 - "November", ///< Index 10 - "December" ///< Index 11 - }; - - /*! - * @brief - * Array of abbreviated Gregorian calendar month - * names. - */ - constexpr std::array MonthAbbrevs = { - std::string_view(Months[JANUARY], 3), ///< Jan - std::string_view(Months[FEBRUARY], 3), ///< Feb - std::string_view(Months[MARCH], 3), ///< Mar - std::string_view(Months[APRIL], 3), ///< Apr - std::string_view(Months[MAY], 3), ///< May - std::string_view(Months[JUNE], 3), ///< Jun - std::string_view(Months[JULY], 3), ///< Jul - std::string_view(Months[AUGUST], 3), ///< Aug - std::string_view(Months[SEPTEMBER], 3), ///< Sep - std::string_view(Months[OCTOBER], 3), ///< Oct - std::string_view(Months[NOVEMBER], 3), ///< Nov - std::string_view(Months[DECEMBER], 3) ///< Dec - }; - - /*! - * @brief - * Array of Gregorian calendar day-of-week literals. - */ - constexpr std::array DaysOfWeek = { - "Sunday", ///< Index 0 - "Monday", ///< Index 1 - "Tuesday", ///< Index 2 - "Wednesday", ///< Index 3 - "Thursday", ///< Index 4 - "Friday", ///< Index 5 - "Saturday" ///< Index 6 - }; - - /*! - * @brief - * Array of abbreviated Gregorian calendar day-of-week - * literals. - */ - constexpr std::array DayOfWeekAbbrevs = { - std::string_view(DaysOfWeek[SUNDAY], 3), ///< Sun - std::string_view(DaysOfWeek[MONDAY], 3), ///< Mon - std::string_view(DaysOfWeek[TUESDAY], 3), ///< Tue - std::string_view(DaysOfWeek[WEDNESDAY], 3), ///< Wed - std::string_view(DaysOfWeek[THURSDAY], 3), ///< Thu - std::string_view(DaysOfWeek[FRIDAY], 3), ///< Fri - std::string_view(DaysOfWeek[SATURDAY], 3) ///< Sat - }; - - /*! - * @brief - * Minimum day of Gregorian month. - */ - constexpr uint8_t MIN_DAY_OF_MONTH = 1; - - /*! - * @brief - * Maximum day of Gregorian month. - */ - constexpr uint8_t MAX_DAY_OF_MONTH = 31; - - /*! - * @brief - * Minimum month of Gregorian year. - */ - constexpr uint8_t MIN_MONTH_OF_YEAR = 1; - - /*! - * @brief - * Maximum month of Gregorian year. - */ - constexpr uint8_t MAX_MONTH_OF_YEAR = 12; - -} // namespace simplydt::gregorian - -#endif // SIMPLYDT_LIB_GREGORIAN_CALENDAR_DEFINITIONS_H_ diff --git a/include/simplydt/julian_calendar/julian_defs.hpp b/include/simplydt/julian_calendar/julian_defs.hpp deleted file mode 100644 index ea9a653..0000000 --- a/include/simplydt/julian_calendar/julian_defs.hpp +++ /dev/null @@ -1,39 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -/*! - * @file julian_defs.hpp - * - * @brief - * Julian calendar system definitions. - */ - -#ifndef SIMPLYDT_LIB_JULIAN_CALENDAR_DEFINITIONS_H_ -#define SIMPLYDT_LIB_JULIAN_CALENDAR_DEFINITIONS_H_ - -/*! - * @namespace simplydt::julian - * - * @brief - * Proleptic Julian calendar system. - */ -namespace simplydt::julian -{ - - /*! - * @brief - * Julian day number value type. - * - * @note - * The underlying type used to represent Julian - * day numbers imposes a limit on the level of - * time precision. - */ - using Jdn_T = double; - -} - -#endif // SIMPLYDT_LIB_JULIAN_CALENDAR_DEFINITIONS_H_ diff --git a/include/simplydt/time/abstract_time.hpp b/include/simplydt/time/abstract_time.hpp new file mode 100644 index 0000000..102312e --- /dev/null +++ b/include/simplydt/time/abstract_time.hpp @@ -0,0 +1,410 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file abstract_time.hpp + * + * @brief + * Base serial time standard declaration. + */ + + +#ifndef SIMPLYDT_LIB_BASE_SERIAL_TIME_SYSTEM_H_ +#define SIMPLYDT_LIB_BASE_SERIAL_TIME_SYSTEM_H_ + +#include "simplydt/common/time_defs.hpp" +#include "simplydt/time/units/time_units.hpp" + +namespace simplydt +{ + +/*! + * @brief + * Base time standard interface. + * + * @details + * This is the foundational interface for serial time in + * Simply Datetime. This class is agnostic of any specific + * time standard and avoids assumptions on underlying time + * units used to represent a time in day. This type holds + * both time standard knowledge and state. Simply Datetime + * relies on the object invariant that any constructed + * instance of this type represents a valid time of day. + * Each implementation is responsible for enforcing this + * invariant to prevent bugs and undefined behavior. This + * structure expects timekeeping to be conducted as a serial + * count of a time unit relative to midnight (0 = 00:00:00 + * AM). The provided underlying serial time representation + * type `Repr_T` is initialized in this base class but can + * also be accessed by the derived time implementation. The + * class hierarchy implements the CRTP design pattern which + * allows this base to reference the derivative. Consequently, + * the convenience methods defined in this base structure + * depend on the concrete time standards public API for them + * to be well-formed. A derivative is expected to present + * the appropriate attributes and methods, which can be + * verified by invoking the contract enforcement macro + * (`SIMPLYDT_ENFORCE_TIME_CONTRACT`) just after the body of + * the implementation. Failing to have a compliant API can + * result in substitution errors or undefined behavior. This + * is not a self-constructable type, it must be inherited by + * a concrete implementation that presents the expected + * public API. + */ +template +struct SerialTimeStandard { + /*! @brief Time standard base class. */ + using Base = SerialTimeStandard; + /*! @brief Time resolution unit type. */ + using Unit_Resolution = Resolution_T; + /*! @brief Underlying serial time type. */ + using Repr_Type = Repr_T; + + /*! + * @brief + * Converts hour, minute, and second values to a + * count of seconds since start of day. + * + * @return + * Seconds since midnight (00:00:00 AM) + */ + [[nodiscard]] static constexpr uint32_t toSerialSeconds( + const uint8_t hr, const uint8_t min, const uint8_t sec + ) noexcept + { + if ((hr >= HOURS_IN_DAY) || (min >= MINUTES_IN_HOUR) || (sec >= SECONDS_IN_MINUTE)) + return MIDNIGHT; // Invalid time parameters + + const uint32_t secsSinceMidnight = + (hr * SECONDS_IN_HOUR) + (min * SECONDS_IN_MINUTE) + sec; + return secsSinceMidnight; + } + + /*! + * @brief + * Calculates hour of time from serial count of + * seconds since start of day. + * + * @return + * Time hour component + */ + [[nodiscard]] static constexpr uint8_t hourFromSerialSecs(const uint32_t serial_secs + ) noexcept + { + if (serial_secs >= SECONDS_IN_DAY) + return 0; // Invalid serial seconds count + + return static_cast(serial_secs / SECONDS_IN_HOUR); + } + + /*! + * @brief + * Calculates minute of time from serial count of + * seconds since start of day. + * + * @return + * Time minute component + */ + [[nodiscard]] static constexpr uint8_t minuteFromSerialSecs(const uint32_t serial_secs + ) noexcept + { + if (serial_secs >= SECONDS_IN_DAY) + return 0; // Invalid serial seconds count + + return static_cast((serial_secs % SECONDS_IN_HOUR) / SECONDS_IN_MINUTE); + } + + /*! + * @brief + * Calculates second of time from serial count of + * seconds since start of day. + * + * @return + * Time second component + */ + [[nodiscard]] static constexpr uint8_t secondFromSerialSecs(const uint32_t serial_secs + ) noexcept + { + if (serial_secs >= SECONDS_IN_DAY) + return 0; // Invalid serial seconds count + + return static_cast(serial_secs % SECONDS_IN_MINUTE); + } + + /*! @brief Puts human-readable time string in output stream. */ + friend inline std::ostream& operator<<(std::ostream& os, const Base& serial_time) noexcept + { + os << serial_time.derivedImpl().toStr(); + return os; + } + + /*! @brief Evaluates equivalence of times. */ + [[nodiscard]] constexpr bool operator==(const Base& serial_time) const noexcept + { + return this->serialUnits == serial_time.serialUnits; + } + + /*! @brief Determines if left-hand side is sequentially before right-hand side. */ + [[nodiscard]] constexpr bool operator<(const Base& serial_time) const noexcept + { + return this->serialUnits < serial_time.serialUnits; + } + + /*! @brief Determines if left-hand side is sequentially after right-hand side. */ + [[nodiscard]] constexpr bool operator>(const Base& serial_time) const noexcept + { + return this->serialUnits > serial_time.serialUnits; + } + + [[nodiscard]] constexpr bool operator<=(const Base& serial_time) const noexcept + { + return this->serialUnits <= serial_time.serialUnits; + } + + [[nodiscard]] constexpr bool operator>=(const Base& serial_time) const noexcept + { + return this->serialUnits >= serial_time.serialUnits; + } + + /*! @brief Returns this time with provided amount of time units added. */ + [[nodiscard]] constexpr Standard_Impl operator+(const Unit_Resolution units) const noexcept + { + Standard_Impl displacedTime{this->derivedImpl()}; + displacedTime.serialUnits += units.count(); + return displacedTime; + } + + /*! @brief Returns this time with provided amount of time units subtracted. */ + [[nodiscard]] constexpr Standard_Impl operator-(const Unit_Resolution units) const noexcept + { + Standard_Impl displacedTime{this->derivedImpl()}; + displacedTime.serialUnits -= units.count(); + return displacedTime; + } + + /*! @brief Calculates number of precision time units between times. */ + [[nodiscard]] constexpr Unit_Resolution operator-(const Standard_Impl time) const noexcept + { + return this->units() - time.units(); + } + + /*! + * @brief + * Increments time one unit of precision. + * + * @return + * Reference to self + */ + Standard_Impl& operator++() noexcept + { + this->serialUnits += 1; + return static_cast(*this); + } + + /*! + * @brief + * Increments time one unit of precision. + * + * @return + * Copy of previous time + */ + [[nodiscard]] Standard_Impl operator++(int) noexcept + { + Standard_Impl previousTime{this->derivedImpl()}; + this->serialUnits += 1; + return previousTime; + } + + /*! @brief Adds provided amount of units to time. */ + Standard_Impl& operator+=(const Unit_Resolution units) noexcept + { + this->serialUnits += units.count(); + return static_cast(*this); + } + + /*! + * @brief + * Decrements time one unit of precision. + * + * @return + * Reference to self + */ + Standard_Impl& operator--() noexcept + { + this->serialUnits -= 1; + return static_cast(*this); + } + + /*! + * @brief + * Decrements time one unit of precision. + * + * @return + * Copy of previous time + */ + [[nodiscard]] Standard_Impl operator--(int) noexcept + { + Standard_Impl previousTime{this->derivedImpl()}; + this->serialUnits -= 1; + return previousTime; + } + + /*! @brief Subtracts provided amount of units from time. */ + Standard_Impl& operator-=(const Unit_Resolution units) noexcept + { + this->serialUnits -= units.count(); + return static_cast(*this); + } + + /*! + * @brief + * Determines if time is midnight. + * + * @return + * True if serial time count is zero + */ + [[nodiscard]] constexpr bool isZero() const noexcept + { + return this->serialUnits == 0; + } + + /*! + * @brief + * Determines if time is sequentially before provided + * time. + * + * @return + * True if this time occurs before provided + */ + [[nodiscard]] constexpr bool isBefore(const Standard_Impl time) const noexcept + { + return this->serialUnits < time.serialUnits; + } + + /*! + * @brief + * Determines if time is sequentially after provided + * time. + * + * @return + * True if this time occurs after provided + */ + [[nodiscard]] constexpr bool isAfter(const Standard_Impl time) const noexcept + { + return this->serialUnits > time.serialUnits; + } + + /*! + * @brief + * Checks if this time falls within a specified + * time range. + * + * @details + * This function compares the serial time count of + * the current time instance against two provided + * times, expressed in the same serial-time format. + * The comparison is inclusive, meaning the + * function returns true if this time is equal to + * either boundary time or lies strictly between + * them. + * + * @return + * True if time lies within inclusive time range + */ + [[nodiscard]] constexpr bool isBetween( + const Standard_Impl start_time, const Standard_Impl end_time + ) const noexcept + { + return start_time.serialUnits <= this->serialUnits && + this->serialUnits <= end_time.serialUnits; + } + + /*! + * @brief + * Returns serial time count as explicit units + * of precision. + * + * @details + * This function exposes the underlying serial time + * count of the current time instance, wrapped in + * the standards units of precision. The value + * represents the total number of units elapsed since + * midnight (00:00:00 AM). + * + * @return + * Serial time count + */ + [[nodiscard]] constexpr Unit_Resolution units() const noexcept + { + return Unit_Resolution{this->serialUnits}; + } + + /*! + * @brief + * Returns constant reference to underlying serial + * time count. + * + * @note + * The units of measure used by the value returned + * depend on the current time implementation. + * + * @return + * Constant reference to serial time count + */ + [[nodiscard]] constexpr const Repr_Type& underlying() const noexcept + { + return this->serialUnits; + } + + private: + Repr_Type serialUnits; ///< Serial time of day + + /*! + * @brief + * Intercepts negative units and returns absolute + * units. + * + * @return + * Absolute unit count + */ + [[nodiscard]] static constexpr Repr_Type absoluteCount(const Unit_Resolution serial_count + ) noexcept + { + return static_cast( + (serial_count.count() < 0) ? serial_count.count() * -1 : serial_count.count() + ); + } + + /*! @brief Construct time of day with serial time units. */ + constexpr SerialTimeStandard(const Repr_Type serial_units) noexcept + : serialUnits{serial_units} + { } + + /*! @brief Construct time of day with serial time units. */ + constexpr SerialTimeStandard(const Unit_Resolution serial_units) noexcept + : serialUnits{absoluteCount(serial_units)} + { } + + ~SerialTimeStandard() = default; + friend Standard_Impl; + + /*! + * @brief + * Returns constant reference to this concrete derivative. + * + * @note + * Do not call this from the derived class, YOU are the + * `derivedImpl()` (a.k.a `this`) + */ + [[nodiscard]] constexpr const Standard_Impl& derivedImpl() const noexcept + { + return static_cast(*this); + } +}; + +} // namespace simplydt + +#endif // SIMPLYDT_LIB_BASE_SERIAL_TIME_SYSTEM_H_ diff --git a/include/simplydt/time/concepts/time_api_contract.hpp b/include/simplydt/time/concepts/time_api_contract.hpp new file mode 100644 index 0000000..d4b053a --- /dev/null +++ b/include/simplydt/time/concepts/time_api_contract.hpp @@ -0,0 +1,65 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file time_api_contract.hpp + * + * @brief + * Conceptual temporaal time implementation contract. + */ + + +#ifndef SIMPLYDT_LIB_TEMPORAL_TIME_CONTRACT_CONCEPT_H_ +#define SIMPLYDT_LIB_TEMPORAL_TIME_CONTRACT_CONCEPT_H_ + +#include "simplydt/time/concepts/time_concepts.hpp" + +namespace simplydt::concepts +{ + +/*! + * @brief + * Concept of a type that meets the criteria to be + * considered a useable time standard implementation. + * + * @details + * A valid time standard type must provide contextual + * nested types (for library internal-use), time + * component accessors, and support for logical and + * arithmetic operators. It must include basic state + * management and sequential evaluation methods, as + * well as stream output and string conversion + * capabilities. Additionally, it must satisfy + * fundamental C++ type requirements including default + * initialization, copyability, and destructibility. + */ +template +concept contract_abiding_time = requires { + requires time::has_contextual_nested_types; + requires time::has_standard_time_component_methods; + requires time::has_logical_operators; + requires time::has_arithmetic_operators; + requires time::has_basic_state_methods; + requires time::has_sequential_evaluation_methods; + requires time::is_stream_out_compatible; + requires time::has_time_string_methods; + requires std::default_initializable; + requires std::copyable; + requires std::destructible; +}; + +} // namespace simplydt::concepts + +#ifndef SIMPLYDT_ENFORCE_TIME_CONTRACT +/*! @brief Macro for asserting time implementation interface contract. */ +# define SIMPLYDT_ENFORCE_TIME_CONTRACT(Class) \ + static_assert( \ + simplydt::concepts::contract_abiding_time, \ + #Class " implementation does not fulfill the public API contract." \ + ) +#endif + +#endif // SIMPLYDT_LIB_TEMPORAL_TIME_CONTRACT_CONCEPT_H_ diff --git a/include/simplydt/time/concepts/time_concepts.hpp b/include/simplydt/time/concepts/time_concepts.hpp new file mode 100644 index 0000000..db67fd5 --- /dev/null +++ b/include/simplydt/time/concepts/time_concepts.hpp @@ -0,0 +1,94 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file time_concepts.hpp + * + * @brief + * Agnostic temporal time concepts. + */ + + +#ifndef SIMPLYDT_LIB_TEMPORAL_TIME_CONCEPTS_H_ +#define SIMPLYDT_LIB_TEMPORAL_TIME_CONCEPTS_H_ + +#include +#include +#include + +/*! + * @namespace simplydt::concepts::time + * + * @brief + * Time concepts. + */ +namespace simplydt::concepts::time +{ + +template +concept has_contextual_nested_types = requires { + typename Time_Impl::Repr_Type; + typename Time_Impl::Unit_Resolution; +}; + +template +concept has_standard_time_component_methods = requires(const Time_Impl& t) { + { t.hour() } -> std::same_as; + { t.minute() } -> std::same_as; + { t.second() } -> std::same_as; +}; + +template +concept has_logical_operators = requires(const Time_Impl& t) { + { t == t } -> std::same_as; + { t < t } -> std::same_as; + { t > t } -> std::same_as; + { t <= t } -> std::same_as; + { t >= t } -> std::same_as; +}; + +template +concept has_arithmetic_operators = + requires(Time_Impl& t, typename Time_Impl::Unit_Resolution units) { + { t + units } -> std::same_as; + { t - units } -> std::same_as; + { t - t } -> std::same_as; + { t += units } -> std::same_as; + { t -= units } -> std::same_as; + { ++t } -> std::same_as; + { t++ } -> std::same_as; + { --t } -> std::same_as; + { t-- } -> std::same_as; + }; + +template +concept has_basic_state_methods = requires(const Time_Impl& t) { + { t.isZero() } -> std::same_as; + { t.units() } -> std::same_as; + { t.underlying() } -> std::same_as; +}; + +template +concept is_stream_out_compatible = requires(std::ostream& os, const Time_Impl& t) { + { os << t } -> std::convertible_to; +}; + +template +concept has_sequential_evaluation_methods = requires(const Time_Impl& t) { + { t.isBefore(t) } -> std::same_as; + { t.isAfter(t) } -> std::same_as; + { t.isBetween(t, t) } -> std::same_as; +}; + +template +concept has_time_string_methods = requires(const Time_Impl& t) { + { t.toStr() } -> std::same_as; + { t.hourPhaseStr() } -> std::same_as; +}; + +} // namespace simplydt::concepts::time + +#endif // SIMPLYDT_LIB_TEMPORAL_TIME_CONCEPTS_H_ diff --git a/include/simplydt/time/units/time_units.hpp b/include/simplydt/time/units/time_units.hpp new file mode 100644 index 0000000..447d8d7 --- /dev/null +++ b/include/simplydt/time/units/time_units.hpp @@ -0,0 +1,38 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file time_units.hpp + * + * @brief + * Aliases of individual units of time. + */ + + +#ifndef SIMPLYDT_LIB_TIME_UNITS_H_ +#define SIMPLYDT_LIB_TIME_UNITS_H_ + +#include "simplydt/common/time_defs.hpp" +#include + +namespace simplydt +{ + +/*! @brief Seconds time unit. */ +using Seconds = std::chrono::duration>; + +/*! @brief Minutes time unit. */ +using Minutes = std::chrono::duration>; + +/*! @brief Hours time unit. */ +using Hours = std::chrono::duration>; + +/*! @brief Days time unit. */ +using Days = std::chrono::duration>; + +} // namespace simplydt + +#endif // SIMPLYDT_LIB_TIME_UNITS_H_ diff --git a/include/simplydt/time/utc/utc_defs.hpp b/include/simplydt/time/utc/utc_defs.hpp new file mode 100644 index 0000000..fbf1ca8 --- /dev/null +++ b/include/simplydt/time/utc/utc_defs.hpp @@ -0,0 +1,36 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file utc_defs.hpp + * + * @brief + * Coordinated Universal Time (UTC) system + * definitions. + */ + + +#ifndef SIMPLYDT_LIB_COORDINATED_UNIVERSAL_TIME_DEFINITIONS_H_ +#define SIMPLYDT_LIB_COORDINATED_UNIVERSAL_TIME_DEFINITIONS_H_ + +#include +#include +#include + +/*! + * @namespace simplydt::utc + * + * @brief + * Coordinated Universal Time (UTC) standard. + */ +namespace simplydt::utc +{ + +// + +} // namespace simplydt::utc + +#endif // SIMPLYDT_LIB_COORDINATED_UNIVERSAL_TIME_DEFINITIONS_H_ diff --git a/include/simplydt/time/utc/utc_time.hpp b/include/simplydt/time/utc/utc_time.hpp new file mode 100644 index 0000000..231cbd8 --- /dev/null +++ b/include/simplydt/time/utc/utc_time.hpp @@ -0,0 +1,200 @@ + +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Released under the terms of the GNU Affero General Public License version 3. + +// [ISJTB-CXX-XL20230401-000001] + +/*! + * @file utc_time.hpp + * + * @brief + * Coordinated Universal Time (UTC) declaration. + */ + + +#ifndef SIMPLYDT_LIB_COORDINATED_UNIVERSAL_TIME_STANDARD_H_ +#define SIMPLYDT_LIB_COORDINATED_UNIVERSAL_TIME_STANDARD_H_ + +#include "simplydt/time/abstract_time.hpp" +#include "simplydt/time/concepts/time_api_contract.hpp" +#include "simplydt/time/utc/utc_defs.hpp" + +namespace simplydt::utc +{ + +/*! + * @brief + * Coordinated Universal Time. + * + * @details + * TODO: INCOMPLETE COMMENT!!! + */ +struct UTCTime : public SerialTimeStandard { + /*! + * @brief + * Identifies the time system represented by this + * implementation. + */ + static constexpr TimeStandard standard = TimeStandard::UTC; + + /*! + * @brief + * Indicates the resolution this time system is based + * on. + */ + static constexpr TimeComponent resolution = TimeComponent::SECOND; + + /*! + * @brief + * Indicates whether the time system is based on + * celestial motion. + * + * @details + * Specifies that UTC is a celestial time system. + * While its rate is derived from atomic clocks, it is + * fundamentally tied to the Earth's rotation (a + * celestial phenomenon) through the mechanism of leap + * seconds. + */ + static constexpr bool isCelestialTimeSystem = true; + + /*! + * @brief + * Indicates whether the time system is a pure atomic + * time scale. + * + * @details + * Specifies that UTC is not a pure atomic time system + * which is a continuous time scale based solely on + * atomic clocks. + */ + static constexpr bool isPureAtomicTimeSystem = false; + + /*! + * @brief + * Indicates whether the time system is derived from an + * atomic time scale. + */ + static constexpr bool isAtomicDerivedTimeSystem = true; + + /*! + * @brief + * Construct UTC time with hour, minute, and second + * values. + */ + constexpr UTCTime(const uint8_t hour, const uint8_t minute, const uint8_t second) noexcept + : SerialTimeStandard{ + Base::toSerialSeconds(hour, minute, second) + } + { } + + /*! @brief Construct default UTC time (00:00:00 AM) */ + constexpr UTCTime() noexcept : SerialTimeStandard{MIDNIGHT} + { } + + ~UTCTime() = default; + + /*! + * @brief + * Time hour component (24-hour). + * + * @return + * Hour of UTC time + */ + [[nodiscard]] constexpr uint8_t hour() const noexcept + { + return Base::hourFromSerialSecs(this->serialUnits); + } + + /*! + * @brief + * Time hour component (12-hour). + * + * @return + * Hour of UTC time + */ + [[nodiscard]] constexpr uint8_t hour12() const noexcept + { + const uint8_t hour24 = Base::hourFromSerialSecs(this->serialUnits); + return (hour24 % 12) + ((hour24 == 12 || hour24 == 0) * 12); + } + + /*! + * @brief + * Time meridiem phase literal. + * + * @return + * Meridiem phase abbreviation + */ + [[nodiscard]] constexpr const char* hourPhaseStr() const noexcept + { + if (Base::hourFromSerialSecs(this->serialUnits) >= 12) + return MeridiemPhases[PM]; + + return MeridiemPhases[AM]; + } + + /*! + * @brief + * Time meridiem phase indicator. + * + * @return + * Meridiem phase enum representation + */ + [[nodiscard]] constexpr MeridiemPhase hourPhaseEnumRepr() const noexcept + { + if (Base::hourFromSerialSecs(this->serialUnits) >= 12) + return MeridiemPhase::PM; + + return MeridiemPhase::AM; + } + + /*! + * @brief + * Time minute component. + * + * @return + * Minute of UTC time + */ + [[nodiscard]] constexpr uint8_t minute() const noexcept + { + return Base::minuteFromSerialSecs(this->serialUnits); + } + + /*! + * @brief + * Time second component. + * + * @return + * Second of UTC time + */ + [[nodiscard]] constexpr uint8_t second() const noexcept + { + return Base::secondFromSerialSecs(this->serialUnits); + } + + /*! + * @brief + * Compose string representation of UTC time. + * + * @return + * UTC time as string + */ + [[nodiscard]] std::string toStr() const noexcept + { + const char delimiter = ':'; + std::string timeStr; + timeStr.reserve(12); + timeStr += (toDoubleDigitStr(this->hour()) + delimiter); + timeStr += (toDoubleDigitStr(this->minute()) + delimiter); + timeStr += (toDoubleDigitStr(this->second()) + ' '); + timeStr += this->hourPhaseStr(); + return timeStr; + } +}; + +SIMPLYDT_ENFORCE_TIME_CONTRACT(UTCTime); + +} // namespace simplydt::utc + +#endif // SIMPLYDT_LIB_COORDINATED_UNIVERSAL_TIME_STANDARD_H_ diff --git a/lib/metadata/info.h b/lib/metadata/info.h index 1c3c668..96df0c7 100644 --- a/lib/metadata/info.h +++ b/lib/metadata/info.h @@ -1,5 +1,5 @@ -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. // Released under the terms of the GNU Affero General Public License version 3. // [ISJTB-CXX-XL20230401-000001] @@ -22,7 +22,6 @@ extern "C" { - /*! * @brief * Simply Datetime publisher. diff --git a/lib/metadata/templ/info.h.in b/lib/metadata/templ/info.h.in index fb81044..9a5f0ad 100644 --- a/lib/metadata/templ/info.h.in +++ b/lib/metadata/templ/info.h.in @@ -1,5 +1,5 @@ -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. // Released under the terms of the GNU Affero General Public License version 3. // [ISJTB-CXX-XL20230401-000001] @@ -22,7 +22,6 @@ extern "C" { - /*! * @brief * @RESOLVED_SFTW_NAME@ publisher. diff --git a/lib/metadata/templ/version.h.in b/lib/metadata/templ/version.h.in index 816524f..9632985 100644 --- a/lib/metadata/templ/version.h.in +++ b/lib/metadata/templ/version.h.in @@ -1,5 +1,5 @@ -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. // Released under the terms of the GNU Affero General Public License version 3. // [ISJTB-CXX-XL20230401-000001] @@ -22,7 +22,6 @@ extern "C" { - /*! * @brief * Software version major. diff --git a/lib/metadata/version.h b/lib/metadata/version.h index 1ec7632..c92ddd1 100644 --- a/lib/metadata/version.h +++ b/lib/metadata/version.h @@ -1,5 +1,5 @@ -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. +// Copyright (C) 2026 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. // Released under the terms of the GNU Affero General Public License version 3. // [ISJTB-CXX-XL20230401-000001] @@ -22,7 +22,6 @@ extern "C" { - /*! * @brief * Software version major. @@ -33,7 +32,7 @@ const uint8_t SIMPLYDT_VERSION_MAJOR = 0; * @brief * Software version minor. */ -const uint8_t SIMPLYDT_VERSION_MINOR = 0; +const uint8_t SIMPLYDT_VERSION_MINOR = 1; /*! * @brief diff --git a/scripts/build.sh b/scripts/build similarity index 100% rename from scripts/build.sh rename to scripts/build diff --git a/scripts/clang-format.sh b/scripts/clang-format similarity index 100% rename from scripts/clang-format.sh rename to scripts/clang-format diff --git a/scripts/configure.sh b/scripts/configure similarity index 100% rename from scripts/configure.sh rename to scripts/configure diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 06c6ce5..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ - -#================================ -# /src Directory Script -#================================ - -include(utility/project_tools) - -set(MAIN_BINARY_NAME "${${PRJ_SCOPE}_MAIN_BINARY_NAME}") - -# Simply Datetime Library Target -add_library("${MAIN_BINARY_NAME}" STATIC) - -target_include_directories( - ${MAIN_BINARY_NAME} - - PUBLIC - ${PRJ_INCLUDE_DIRS} -) - -# Set library-wide C++ standard -use_project_cxx_standard(GLOBAL_CXX_OPTIONS INTERFACE) - -target_link_options( - GLOBAL_CXX_OPTIONS - - INTERFACE - ${GLOBAL_LINK_FLAGS} -) - -# Sources -add_subdirectory(gregorian_calendar) -add_subdirectory(coord_universal_time) - -target_link_libraries( - ${MAIN_BINARY_NAME} - - PUBLIC - GLOBAL_CXX_OPTIONS -) diff --git a/src/coord_universal_time/CMakeLists.txt b/src/coord_universal_time/CMakeLists.txt deleted file mode 100644 index 15784dd..0000000 --- a/src/coord_universal_time/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ - -#===================================================== -# /src/coord_universal_time Directory Script -#===================================================== - -target_sources( - ${MAIN_BINARY_NAME} - - PUBLIC - "${CMAKE_CURRENT_SOURCE_DIR}/utc_time.cpp" -) diff --git a/src/coord_universal_time/utc_time.cpp b/src/coord_universal_time/utc_time.cpp deleted file mode 100644 index 1b8af48..0000000 --- a/src/coord_universal_time/utc_time.cpp +++ /dev/null @@ -1,179 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -#include "simplydt/coord_universal_time/utc_time.hpp" - -// simplydt::utc::Time : CONSTRUCTOR - -simplydt::utc::Time::Time( - const uint8_t hour, const uint8_t minute, const uint8_t second -) noexcept - : time{ DEFAULT_TIME } -{ - Time::encodeUTCTimeIntoInteger(&this->time, hour, minute, second); -} - -simplydt::utc::Time::Time(const uint8_t hour, const uint8_t minute) noexcept - : time{ DEFAULT_TIME } -{ - Time::encodeUTCTimeIntoInteger(&this->time, hour, minute, 0); -} - -simplydt::utc::Time::Time(const Time& time) noexcept : time{ time.time } -{ - // -} - -simplydt::utc::Time::Time() noexcept : time{ DEFAULT_TIME } -{ - // -} - -// simplydt::utc::Time : CONSTRUCTOR END! - - -// simplydt::utc::Time : STATIC - -bool simplydt::utc::Time::encodeUTCTimeIntoInteger( - Underlying_T* integer, const uint8_t& hour, const uint8_t& minute, const uint8_t& second -) noexcept -{ - if (integer == nullptr) - return false; - - if ((hour > MAX_HOURS_IN_DAY) || (minute > MAX_MINUTES_IN_HOUR) || - (second > MAX_SECONDS_IN_MINUTE)) - return false; // Invalid UTC time params - - *integer = ((hour * HOUR_FACTOR) + (minute * MINUTE_FACTOR) + second); - - return true; -} - -uint8_t simplydt::utc::Time::extractEncodedHour(const Underlying_T* time) noexcept -{ - return static_cast(*time / HOUR_FACTOR); -} - -uint8_t simplydt::utc::Time::extractEncodedMinute(const Underlying_T* time) noexcept -{ - return static_cast((*time % HOUR_FACTOR) / MINUTE_FACTOR); -} - -uint8_t simplydt::utc::Time::extractEncodedSecond(const Underlying_T* time) noexcept -{ - return static_cast((*time % HOUR_FACTOR) % MINUTE_FACTOR); -} - -// simplydt::utc::Time : STATIC END! - - -// simplydt::utc::Time : OPERATOR - -bool simplydt::utc::Time::operator==(const Time time) const noexcept -{ - return this->time == time.time; -} - -bool simplydt::utc::Time::operator<(const Time time) const noexcept -{ - return this->time < time.time; -} - -bool simplydt::utc::Time::operator>(const Time time) const noexcept -{ - return this->time > time.time; -} - -bool simplydt::utc::Time::operator<=(const Time time) const noexcept -{ - return this->time <= time.time; -} - -bool simplydt::utc::Time::operator>=(const Time time) const noexcept -{ - return this->time >= time.time; -} - -uint16_t simplydt::utc::Time::operator[](const CalendarComponent component) const noexcept -{ - return this->getComponent(component); -} - -// simplydt::utc::Time : OPERATOR END! - - -// simplydt::utc::Time : PUBLIC - -uint8_t simplydt::utc::Time::hour() const noexcept -{ - return Time::extractEncodedHour(&this->time); -} - -std::string simplydt::utc::Time::hourPhaseLiteral() const noexcept -{ - if ((this->time / HOUR_FACTOR) < 12) - return MeridiemPhases[AM]; - - return MeridiemPhases[PM]; -} - -uint8_t simplydt::utc::Time::minute() const noexcept -{ - return Time::extractEncodedMinute(&this->time); -} - -uint8_t simplydt::utc::Time::second() const noexcept -{ - return Time::extractEncodedSecond(&this->time); -} - -uint16_t simplydt::utc::Time::getComponent(const CalendarComponent component) const noexcept -{ - switch (component) { - case CalendarComponent::HOUR: - return Time::extractEncodedHour(&this->time); - - case CalendarComponent::MINUTE: - return Time::extractEncodedMinute(&this->time); - - case CalendarComponent::SECOND: - return Time::extractEncodedSecond(&this->time); - - default: - return 0; // Invalid component - } -} - -std::string simplydt::utc::Time::toStr() const noexcept -{ - const char delimiter = ':'; - - std::string timeStr; - timeStr.reserve(12); - - timeStr += (std::to_string(this->hour()) + delimiter); - timeStr += (toDoubleDigitStr(this->minute()) + delimiter); - timeStr += (toDoubleDigitStr(this->second()) + ' '); - timeStr += this->hourPhaseLiteral(); - - return timeStr; -} - -simplydt::utc::Time::Underlying_T simplydt::utc::Time::underlying() const noexcept -{ - return this->time; -} - -// simplydt::utc::Time : PUBLIC END! - - -// simplydt::utc::Time : PROTECTED -// simplydt::utc::Time : PROTECTED END! - - -// simplydt::utc::Time : PRIVATE -// simplydt::utc::Time : PRIVATE END! diff --git a/src/gregorian_calendar/CMakeLists.txt b/src/gregorian_calendar/CMakeLists.txt deleted file mode 100644 index 742e62a..0000000 --- a/src/gregorian_calendar/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ - -#=================================================== -# /src/gregorian_calendar Directory Script -#=================================================== - -target_sources( - ${MAIN_BINARY_NAME} - - PUBLIC - "${CMAKE_CURRENT_SOURCE_DIR}/gregorian_date.cpp" -) diff --git a/src/gregorian_calendar/gregorian_date.cpp b/src/gregorian_calendar/gregorian_date.cpp deleted file mode 100644 index b2b16f0..0000000 --- a/src/gregorian_calendar/gregorian_date.cpp +++ /dev/null @@ -1,190 +0,0 @@ - -// Copyright (C) 2023-2025 by Jamon T. Bailey and Infinity Systems, LLC. All rights reserved. -// Released under the terms of the GNU Affero General Public License version 3. - -// [ISJTB-CXX-XL20230401-000001] - -#include "simplydt/gregorian_calendar/gregorian_date.hpp" - -// simplydt::gregorian::Date : CONSTRUCTOR - -simplydt::gregorian::Date::Date( - const YearInt_T year, const uint8_t month, const uint8_t day -) noexcept - : date{ DEFAULT_DATE } -{ - Date::encodeGregorianDateIntoInteger(&this->date, year, month, day); -} - -simplydt::gregorian::Date::Date(const YearInt_T year, const uint8_t month) noexcept - : date{ DEFAULT_DATE } -{ - Date::encodeGregorianDateIntoInteger(&this->date, year, month, 1); -} - -simplydt::gregorian::Date::Date(const Date& date) noexcept : date{ DEFAULT_DATE } -{ - this->date = date.date; -} - -simplydt::gregorian::Date::Date() noexcept : date{ DEFAULT_DATE } -{ - // -} - -// simplydt::gregorian::Date : CONSTRUCTOR END! - - -// simplydt::gregorian::Date : STATIC - -bool simplydt::gregorian::Date::encodeGregorianDateIntoInteger( - Underlying_T* integer, const YearInt_T& year, const uint8_t& month, const uint8_t& day -) noexcept -{ - if (integer == nullptr) - return false; - - if ((month < MIN_MONTH_OF_YEAR) || (month > MAX_MONTH_OF_YEAR)) - return false; // Invalid date params - - if ((day < MIN_DAY_OF_MONTH) || (day > MAX_DAY_OF_MONTH)) - return false; // Invalid date params - - *integer = ((year * YEAR_FACTOR) + (month * MONTH_FACTOR) + day); - - return true; -} - -simplydt::gregorian::YearInt_T simplydt::gregorian::Date::extractEncodedYear( - const Underlying_T* date -) noexcept -{ - return static_cast(*date / YEAR_FACTOR); -} - -uint8_t simplydt::gregorian::Date::extractEncodedMonth(const Underlying_T* date) noexcept -{ - return static_cast((*date % YEAR_FACTOR) / MONTH_FACTOR); -} - -uint8_t simplydt::gregorian::Date::extractEncodedDay(const Underlying_T* date) noexcept -{ - return static_cast((*date % YEAR_FACTOR) % MONTH_FACTOR); -} - -// simplydt::gregorian::Date : STATIC END! - - -// simplydt::gregorian::Date : OPERATOR - -bool simplydt::gregorian::Date::operator==(const Date date) const noexcept -{ - return this->date == date.date; -} - -bool simplydt::gregorian::Date::operator<(const Date date) const noexcept -{ - return this->date < date.date; -} - -bool simplydt::gregorian::Date::operator>(const Date date) const noexcept -{ - return this->date > date.date; -} - -bool simplydt::gregorian::Date::operator<=(const Date date) const noexcept -{ - return this->date <= date.date; -} - -bool simplydt::gregorian::Date::operator>=(const Date date) const noexcept -{ - return this->date >= date.date; -} - -uint16_t simplydt::gregorian::Date::operator[](const CalendarComponent component -) const noexcept -{ - return this->getComponent(component); -} - -// simplydt::gregorian::Date : OPERATOR END! - - -// simplydt::gregorian::Date : PUBLIC - -simplydt::gregorian::YearInt_T simplydt::gregorian::Date::year() const noexcept -{ - return Date::extractEncodedYear(&this->date); -} - -uint8_t simplydt::gregorian::Date::month() const noexcept -{ - return Date::extractEncodedMonth(&this->date); -} - -std::string simplydt::gregorian::Date::monthLiteral() const noexcept -{ - const uint8_t monthIndex = this->month() - 1; - - return Months[monthIndex]; -} - -std::string simplydt::gregorian::Date::monthAbbreviation() const noexcept -{ - const uint8_t monthIndex = this->month() - 1; - - return std::string{ MonthAbbrevs[monthIndex] }; -} - -uint8_t simplydt::gregorian::Date::day() const noexcept -{ - return Date::extractEncodedDay(&this->date); -} - -uint16_t simplydt::gregorian::Date::getComponent(const CalendarComponent component -) const noexcept -{ - switch (component) { - case CalendarComponent::YEAR: - return Date::extractEncodedYear(&this->date); - - case CalendarComponent::MONTH: - return Date::extractEncodedMonth(&this->date); - - case CalendarComponent::DAY: - return Date::extractEncodedDay(&this->date); - - default: - return 0; // Invalid component - } -} - -std::string simplydt::gregorian::Date::toStr() const noexcept -{ - const char delimiter = '-'; - - std::string dateStr; - dateStr.reserve(12); - - dateStr += (std::to_string(this->year()) + delimiter); - dateStr += (toDoubleDigitStr(this->month()) + delimiter); - dateStr += toDoubleDigitStr(this->day()); - - return dateStr; -} - -simplydt::gregorian::Date::Underlying_T simplydt::gregorian::Date::underlying() const noexcept -{ - return this->date; -} - -// simplydt::gregorian::Date : PUBLIC END! - - -// simplydt::gregorian::Date : PROTECTED -// simplydt::gregorian::Date : PROTECTED END! - - -// simplydt::gregorian::Date : PRIVATE -// simplydt::gregorian::Date : PRIVATE END! diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index 9acf607..0000000 --- a/src/main.cpp +++ /dev/null @@ -1,5 +0,0 @@ - -int doNothing() -{ - return 0; -} diff --git a/tests/cli/CMakeLists.txt b/tests/cli/CMakeLists.txt index 8c2370b..c037fcc 100644 --- a/tests/cli/CMakeLists.txt +++ b/tests/cli/CMakeLists.txt @@ -14,12 +14,3 @@ target_compile_features( PUBLIC cxx_std_20 ) - -set(SIMPLYDT_TARGET_NAME "${${PRJ_SCOPE}_MAIN_BINARY_NAME}") - -target_link_libraries( - dev_cli_debug - - PUBLIC - ${SIMPLYDT_TARGET_NAME} -) diff --git a/tests/cli/dev_exe.cpp b/tests/cli/dev_exe.cpp index 55154cd..f3b74ca 100644 --- a/tests/cli/dev_exe.cpp +++ b/tests/cli/dev_exe.cpp @@ -5,124 +5,198 @@ * * ~ CLI Debug Executable Notes / TO-DO List ~ * -* -> [X] Create concepts::useable_underlying_type -* -> [] Implement SDT duration configuration -* -> [] Create concepts::valid_dap_duration -* -> [] Create contracts::dap_duration_compliant -* -> [] Create dap::AbstractDatetimeDriver<...> -* -> [] Implement JDN algorithms -* -> [] Implement DOW algorithm -* -> [] Implement SDT datetime configuration -* -> [] Create contracts::dap_datetime_compliant -* -> [] Implement STL duration configuration -* -> [] Implement STL datetime configuration -* -> [] Need a general place for stuff like -* 'YEAR_MIN/MAX', 'Invalid' string, etc. +* -> [] :: Datetime Output Styles :: +* -> [] StandardStyle ----> 2004-09-17:00:00:00.000 +* -> [] UnixStyle --------> 2004-09-17T00:00:00.000 +* -> [] CivilStyle -------> 2004-09-17 00:00:00 +* -> [] ... +* +* +* -> [] :: Common Time Definitions :: +* -> [] using ExTime_t = uint64_t; * \* /// \\\ /// \\\ /// | END | \\\ /// \\\ /// \\\ */ +// Includes... +#include "simplydt/calendar/gregorian/gregorian_calendar.hpp" +#include "simplydt/time/utc/utc_time.hpp" -// ~ Possible Method to Shrink Date to 16-bits ~ // -// -// (STEPS): -// 1. Encode Gregorian date into integer per usual. -// 2. Deduct necessary amount to make last steps -// value cleanly divisible by 10. -// -> (RETAIN THE DEDUCTED AMOUNT!) -// 3. Using a loop, compose a divisor that increases -// in decimal magnitude (x10) with each iteration -// until it is an appropriate amount to compress -// the given encoded date integer. -// -> (RETAIN THE NUMBER OF LOOP ITERATIONS!) -// -> (The amount used to divide the date cannot -// be too excessive or light.) -// -> (Idealy the computed value should be 4-5 -// significant figures.) -// 4. Divide the encoded date by the value computed -// from the previous step. The divisor cannot be -// too excessive because the two retained values -// from steps 2 and 3 will now be encoded into -// the tail-end of this steps computed value. -// -> (Neither of the two retained values will -// exceed 1 significant figure.) -// 5. This is your uint16_t Gregorian date than can -// be completely reversed with no information -// loss. -//================================================= - - -#include "simplydt/dap/driver/duration/abstract_driver.hpp" - - -// SAMPLE BELOW: - -/*! @brief Example duration implementation. */ -class FakeDuration final : public simplydt::dap::AbstractDurationDriver { - -public: - static constexpr Underlying_T DEFAULT_VALUE = 0.0; - - FakeDuration() noexcept - : AbstractDurationDriver{}, - duration{ DEFAULT_VALUE } - { - // - } +/* @brief Dummy structure. */ +struct Object { + bool testing = true; +}; + +int main(int argc, char* argv[]) +{ + ProjectInfoOut(); - ~FakeDuration() = default; + bool Object::* frtho = nullptr; // Member pointer + bool* known = nullptr; // Raw pointer + + //\\// + using Calendar = simplydt::gregorian::GregorianCalendar; + using Date = simplydt::gregorian::GregorianDate; + using Time = simplydt::utc::UTCTime; + using Days = simplydt::Days; + using Seconds = simplydt::Seconds; + using SystemClock = simplydt::stl::SystemClock; + using UnixTimestamp = simplydt::stl::UnixTimestamp; - /*! @brief Determine if duration value is default. */ - [[nodiscard]] bool isDefault() const noexcept + constexpr Date todayDate{2'025, 9, 25}; + constexpr Date pastDate{2'025, 9, 7}; + + constexpr Time rightNow{1, 54, 19}; + constexpr Time secondsAgo{1, 53, 48}; + + // GregorianDate constexpr tests: { - return isDefaultValue(this->duration); + constexpr Date::YearInt_t today_yr = todayDate.year(); + constexpr uint8_t today_mn = todayDate.month(); + constexpr uint8_t today_dy = todayDate.day(); + + constexpr bool eq = todayDate == pastDate; + constexpr bool lt = todayDate < pastDate; + constexpr bool gt = todayDate > pastDate; + constexpr bool lte = todayDate <= pastDate; + constexpr bool gte = todayDate >= pastDate; + + constexpr Date pd = todayDate + Days{7}; + constexpr Date md = todayDate - Days{7}; + constexpr Days diff1 = todayDate - pastDate; + constexpr Days diff2 = pastDate - todayDate; + constexpr Date arith = pastDate + (todayDate - pastDate); + + constexpr bool zro = todayDate.isZero(); + constexpr bool ftr = todayDate.isAfter(pastDate); + constexpr bool pst = todayDate.isBefore(pastDate); + constexpr bool btw = todayDate.isBetween(pastDate, todayDate); + constexpr Days ntl = pastDate.daysUntil(todayDate); + constexpr Days unit = todayDate.units(); + constexpr int32_t rep = todayDate.underlying(); } - /*! @brief Determine if duration represents negative elapsed time. */ - [[nodiscard]] bool isNegative() const noexcept + // GregorianDate increment/decrement tests: { - return isNegativeValue(this->duration); + Date today = todayDate; + Date next = ++today; + next = today++; + + Date yesterday = todayDate - Days{1}; + Date last = --yesterday; + last = yesterday--; + + next += Days{15}; + last -= Days{10}; } - [[nodiscard]] const Underlying_T& underlying() const noexcept + // UTCTime constexpr tests: { - return this->duration; + constexpr uint8_t now_hr = rightNow.hour12(); + constexpr uint8_t now_min = rightNow.minute(); + constexpr uint8_t now_sec = rightNow.second(); + + constexpr bool eq = rightNow == secondsAgo; + constexpr bool lt = rightNow < secondsAgo; + constexpr bool gt = rightNow > secondsAgo; + constexpr bool lte = rightNow <= secondsAgo; + constexpr bool gte = rightNow >= secondsAgo; + + constexpr Time ps = rightNow + Seconds{15}; + constexpr Time ms = rightNow - Seconds{15}; + constexpr Seconds diff1 = rightNow - secondsAgo; + constexpr Seconds diff2 = secondsAgo - rightNow; + constexpr Time arith = secondsAgo + (rightNow - secondsAgo); + + constexpr bool zro = rightNow.isZero(); + constexpr bool ftr = rightNow.isAfter(secondsAgo); + constexpr bool pst = rightNow.isBefore(secondsAgo); + constexpr bool btw = rightNow.isBetween(secondsAgo, rightNow); + constexpr Seconds unit = rightNow.units(); + constexpr uint32_t rep = rightNow.underlying(); } - /*! @brief Determine if duration value is default. */ - struct IsDefaultValue_Impl : simplydt::dap::Overload { - using ReturnType = bool; + // UTCTime increment/decrement tests: + { + Time now = rightNow; + Time next = ++now; + next = now++; - static ReturnType call(const Underlying_T& selfImpl) noexcept { return selfImpl == DEFAULT_VALUE; } - }; + next = rightNow - Seconds{30}; + next = --now; + next = now--; - /*! @brief Determine if duration represents negative elapsed time. */ - struct IsNegativeValue_Impl : simplydt::dap::Overload { - using ReturnType = bool; + next += Seconds{15}; + next -= Seconds{10}; + } - static ReturnType call(const Underlying_T& selfImpl) noexcept { return selfImpl < 0.0; } - }; + // Gregorian calendar tests: + { + constexpr const char* mnm = Calendar::getMonthName(todayDate); + constexpr std::string_view mabbr = Calendar::getMonthAbbrev(todayDate); + constexpr Calendar::Month mrepr = Calendar::getMonthEnumRepr(todayDate); + constexpr Days serial = Calendar::toDaysSinceEpoch(2'001, 2, 23); + constexpr Days serial2 = Calendar::toDaysSinceEpoch(todayDate); + constexpr Date jtb = Calendar::fromDaysSinceEpoch(serial); + constexpr UnixTimestamp ts = Calendar::toUnixTimestamp(2'001, 2, 23); + constexpr Date rtc2 = Calendar::fromUnixTimestamp(ts); + constexpr Date next = Calendar::getNextWeekday(jtb); + constexpr Date last = Calendar::getLastWeekday(next); + constexpr Date mvv = Calendar::getNextWeekend(jtb); + constexpr Date weekend = Calendar::getLastWeekend(jtb); + constexpr Calendar::WeekDates week = Calendar::getWeek(2'025, 0); + constexpr uint8_t wkIndex = Calendar::getWeekIndex(2'025, 9, 22); + constexpr uint8_t wks = Calendar::getWeeksInMonth(2025, 9); + constexpr Date today = Calendar::getDate(2'025, 9, 23); + const Date rightNow = Calendar::getDate(SystemClock::now()); + constexpr Date nxt = Calendar::getNextDate(jtb, simplydt::gregorian::MONDAY); + constexpr Date nxtMonth = Calendar::getNextDate(nxt, simplydt::gregorian::MARCH); + constexpr Date lstMonth = Calendar::getLastDate(jtb, simplydt::gregorian::JANUARY); + constexpr Date lst = Calendar::getLastDate(nxt, simplydt::gregorian::SUNDAY); + constexpr Date n = Calendar::getNextDate(jtb); + constexpr Date l = Calendar::getLastDate(jtb); + constexpr uint8_t dow = Calendar::getDayOfWeekIndex(jtb); + } -private: - Underlying_T duration; + // Console calendar test: + { + constexpr Date::YearInt_t year = 2'025; + constexpr uint8_t month = simplydt::gregorian::September; -}; + std::cout << "\n\n\t[ ~ " << Calendar::getMonthName(month) << ' ' << year << " ~ ]" + << std::endl; -// SAMPLE ABOVE: + for (const std::string_view& dowAbbrev : Calendar::DAY_OF_WEEK_ABBREVS) { + std::cout << ' ' << dowAbbrev << " "; + } + std::cout << std::endl; + Calendar::WeekDates week = Calendar::getWeek(Date{year, month, 1}); + const Date today = Calendar::getDate(SystemClock::now()); -int main(int argc, char* argv[]) -{ - ProjectInfoOut(); + while (true) { + for (const Date date : week) { + if (date.month() != month) { + std::cout << " "; + continue; + } - //\\// - using namespace simplydt; + if (date == today) + std::cout << ">_"; + else + std::cout << "__"; + + std::cout << simplydt::toDoubleDigitStr(date.day()) << "_|"; + } - FakeDuration demo{}; + std::cout << std::endl; - std::cout << "\nIs default? -> " << std::boolalpha << demo.isDefault() << std::endl; + if (week[simplydt::gregorian::SATURDAY].month() != month) + break; - // template - // class AbstractDatetimeDriver : public AbstractDAPDriver { ... }; + week = Calendar::getWeek(week[simplydt::gregorian::SATURDAY] + Days{1}); + } + } + std::cout << "\n\n\t[ Complete ]" << std::endl; return 0; }