pax_global_header00006660000000000000000000000064146153422620014517gustar00rootroot0000000000000052 comment=2a975bb7fd634223999c50f43af2b3b63b1fbc43 dfdatetime-20240504/000077500000000000000000000000001461534226200140675ustar00rootroot00000000000000dfdatetime-20240504/.github/000077500000000000000000000000001461534226200154275ustar00rootroot00000000000000dfdatetime-20240504/.github/workflows/000077500000000000000000000000001461534226200174645ustar00rootroot00000000000000dfdatetime-20240504/.github/workflows/test_docker.yml000066400000000000000000000045331461534226200225220ustar00rootroot00000000000000# Run tests on Fedora and Ubuntu Docker images using GIFT CORP and GIFT PPA on commit name: test_docker on: [push] permissions: read-all jobs: test_fedora: runs-on: ubuntu-latest strategy: matrix: version: ['39', '40'] container: image: registry.fedoraproject.org/fedora:${{ matrix.version }} steps: - uses: actions/checkout@v4 - name: Set up container run: | dnf install -y dnf-plugins-core langpacks-en - name: Install dependencies run: | dnf copr -y enable @gift/dev dnf install -y @development-tools python3 python3-build python3-devel python3-setuptools python3-wheel - name: Run tests env: LANG: C.utf8 run: | python3 ./run_tests.py - name: Run end-to-end tests run: | if test -f tests/end-to-end.py; then PYTHONPATH=. python3 ./tests/end-to-end.py --debug -c config/end-to-end.ini; fi - name: Build source distribution (sdist) run: | python3 -m build --no-isolation --sdist - name: Build binary distribution (wheel) run: | python3 -m build --no-isolation --wheel test_ubuntu: runs-on: ubuntu-latest strategy: matrix: version: ['24.04'] container: image: ubuntu:${{ matrix.version }} steps: - uses: actions/checkout@v4 - name: Set up container env: DEBIAN_FRONTEND: noninteractive run: | apt-get update -q apt-get install -y libterm-readline-gnu-perl locales software-properties-common locale-gen en_US.UTF-8 ln -f -s /usr/share/zoneinfo/UTC /etc/localtime - name: Install dependencies run: | add-apt-repository -y ppa:gift/dev apt-get update -q apt-get install -y build-essential python3 python3-build python3-dev python3-pip python3-setuptools python3-wheel - name: Run tests env: LANG: en_US.UTF-8 run: | python3 ./run_tests.py - name: Run end-to-end tests env: LANG: en_US.UTF-8 run: | if test -f tests/end-to-end.py; then PYTHONPATH=. python3 ./tests/end-to-end.py --debug -c config/end-to-end.ini; fi - name: Build source distribution (sdist) run: | python3 -m build --no-isolation --sdist - name: Build binary distribution (wheel) run: | python3 -m build --no-isolation --wheel dfdatetime-20240504/.github/workflows/test_docs.yml000066400000000000000000000025171461534226200222030ustar00rootroot00000000000000# Run docs tox tests on Ubuntu Docker images using GIFT PPA name: test_docs on: pull_request: branches: - main push: branches: - main permissions: read-all jobs: build: runs-on: ubuntu-latest strategy: matrix: include: - python-version: '3.12' toxenv: 'docs' container: image: ubuntu:22.04 steps: - uses: actions/checkout@v4 - name: Set up container env: DEBIAN_FRONTEND: noninteractive run: | apt-get update -q apt-get install -y libterm-readline-gnu-perl locales software-properties-common locale-gen en_US.UTF-8 ln -f -s /usr/share/zoneinfo/UTC /etc/localtime - name: Install dependencies env: DEBIAN_FRONTEND: noninteractive run: | add-apt-repository -y universe add-apt-repository -y ppa:deadsnakes/ppa add-apt-repository -y ppa:gift/dev apt-get update -q apt-get install -y build-essential git python${{ matrix.python-version }} python${{ matrix.python-version }}-dev python${{ matrix.python-version }}-venv python3-distutils python3-lib2to3 python3-pip python3-setuptools - name: Install tox run: | python3 -m pip install tox - name: Run tests env: LANG: en_US.UTF-8 run: | tox -e${{ matrix.toxenv }} dfdatetime-20240504/.github/workflows/test_tox.yml000066400000000000000000000100431461534226200220560ustar00rootroot00000000000000# Run tox tests on Ubuntu Docker images using GIFT PPA name: test_tox on: pull_request: branches: - main push: branches: - main permissions: read-all jobs: build: runs-on: ubuntu-latest strategy: matrix: include: - python-version: '3.8' toxenv: 'py38,wheel' - python-version: '3.9' toxenv: 'py39,wheel' - python-version: '3.10' toxenv: 'py310,wheel' - python-version: '3.11' toxenv: 'py311,wheel' - python-version: '3.12' toxenv: 'py312,wheel' container: image: ubuntu:22.04 steps: - uses: actions/checkout@v4 - name: Set up container env: DEBIAN_FRONTEND: noninteractive run: | apt-get update -q apt-get install -y libterm-readline-gnu-perl locales software-properties-common locale-gen en_US.UTF-8 ln -f -s /usr/share/zoneinfo/UTC /etc/localtime - name: Install dependencies env: DEBIAN_FRONTEND: noninteractive run: | add-apt-repository -y universe add-apt-repository -y ppa:deadsnakes/ppa add-apt-repository -y ppa:gift/dev apt-get update -q apt-get install -y build-essential git python${{ matrix.python-version }} python${{ matrix.python-version }}-dev python${{ matrix.python-version }}-venv python3-distutils python3-lib2to3 python3-pip python3-setuptools - name: Install tox run: | python3 -m pip install tox - name: Run tests env: LANG: en_US.UTF-8 run: | tox -e${{ matrix.toxenv }} coverage: runs-on: ubuntu-latest strategy: matrix: include: - python-version: '3.10' toxenv: 'coverage' container: image: ubuntu:22.04 steps: - uses: actions/checkout@v4 - name: Set up container env: DEBIAN_FRONTEND: noninteractive run: | apt-get update -q apt-get install -y libterm-readline-gnu-perl locales software-properties-common locale-gen en_US.UTF-8 ln -f -s /usr/share/zoneinfo/UTC /etc/localtime - name: Install dependencies env: DEBIAN_FRONTEND: noninteractive run: | add-apt-repository -y universe add-apt-repository -y ppa:deadsnakes/ppa add-apt-repository -y ppa:gift/dev apt-get update -q apt-get install -y build-essential git python${{ matrix.python-version }} python${{ matrix.python-version }}-dev python${{ matrix.python-version }}-venv python3-distutils python3-lib2to3 python3-pip python3-setuptools - name: Install tox run: | python3 -m pip install tox - name: Run tests with coverage env: LANG: en_US.UTF-8 run: | tox -e${{ matrix.toxenv }} - name: Upload coverage report to Codecov uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} lint: runs-on: ubuntu-latest strategy: matrix: include: - python-version: '3.12' toxenv: 'lint' container: image: ubuntu:22.04 steps: - uses: actions/checkout@v4 - name: Set up container env: DEBIAN_FRONTEND: noninteractive run: | apt-get update -q apt-get install -y libterm-readline-gnu-perl locales software-properties-common locale-gen en_US.UTF-8 ln -f -s /usr/share/zoneinfo/UTC /etc/localtime - name: Install dependencies env: DEBIAN_FRONTEND: noninteractive run: | add-apt-repository -y universe add-apt-repository -y ppa:deadsnakes/ppa add-apt-repository -y ppa:gift/dev apt-get update -q apt-get install -y build-essential git python${{ matrix.python-version }} python${{ matrix.python-version }}-dev python${{ matrix.python-version }}-venv python3-distutils python3-lib2to3 python3-pip python3-setuptools - name: Install tox run: | python3 -m pip install tox - name: Run linter env: LANG: en_US.UTF-8 run: | tox -e${{ matrix.toxenv }} dfdatetime-20240504/.gitignore000066400000000000000000000004541461534226200160620ustar00rootroot00000000000000# Files to ignore by git. # Back-up files *~ *.swp # Generic auto-generated build files *.pyc *.pyo # Specific auto-generated build files /.tox /__pycache__ /build /dfdatetime.egg-info /dist # Code review files /.review # Pycharm files /.idea # Test coverage files .coverage tests-coverage.txt dfdatetime-20240504/.pylintrc000066400000000000000000000551471461534226200157500ustar00rootroot00000000000000# Pylint 3.0.x configuration file # # This file is generated by l2tdevtools update-dependencies.py, any dependency # related changes should be made in dependencies.ini. [MAIN] # Analyse import fallback blocks. This can be used to support both Python 2 and # 3 compatible code, which means that the block might have code that exists # only in one or another interpreter, leading to false positives when analysed. analyse-fallback-blocks=no # Clear in-memory caches upon conclusion of linting. Useful if running pylint # in a server-like mode. clear-cache-post-run=no # Load and enable all available extensions. Use --list-extensions to see a list # all available extensions. #enable-all-extensions= # In error mode, messages with a category besides ERROR or FATAL are # suppressed, and no reports are done by default. Error mode is compatible with # disabling specific errors. #errors-only= # Always return a 0 (non-error) status code, even if lint errors are found. # This is primarily useful in continuous integration scripts. #exit-zero= # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code. extension-pkg-allow-list= # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code. (This is an alternative name to extension-pkg-allow-list # for backward compatibility.) extension-pkg-whitelist= # Return non-zero exit code if any of these messages/categories are detected, # even if score is above --fail-under value. Syntax same as enable. Messages # specified are enabled, while categories only check already-enabled messages. fail-on= # Specify a score threshold under which the program will exit with error. fail-under=10 # Interpret the stdin as a python script, whose filename needs to be passed as # the module_or_package argument. #from-stdin= # Files or directories to be skipped. They should be base names, not paths. ignore=CVS # Add files or directories matching the regular expressions patterns to the # ignore-list. The regex matches against paths and can be in Posix or Windows # format. Because '\\' represents the directory delimiter on Windows systems, # it can't be used as an escape character. ignore-paths= # Files or directories matching the regular expression patterns are skipped. # The regex matches against base names, not paths. The default value ignores # Emacs file locks ignore-patterns=^\.# # List of module names for which member attributes should not be checked # (useful for modules/projects where namespaces are manipulated during runtime # and thus existing member attributes cannot be deduced by static analysis). It # supports qualified module names, as well as Unix pattern matching. ignored-modules= # Python code to execute, usually for sys.path manipulation such as # pygtk.require(). #init-hook= # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the # number of processors available to use, and will cap the count on Windows to # avoid hangs. jobs=1 # Control the amount of potential inferred values when inferring a single # object. This can help the performance when dealing with large functions or # complex, nested conditions. limit-inference-results=100 # List of plugins (as comma separated values of python module names) to load, # usually to register additional checkers. load-plugins=pylint.extensions.docparams # Pickle collected data for later comparisons. persistent=yes # Minimum Python version to use for version dependent checks. Will default to # the version used to run pylint. py-version=3.12 # Discover python modules and packages in the file system subtree. # recursive=no recursive=yes # Add paths to the list of the source roots. Supports globbing patterns. The # source root is an absolute path or a path relative to the current working # directory used to determine a package namespace for modules located under the # source root. source-roots= # When enabled, pylint would attempt to guess common misconfiguration and emit # user-friendly hints instead of false-positive error messages. suggestion-mode=yes # Allow loading of arbitrary C extensions. Extensions are imported into the # active Python interpreter and may run arbitrary code. unsafe-load-any-extension=no # In verbose mode, extra non-checker-related info will be displayed. #verbose= [BASIC] # Naming style matching correct argument names. argument-naming-style=snake_case # Regular expression matching correct argument names. Overrides argument- # naming-style. If left empty, argument names will be checked with the set # naming style. #argument-rgx= argument-rgx=(([a-z][a-z0-9_]*)|(_[a-z0-9_]*))$ # Naming style matching correct attribute names. attr-naming-style=snake_case # Regular expression matching correct attribute names. Overrides attr-naming- # style. If left empty, attribute names will be checked with the set naming # style. #attr-rgx= attr-rgx=(([a-z][a-z0-9_]*)|(_[a-z0-9_]*))$ # Bad variable names which should always be refused, separated by a comma. bad-names=foo, bar, baz, toto, tutu, tata # Bad variable names regexes, separated by a comma. If names match any regex, # they will always be refused bad-names-rgxs= # Naming style matching correct class attribute names. class-attribute-naming-style=any # Regular expression matching correct class attribute names. Overrides class- # attribute-naming-style. If left empty, class attribute names will be checked # with the set naming style. #class-attribute-rgx= class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]*|(__.*__))$ # Naming style matching correct class constant names. class-const-naming-style=UPPER_CASE # Regular expression matching correct class constant names. Overrides class- # const-naming-style. If left empty, class constant names will be checked with # the set naming style. #class-const-rgx= # Naming style matching correct class names. class-naming-style=PascalCase # Regular expression matching correct class names. Overrides class-naming- # style. If left empty, class names will be checked with the set naming style. #class-rgx= class-rgx=[A-Z_][a-zA-Z0-9]+$ # Naming style matching correct constant names. const-naming-style=UPPER_CASE # Regular expression matching correct constant names. Overrides const-naming- # style. If left empty, constant names will be checked with the set naming # style. #const-rgx= const-rgx=(([a-zA-Z_][a-zA-Z0-9_]*)|(__.*__))$ # Minimum line length for functions/classes that require docstrings, shorter # ones are exempt. docstring-min-length=-1 # Naming style matching correct function names. function-naming-style=snake_case # Regular expression matching correct function names. Overrides function- # naming-style. If left empty, function names will be checked with the set # naming style. #function-rgx= function-rgx=[A-Z_][a-zA-Z0-9_]*$ # Good variable names which should always be accepted, separated by a comma. good-names=i, j, k, ex, Run, _ # Good variable names regexes, separated by a comma. If names match any regex, # they will always be accepted good-names-rgxs= # Include a hint for the correct naming format with invalid-name. include-naming-hint=no # Naming style matching correct inline iteration names. inlinevar-naming-style=any # Regular expression matching correct inline iteration names. Overrides # inlinevar-naming-style. If left empty, inline iteration names will be checked # with the set naming style. #inlinevar-rgx= inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ # Naming style matching correct method names. method-naming-style=snake_case # Regular expression matching correct method names. Overrides method-naming- # style. If left empty, method names will be checked with the set naming style. #method-rgx= method-rgx=(test|[A-Z_])[a-zA-Z0-9_]*$ # Naming style matching correct module names. module-naming-style=snake_case # Regular expression matching correct module names. Overrides module-naming- # style. If left empty, module names will be checked with the set naming style. #module-rgx= module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ # Colon-delimited sets of names that determine each other's naming style when # the name regexes allow several styles. name-group= # Regular expression which should only match function or class names that do # not require a docstring. no-docstring-rgx=^_ # List of decorators that produce properties, such as abc.abstractproperty. Add # to this list to register other decorators that produce valid properties. # These decorators are taken in consideration only for invalid-name. property-classes=abc.abstractproperty # Regular expression matching correct type alias names. If left empty, type # alias names will be checked with the set naming style. #typealias-rgx= # Regular expression matching correct type variable names. If left empty, type # variable names will be checked with the set naming style. #typevar-rgx= # Naming style matching correct variable names. variable-naming-style=snake_case # Regular expression matching correct variable names. Overrides variable- # naming-style. If left empty, variable names will be checked with the set # naming style. #variable-rgx= variable-rgx=(([a-z][a-z0-9_]*)|(_[a-z0-9_]*))$ [CLASSES] # Warn about protected attribute access inside special methods check-protected-access-in-special-methods=no # List of method names used to declare (i.e. assign) instance attributes. defining-attr-methods=__init__, __new__, setUp, asyncSetUp, __post_init__ # List of member names, which should be excluded from the protected access # warning. exclude-protected=_asdict,_fields,_replace,_source,_make,os._exit # List of valid names for the first argument in a class method. valid-classmethod-first-arg=cls # List of valid names for the first argument in a metaclass class method. # valid-metaclass-classmethod-first-arg=mcs valid-metaclass-classmethod-first-arg=cls [DESIGN] # List of regular expressions of class ancestor names to ignore when counting # public methods (see R0903) exclude-too-few-public-methods= # List of qualified class names to ignore when counting class parents (see # R0901) ignored-parents= # Maximum number of arguments for function / method. # max-args=5 max-args=10 # Maximum number of attributes for a class (see R0902). max-attributes=7 # Maximum number of boolean expressions in an if statement (see R0916). max-bool-expr=5 # Maximum number of branch for function / method body. max-branches=12 # Maximum number of locals for function / method body. max-locals=15 # Maximum number of parents for a class (see R0901). max-parents=7 # Maximum number of public methods for a class (see R0904). max-public-methods=20 # Maximum number of return / yield for function / method body. max-returns=6 # Maximum number of statements in function / method body. max-statements=50 # Minimum number of public methods for a class (see R0903). min-public-methods=2 [EXCEPTIONS] # Exceptions that will emit a warning when caught. overgeneral-exceptions=builtins.BaseException,builtins.Exception [FORMAT] # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. expected-line-ending-format= # Regexp for a line that is allowed to be longer than the limit. ignore-long-lines=^\s*(# )??$ # Number of spaces of indent required inside a hanging or continued line. indent-after-paren=4 # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 # tab). # indent-string=' ' indent-string=' ' # Maximum number of characters on a single line. # max-line-length=100 max-line-length=80 # Maximum number of lines in a module. max-module-lines=1000 # Allow the body of a class to be on the same line as the declaration if body # contains single statement. single-line-class-stmt=no # Allow the body of an if to be on the same line as the test if there is no # else. single-line-if-stmt=no [IMPORTS] # List of modules that can be imported at any level, not just the top level # one. allow-any-import-level= # Allow explicit reexports by alias from a package __init__. allow-reexport-from-package=no # Allow wildcard imports from modules that define __all__. allow-wildcard-with-all=no # Deprecated modules which should not be used, separated by a comma. deprecated-modules= # Output a graph (.gv or any supported image format) of external dependencies # to the given file (report RP0402 must not be disabled). ext-import-graph= # Output a graph (.gv or any supported image format) of all (i.e. internal and # external) dependencies to the given file (report RP0402 must not be # disabled). import-graph= # Output a graph (.gv or any supported image format) of internal dependencies # to the given file (report RP0402 must not be disabled). int-import-graph= # Force import order to recognize a module as part of the standard # compatibility libraries. known-standard-library= # Force import order to recognize a module as part of a third party library. known-third-party=enchant # Couples of modules and preferred modules, separated by a comma. preferred-modules= [LOGGING] # The type of string formatting that logging methods do. `old` means using % # formatting, `new` is for `{}` formatting. logging-format-style=old # Logging modules to check that the string format arguments are in logging # function parameter format. logging-modules=logging [MESSAGES CONTROL] # Only show warnings with the listed confidence levels. Leave empty to show # all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, # UNDEFINED. confidence=HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE, UNDEFINED # Disable the message, report, category or checker with the given id(s). You # can either give multiple identifiers separated by comma (,) or put this # option multiple times (only on the command line, not in the configuration # file where it should appear only once). You can also use "--disable=all" to # disable everything first and then re-enable specific checks. For example, if # you want to run only the similarities checker, you can use "--disable=all # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use "--disable=all --enable=classes # --disable=W". disable=assignment-from-none, bad-inline-option, consider-using-f-string, deprecated-pragma, duplicate-code, file-ignored, fixme, locally-disabled, logging-format-interpolation, logging-fstring-interpolation, missing-param-doc, raise-missing-from, raw-checker-failed, super-with-arguments, suppressed-message, too-few-public-methods, too-many-ancestors, too-many-boolean-expressions, too-many-branches, too-many-instance-attributes, too-many-lines, too-many-locals, too-many-nested-blocks, too-many-public-methods, too-many-return-statements, too-many-statements, unsubscriptable-object, use-implicit-booleaness-not-comparison-to-string, use-implicit-booleaness-not-comparison-to-zero, useless-object-inheritance, useless-suppression, use-symbolic-message-instead # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). See also the "--disable" option for examples. enable=c-extension-no-member [METHOD_ARGS] # List of qualified names (i.e., library.method) which require a timeout # parameter e.g. 'requests.api.get,requests.api.post' timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. notes=FIXME, XXX, TODO # Regular expression of note tags to take in consideration. notes-rgx= [REFACTORING] # Maximum number of nested blocks for function / method body max-nested-blocks=5 # Complete name of functions that never returns. When checking for # inconsistent-return-statements if a never returning function is called then # it will be considered as an explicit return statement and no message will be # printed. never-returning-functions=sys.exit,argparse.parse_error [REPORTS] # Python expression which should return a score less than or equal to 10. You # have access to the variables 'fatal', 'error', 'warning', 'refactor', # 'convention', and 'info' which contain the number of messages in each # category, as well as 'statement' which is the total number of statements # analyzed. This score is used by the global evaluation report (RP0004). evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)) # Template used to display messages. This is a python new-style format string # used to format the message information. See doc for all details. msg-template= # Set the output format. Available formats are: text, parseable, colorized, # json2 (improved json format), json (old json format) and msvs (visual # studio). You can also give a reporter class, e.g. # mypackage.mymodule.MyReporterClass. #output-format= # Tells whether to display a full report or only the messages. reports=no # Activate the evaluation score. # score=yes score=no [SIMILARITIES] # Comments are removed from the similarity computation ignore-comments=yes # Docstrings are removed from the similarity computation ignore-docstrings=yes # Imports are removed from the similarity computation ignore-imports=yes # Signatures are removed from the similarity computation ignore-signatures=yes # Minimum lines number of a similarity. min-similarity-lines=4 [SPELLING] # Limits count of emitted suggestions for spelling mistakes. max-spelling-suggestions=4 # Spelling dictionary name. Available dictionaries: en_AG (hunspell), en_AU # (hunspell), en_BS (hunspell), en_BW (hunspell), en_BZ (hunspell), en_CA # (hunspell), en_DK (hunspell), en_GB (hunspell), en_GH (hunspell), en_HK # (hunspell), en_IE (hunspell), en_IN (hunspell), en_JM (hunspell), en_MW # (hunspell), en_NA (hunspell), en_NG (hunspell), en_NZ (hunspell), en_PH # (hunspell), en_SG (hunspell), en_TT (hunspell), en_US (hunspell), en_ZA # (hunspell), en_ZM (hunspell), en_ZW (hunspell). spelling-dict= # List of comma separated words that should be considered directives if they # appear at the beginning of a comment and should not be checked. spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: # List of comma separated words that should not be checked. spelling-ignore-words= # A path to a file that contains the private dictionary; one word per line. spelling-private-dict-file= # Tells whether to store unknown words to the private dictionary (see the # --spelling-private-dict-file option) instead of raising a message. spelling-store-unknown-words=no [STRING] # This flag controls whether inconsistent-quotes generates a warning when the # character used as a quote delimiter is used inconsistently within a module. check-quote-consistency=no # This flag controls whether the implicit-str-concat should generate a warning # on implicit string concatenation in sequences defined over several lines. check-str-concat-over-line-jumps=no [TYPECHECK] # List of decorators that produce context managers, such as # contextlib.contextmanager. Add to this list to register other decorators that # produce valid context managers. contextmanager-decorators=contextlib.contextmanager # List of members which are set dynamically and missed by pylint inference # system, and so shouldn't trigger E1101 when accessed. Python regular # expressions are accepted. generated-members= # Tells whether to warn about missing members when the owner of the attribute # is inferred to be None. ignore-none=yes # This flag controls whether pylint should warn about no-member and similar # checks whenever an opaque object is returned when inferring. The inference # can return multiple potential results while evaluating a Python object, but # some branches might not be evaluated, which results in partial inference. In # that case, it might be useful to still emit no-member and other checks for # the rest of the inferred objects. ignore-on-opaque-inference=yes # List of symbolic message names to ignore for Mixin members. ignored-checks-for-mixins=no-member, not-async-context-manager, not-context-manager, attribute-defined-outside-init # List of class names for which member attributes should not be checked (useful # for classes with dynamically set attributes). This supports the use of # qualified names. ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace # Show a hint with possible names when a member name was not found. The aspect # of finding the hint is based on edit distance. missing-member-hint=yes # The minimum edit distance a name should have in order to be considered a # similar match for a missing member name. missing-member-hint-distance=1 # The total number of similar names that should be taken in consideration when # showing a hint for a missing member. missing-member-max-choices=1 # Regex pattern to define which classes are considered mixins. mixin-class-rgx=.*[Mm]ixin # List of decorators that change the signature of a decorated function. signature-mutators= [VARIABLES] # List of additional names supposed to be defined in builtins. Remember that # you should avoid defining new builtins when possible. additional-builtins= # Tells whether unused global variables should be treated as a violation. allow-global-unused-variables=yes # List of names allowed to shadow builtins allowed-redefined-builtins= # List of strings which can identify a callback function by name. A callback # name must start or end with one of those strings. callbacks=cb_, _cb # A regular expression matching the name of dummy variables (i.e. expected to # not be used). dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ # Argument names that match this expression will be ignored. ignored-argument-names=_.*|^ignored_|^unused_ # Tells whether we should check for unused import in __init__ files. init-import=no # List of qualified module names which can have objects that can redefine # builtins. redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io dfdatetime-20240504/.readthedocs.yaml000066400000000000000000000005021461534226200173130ustar00rootroot00000000000000# Read the Docs configuration file for Sphinx projects # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details version: 2 build: os: ubuntu-22.04 tools: python: "3.12" sphinx: configuration: docs/conf.py fail_on_warning: false python: install: - requirements: docs/requirements.txt dfdatetime-20240504/ACKNOWLEDGEMENTS000066400000000000000000000012251461534226200163440ustar00rootroot00000000000000Acknowledgements: dfdatetime Copyright 2016 The dfDateTime Project Authors. Please see the AUTHORS file for details on individual authors. dfDateTime, or Digital Forensics date and time, provides date and time objects to preserve accuracy and precision. dfDateTime, originates from the dfVFS project [https://github.com/log2timeline/dfvfs] and the dfWinReg project [//github.com/log2timeline/dfvfs]. It was largely rewritten and made into a stand-alone project to provide more flexibility and allow other projects to make use of the Digital Forensics date and time objects. dfDateTime is currently developed and maintained by: * Daniel White * Joachim Metz dfdatetime-20240504/AUTHORS000066400000000000000000000005601461534226200151400ustar00rootroot00000000000000# Names should be added to this file with this pattern: # # For individuals: # Name (email address) # # For organizations: # Organization (fnmatch pattern) # # See python fnmatch module documentation for more information. Google Inc. (*@google.com) Kristinn Gudjonsson (kiddi@kiddaland.net) Joachim Metz (joachim.metz@gmail.com) Daniel White (onager@deerpie.com) dfdatetime-20240504/LICENSE000066400000000000000000000261361461534226200151040ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. dfdatetime-20240504/MANIFEST.in000066400000000000000000000007611461534226200156310ustar00rootroot00000000000000include ACKNOWLEDGEMENTS AUTHORS LICENSE README include dependencies.ini run_tests.py utils/__init__.py utils/dependencies.py include utils/check_dependencies.py include requirements.txt test_requirements.txt exclude .gitignore exclude *.pyc recursive-include config * recursive-exclude dfdatetime *.pyc # The test scripts are not required in a binary distribution package they # are considered source distribution files and excluded in find_package() # in setup.py. recursive-include tests *.py dfdatetime-20240504/README000066400000000000000000000003251461534226200147470ustar00rootroot00000000000000dfDateTime, or Digital Forensics date and time, provides date and time objects to preserve accuracy and precision. For more information see: * Project documentation: https://dfdatetime.readthedocs.io/en/latest dfdatetime-20240504/appveyor.yml000066400000000000000000000044211461534226200164600ustar00rootroot00000000000000environment: PYPI_TOKEN: secure: /FwQrmudDyj+Mu3DaxLEo23Y6/OEgdHJqyWyZTjkJKje8pxCOrUorN8ZlXRGXbd3UA60emClt0M+SI+xqyA/qkpqZTgd5CKohpVAGH2EfzRc/zwJSGJ4tmZmMVAG8ayk6N9zFxCeC+y0BgZPQnj/Eq/RfuS4YIuaKutIUa5gTMmhWpODFKGV/2Wx1w67xWxAoONfEC5j0Gu3R274SS7FfBb4qWyIiBIJMwHGjlgp1Onk8KlpCLauZv8/hGfQDmWEdZ+mjcsTYyQYr1xfr1/FjQ== matrix: - DESCRIPTION: "Run tests on Windows with 32-bit Python 3.12" MACHINE_TYPE: "x86" APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 PYTHON: "C:\\Python312" PYTHON_VERSION: "3.12" L2TBINARIES_TRACK: "dev" TARGET: tests - DESCRIPTION: "Run tests on Windows with 64-bit Python 3.12" MACHINE_TYPE: "amd64" APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 PYTHON: "C:\\Python312-x64" PYTHON_VERSION: "3.12" L2TBINARIES_TRACK: "dev" TARGET: tests - DESCRIPTION: "Build wheel on Windows with 32-bit Python 3.12" MACHINE_TYPE: "amd64" APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 PYTHON: "C:\\Python312-x64" PYTHON_VERSION: "3.12" L2TBINARIES_TRACK: "dev" TARGET: wheel - DESCRIPTION: "Build wheel on Windows with 64-bit Python 3.12" MACHINE_TYPE: "amd64" APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 PYTHON: "C:\\Python312-x64" PYTHON_VERSION: "3.12" L2TBINARIES_TRACK: "dev" TARGET: wheel - DESCRIPTION: "Run tests on Mac OS with Python 3.12" APPVEYOR_BUILD_WORKER_IMAGE: macos-monterey HOMEBREW_NO_INSTALL_CLEANUP: 1 TARGET: tests install: - cmd: "%PYTHON%\\python.exe -m pip install -U build pip setuptools twine wheel" - ps: If ($isWindows) { .\config\appveyor\install.ps1 } - sh: config/appveyor/install.sh build_script: - cmd: IF [%TARGET%]==[wheel] ( "%PYTHON%\\python.exe" -m build --wheel ) test_script: - cmd: IF [%TARGET%]==[tests] ( "%PYTHON%\\python.exe" run_tests.py && IF EXIST "tests\\end-to-end.py" ( set PYTHONPATH=. && "%PYTHON%\\python.exe" "tests\\end-to-end.py" --debug -c "config\\end-to-end.ini" ) ) - sh: config/appveyor/runtests.sh artifacts: - path: dist\*.whl deploy_script: - ps: If ($env:APPVEYOR_REPO_TAG -eq "true" -And $isWindows -And $env:MACHINE_TYPE -eq "x86") { Invoke-Expression "${env:PYTHON}\\python.exe -m twine upload dist/*.whl --username __token__ --password ${env:PYPI_TOKEN} --skip-existing" } dfdatetime-20240504/config/000077500000000000000000000000001461534226200153345ustar00rootroot00000000000000dfdatetime-20240504/config/appveyor/000077500000000000000000000000001461534226200172015ustar00rootroot00000000000000dfdatetime-20240504/config/appveyor/install.ps1000066400000000000000000000014361461534226200213000ustar00rootroot00000000000000# Script to set up tests on AppVeyor Windows. $Dependencies = "" If ($Dependencies.Length -gt 0) { $Dependencies = ${Dependencies} -split " " $Output = Invoke-Expression -Command "git clone https://github.com/log2timeline/l2tdevtools.git ..\l2tdevtools 2>&1" | %{ "$_" } Write-Host (${Output} | Out-String) If ($env:APPVEYOR_REPO_BRANCH -eq "main") { $Track = "stable" } Else { $Track = $env:APPVEYOR_REPO_BRANCH } New-Item -ItemType "directory" -Name "dependencies" $env:PYTHONPATH = "..\l2tdevtools" $Output = Invoke-Expression -Command "& '${env:PYTHON}\python.exe' ..\l2tdevtools\tools\update.py --download-directory dependencies --machine-type ${env:MACHINE_TYPE} --track ${env:L2TBINARIES_TRACK} ${Dependencies} 2>&1" | %{ "$_" } Write-Host (${Output} | Out-String) } dfdatetime-20240504/config/appveyor/install.sh000077500000000000000000000001751461534226200212110ustar00rootroot00000000000000# Script to set up tests on AppVeyor MacOS. set -e brew update -q brew install -q gettext gnu-sed python@3.12 tox || true dfdatetime-20240504/config/appveyor/runtests.sh000077500000000000000000000011541461534226200214300ustar00rootroot00000000000000#!/bin/sh # Script to run tests # Set the following environment variables to build libyal with gettext. export CPPFLAGS="-I/usr/local/include -I/usr/local/opt/gettext/include ${CPPFLAGS}"; export LDFLAGS="-L/usr/local/lib -L/usr/local/opt/gettext/lib ${LDFLAGS}"; # Set the following environment variables to build pycrypto and yara-python. export CPPFLAGS="-I/usr/local/opt/openssl@1.1/include ${CPPFLAGS}"; export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib ${LDFLAGS}"; # Set the following environment variables to ensure tox can find Python 3.12. export PATH="/usr/local/opt/python@3.12/bin:${PATH}"; tox -e py312 dfdatetime-20240504/config/dpkg/000077500000000000000000000000001461534226200162615ustar00rootroot00000000000000dfdatetime-20240504/config/dpkg/changelog000066400000000000000000000002551461534226200201350ustar00rootroot00000000000000dfdatetime (20240504-1) unstable; urgency=low * Auto-generated -- Log2Timeline maintainers Sat, 04 May 2024 07:02:38 +0200 dfdatetime-20240504/config/dpkg/clean000066400000000000000000000000271461534226200172650ustar00rootroot00000000000000dfdatetime/*.pyc *.pyc dfdatetime-20240504/config/dpkg/compat000066400000000000000000000000021461534226200174570ustar00rootroot000000000000009 dfdatetime-20240504/config/dpkg/control000066400000000000000000000010521461534226200176620ustar00rootroot00000000000000Source: dfdatetime Section: python Priority: extra Maintainer: Log2Timeline maintainers Build-Depends: debhelper (>= 9), dh-python, python3-all (>= 3.6~), python3-setuptools Standards-Version: 4.1.4 X-Python3-Version: >= 3.6 Homepage: https://github.com/log2timeline/dfdatetime Package: python3-dfdatetime Architecture: all Depends: ${misc:Depends} Description: Python 3 module of dfDateTime dfDateTime, or Digital Forensics date and time, provides date and time objects to preserve accuracy and precision. dfdatetime-20240504/config/dpkg/copyright000066400000000000000000000017101461534226200202130ustar00rootroot00000000000000Format: http://dep.debian.net/deps/dep5 Upstream-Name: dfdatetime Source: https://github.com/log2timeline/dfdatetime/ Files: * Copyright: 2016 The dfDateTime project authors. License: Apache-2.0 Files: debian/* Copyright: 2016 The dfDateTime project authors. License: Apache-2.0 License: Apache-2.0 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at . http://www.apache.org/licenses/LICENSE-2.0 . Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. . On Debian systems, the complete text of the Apache version 2.0 license can be found in "/usr/share/common-licenses/Apache-2.0". dfdatetime-20240504/config/dpkg/rules000077500000000000000000000001721461534226200173410ustar00rootroot00000000000000#!/usr/bin/make -f %: dh $@ --buildsystem=pybuild --with=python3 .PHONY: override_dh_auto_test override_dh_auto_test: dfdatetime-20240504/config/dpkg/source/000077500000000000000000000000001461534226200175615ustar00rootroot00000000000000dfdatetime-20240504/config/dpkg/source/format000066400000000000000000000000141461534226200207670ustar00rootroot000000000000003.0 (quilt) dfdatetime-20240504/config/pylint/000077500000000000000000000000001461534226200166535ustar00rootroot00000000000000dfdatetime-20240504/config/pylint/spelling-private-dict000066400000000000000000000003451461534226200230060ustar00rootroot00000000000000argparse args bool config deciseconds dev dfdatetime dfvfs divmod dpkg filenames floatingtime hfs hfsx hh hhmm hhmmss macos msi nano plaso pre precisions py rc readfp sdist ss stdin str struct sys timegm timestamp tuple utc util dfdatetime-20240504/dependencies.ini000066400000000000000000000000001461534226200172040ustar00rootroot00000000000000dfdatetime-20240504/dfdatetime.ini000066400000000000000000000013701461534226200166770ustar00rootroot00000000000000[project] name: dfdatetime status: alpha name_description: dfDateTime maintainer: Log2Timeline maintainers git_url: https://github.com/log2timeline/dfdatetime.git homepage_url: https://github.com/log2timeline/dfdatetime description_short: Digital Forensics date and time (dfDateTime). description_long: dfDateTime, or Digital Forensics date and time, provides date and time objects to preserve accuracy and precision. pypi_token: /FwQrmudDyj+Mu3DaxLEo23Y6/OEgdHJqyWyZTjkJKje8pxCOrUorN8ZlXRGXbd3UA60emClt0M+SI+xqyA/qkpqZTgd5CKohpVAGH2EfzRc/zwJSGJ4tmZmMVAG8ayk6N9zFxCeC+y0BgZPQnj/Eq/RfuS4YIuaKutIUa5gTMmhWpODFKGV/2Wx1w67xWxAoONfEC5j0Gu3R274SS7FfBb4qWyIiBIJMwHGjlgp1Onk8KlpCLauZv8/hGfQDmWEdZ+mjcsTYyQYr1xfr1/FjQ== dfdatetime-20240504/dfdatetime/000077500000000000000000000000001461534226200161755ustar00rootroot00000000000000dfdatetime-20240504/dfdatetime/__init__.py000066400000000000000000000015461461534226200203140ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Digital Forensics Date and Time (dfDateTime). dfDateTime, or Digital Forensics date and time, provides date and time objects to preserve accuracy and precision. """ # Imports for date time values factory. from dfdatetime import apfs_time from dfdatetime import cocoa_time from dfdatetime import delphi_date_time from dfdatetime import dotnet_datetime from dfdatetime import fat_date_time from dfdatetime import filetime from dfdatetime import hfs_time from dfdatetime import golang_time from dfdatetime import java_time from dfdatetime import ole_automation_date from dfdatetime import posix_time from dfdatetime import rfc2579_date_time from dfdatetime import semantic_time from dfdatetime import systemtime from dfdatetime import time_elements from dfdatetime import uuid_time from dfdatetime import webkit_time __version__ = '20240504' dfdatetime-20240504/dfdatetime/apfs_time.py000066400000000000000000000050431461534226200205200ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Apple File System (APFS) time implementation.""" import decimal from dfdatetime import definitions from dfdatetime import factory from dfdatetime import posix_time class APFSTime(posix_time.PosixTimeInNanoseconds): """Apple File System (APFS) timestamp. The APFS timestamp is a signed 64-bit integer that contains the number of nanoseconds since 1970-01-01 00:00:00. Attributes: is_local_time (bool): True if the date and time value is in local time. """ def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if (self._timestamp is not None and self._timestamp >= self._INT64_MIN and self._timestamp <= self._INT64_MAX): self._normalized_timestamp = ( decimal.Decimal(self._timestamp) / definitions.NANOSECONDS_PER_SECOND) if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def CopyFromDateTimeString(self, time_string): """Copies a APFS timestamp from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Raises: ValueError: if the date and time value is not supported. """ super(APFSTime, self)._CopyFromDateTimeString(time_string) if (self._timestamp is None or self._timestamp < self._INT64_MIN or self._timestamp > self._INT64_MAX): raise ValueError('Date time value not supported.') def CopyToDateTimeString(self): """Copies the APFS timestamp to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.#########" or None if the timestamp is missing or invalid. """ if (self._timestamp is None or self._timestamp < self._INT64_MIN or self._timestamp > self._INT64_MAX): return None return super(APFSTime, self)._CopyToDateTimeString() factory.Factory.RegisterDateTimeValues(APFSTime) dfdatetime-20240504/dfdatetime/cocoa_time.py000066400000000000000000000106541461534226200206570ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Cocoa timestamp implementation.""" import decimal from dfdatetime import definitions from dfdatetime import factory from dfdatetime import interface class CocoaTimeEpoch(interface.DateTimeEpoch): """Cocoa time epoch.""" def __init__(self): """Initializes a Cocoa time epoch.""" super(CocoaTimeEpoch, self).__init__(2001, 1, 1) class CocoaTime(interface.DateTimeValues): """Cocoa timestamp. The Cocoa timestamp is a floating point value that contains the number of seconds since 2001-01-01 00:00:00 (also known as the Cocoa epoch). Negative values represent date and times predating the Cocoa epoch. Attributes: is_local_time (bool): True if the date and time value is in local time. """ # The difference between January 1, 2001 and January 1, 1970 in seconds. _COCOA_TO_POSIX_BASE = -978307200 _EPOCH = CocoaTimeEpoch() def __init__(self, precision=None, time_zone_offset=None, timestamp=None): """Initializes a Cocoa timestamp. Args: precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. timestamp (Optional[float]): Cocoa timestamp. """ super(CocoaTime, self).__init__( precision=precision or definitions.PRECISION_1_SECOND, time_zone_offset=time_zone_offset) self._timestamp = timestamp @property def timestamp(self): """float: Cocoa timestamp or None if not set.""" return self._timestamp def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: float: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if self._timestamp is not None: self._normalized_timestamp = ( decimal.Decimal(self._timestamp) - self._COCOA_TO_POSIX_BASE) if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def CopyFromDateTimeString(self, time_string): """Copies a Cocoa timestamp from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Raises: ValueError: if the time string is invalid or not supported. """ date_time_values = self._CopyDateTimeFromString(time_string) year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) nanoseconds = date_time_values.get('nanoseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp += self._COCOA_TO_POSIX_BASE timestamp = float(timestamp) timestamp += float(nanoseconds) / definitions.NANOSECONDS_PER_SECOND self._normalized_timestamp = None self._timestamp = timestamp self._time_zone_offset = time_zone_offset def CopyToDateTimeString(self): """Copies the Cocoa timestamp to a date and time string. Returns: str: date and time value formatted as: YYYY-MM-DD hh:mm:ss.###### or None if the timestamp cannot be copied to a date and time string. """ if self._timestamp is None: return None number_of_days, hours, minutes, seconds = self._GetTimeValues( int(self._timestamp)) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) microseconds = int( (self._timestamp % 1) * definitions.MICROSECONDS_PER_SECOND) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}.{microseconds:06d}') factory.Factory.RegisterDateTimeValues(CocoaTime) dfdatetime-20240504/dfdatetime/decorators.py000066400000000000000000000013061461534226200207140ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Function decorators.""" import warnings def deprecated(function): # pylint: disable=invalid-name """Decorator to mark functions or methods as deprecated.""" def IssueDeprecationWarning(*args, **kwargs): """Issue a deprecation warning.""" warnings.simplefilter('default', DeprecationWarning) warnings.warn( f'Call to deprecated function: {function.__name__:s}.', category=DeprecationWarning, stacklevel=2) return function(*args, **kwargs) IssueDeprecationWarning.__name__ = function.__name__ IssueDeprecationWarning.__doc__ = function.__doc__ IssueDeprecationWarning.__dict__.update(function.__dict__) return IssueDeprecationWarning dfdatetime-20240504/dfdatetime/definitions.py000066400000000000000000000050311461534226200210610ustar00rootroot00000000000000# -*- coding: utf-8 -*- """The date and time definitions. Also see: https://en.wikipedia.org/wiki/Day https://en.wikipedia.org/wiki/Hour https://en.wikipedia.org/wiki/Minute """ DAYS_PER_MONTH = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) SECONDS_PER_DAY = 86400 DECISECONDS_PER_SECOND = 10 CENTISECONDS_PER_SECOND = 100 MILLISECONDS_PER_SECOND = 1000 DECIMICROSECONDS_PER_SECOND = 10000 MICROSECONDS_PER_DAY = 86400000000 MICROSECONDS_PER_SECOND = 1000000 MICROSECONDS_PER_DECISECOND = 100000 MICROSECONDS_PER_MILLISECOND = 1000 NANOSECONDS_PER_DAY = 86400000000000 NANOSECONDS_PER_SECOND = 1000000000 NANOSECONDS_PER_DECISECOND = 100000000 NANOSECONDS_PER_CENTISECOND = 10000000 NANOSECONDS_PER_MILLISECOND = 1000000 NANOSECONDS_PER_DECIMILISECOND = 100000 NANOSECONDS_PER_MICROSECOND = 1000 PRECISION_1_DAY = '1d' PRECISION_1_HOUR = '1h' PRECISION_1_NANOSECOND = '1ns' PRECISION_10_NANOSECONDS = '10s' PRECISION_100_NANOSECONDS = '100ns' PRECISION_1_MICROSECOND = '1us' PRECISION_10_MICROSECONDS = '10us' PRECISION_100_MICROSECONDS = '100us' PRECISION_1_MILLISECOND = '1ms' PRECISION_10_MILLISECONDS = '10ms' PRECISION_100_MILLISECONDS = '100ms' PRECISION_1_MINUTE = '1min' PRECISION_1_SECOND = '1s' PRECISION_2_SECONDS = '2s' PRECISION_VALUES = frozenset([ PRECISION_1_DAY, PRECISION_1_HOUR, PRECISION_1_NANOSECOND, PRECISION_10_NANOSECONDS, PRECISION_100_NANOSECONDS, PRECISION_1_MICROSECOND, PRECISION_10_MICROSECONDS, PRECISION_100_MICROSECONDS, PRECISION_1_MILLISECOND, PRECISION_10_MILLISECONDS, PRECISION_100_MILLISECONDS, PRECISION_1_MINUTE, PRECISION_1_SECOND, PRECISION_2_SECONDS]) # Create a days per century lookup table. DAYS_PER_CENTURY = {} for year in range(-10000, 10000, 100): if (year % 4 == 0 and year % 100 != 0) or year % 400 == 0: number_of_days = 36525 else: number_of_days = 36524 DAYS_PER_CENTURY[year] = number_of_days # Create a days per year lookup table. DAYS_PER_YEAR = {} for year in range(-10000, 10000, 1): if (year % 4 == 0 and year % 100 != 0) or year % 400 == 0: number_of_days = 366 else: number_of_days = 365 DAYS_PER_YEAR[year] = number_of_days # Create a days per year in POSIX epoch lookup table. DAYS_PER_YEAR_IN_POSIX_EPOCH = {} number_of_days = 0 for year in range(1969, -10000, -1): number_of_days -= DAYS_PER_YEAR[year] DAYS_PER_YEAR_IN_POSIX_EPOCH[year] = number_of_days number_of_days = 0 for year in range(1970, 10000, 1): DAYS_PER_YEAR_IN_POSIX_EPOCH[year] = number_of_days number_of_days += DAYS_PER_YEAR[year] dfdatetime-20240504/dfdatetime/delphi_date_time.py000066400000000000000000000123011461534226200220240ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Delphi TDateTime implementation.""" import decimal from dfdatetime import definitions from dfdatetime import factory from dfdatetime import interface class DelphiDateTimeEpoch(interface.DateTimeEpoch): """Delphi TDateTime epoch.""" def __init__(self): """Initializes a Delphi TDateTime epoch.""" super(DelphiDateTimeEpoch, self).__init__(1899, 12, 30) class DelphiDateTime(interface.DateTimeValues): """Delphi TDateTime timestamp. The Delphi TDateTime timestamp is a floating point value that contains the number of days since 1899-12-30 00:00:00 (also known as the epoch). Negative values represent date and times predating the epoch. The maximal correct date supported by TDateTime values is limited to: 9999-12-31 23:59:59.999 Attributes: is_local_time (bool): True if the date and time value is in local time. """ # The difference between December 30, 1899 and January 1, 1970 in days. _DELPHI_TO_POSIX_BASE = 25569 _EPOCH = DelphiDateTimeEpoch() def __init__(self, precision=None, time_zone_offset=None, timestamp=None): """Initializes a Delphi TDateTime timestamp. Args: precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. timestamp (Optional[float]): Delphi TDateTime timestamp. """ super(DelphiDateTime, self).__init__( precision=precision or definitions.PRECISION_1_MILLISECOND, time_zone_offset=time_zone_offset) self._timestamp = timestamp @property def timestamp(self): """float: Delphi TDateTime timestamp or None if not set.""" return self._timestamp def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if self._timestamp is not None: self._normalized_timestamp = ( decimal.Decimal(self._timestamp) - self._DELPHI_TO_POSIX_BASE) self._normalized_timestamp *= definitions.SECONDS_PER_DAY if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def CopyFromDateTimeString(self, time_string): """Copies a Delphi TDateTime timestamp from a string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Raises: ValueError: if the time string is invalid or not supported. """ date_time_values = self._CopyDateTimeFromString(time_string) year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) nanoseconds = date_time_values.get('nanoseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) if year > 9999: raise ValueError(f'Unsupported year value: {year:d}.') timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp = float(timestamp) / definitions.SECONDS_PER_DAY timestamp += self._DELPHI_TO_POSIX_BASE timestamp += float(nanoseconds) / definitions.NANOSECONDS_PER_DAY self._normalized_timestamp = None self._timestamp = timestamp self._time_zone_offset = time_zone_offset def CopyToDateTimeString(self): """Copies the Delphi TDateTime timestamp to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.######" or None if the timestamp is missing. """ if self._timestamp is None: return None number_of_seconds = self._timestamp * definitions.SECONDS_PER_DAY number_of_days, hours, minutes, seconds = self._GetTimeValues( int(number_of_seconds)) # The maximum date supported by TDateTime values is limited to: # 9999-12-31 23:59:59.999 (approximate 2958465 days since epoch). # The minimum date is unknown hence assuming it is limited to: # 0001-01-01 00:00:00.000 (approximate -693593 days since epoch). if number_of_days < -693593 or number_of_days > 2958465: return None year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) microseconds = int( (number_of_seconds % 1) * definitions.MICROSECONDS_PER_SECOND) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}.{microseconds:06d}') factory.Factory.RegisterDateTimeValues(DelphiDateTime) dfdatetime-20240504/dfdatetime/dotnet_datetime.py000066400000000000000000000113141461534226200217200ustar00rootroot00000000000000# -*- coding: utf-8 -*- """.NET DateTime implementation.""" import decimal from dfdatetime import definitions from dfdatetime import factory from dfdatetime import interface class DotNetDateTimeEpoch(interface.DateTimeEpoch): """.NET DateTime epoch.""" def __init__(self): """Initializes a .NET DateTime epoch.""" super(DotNetDateTimeEpoch, self).__init__(1, 1, 1) class DotNetDateTime(interface.DateTimeValues): """.NET DateTime ticks. The .NET DateTime timestamp is a 64-bit signed integer that contains the date and time as the number of 100 nanoseconds since 12:00 AM January 1, year 1 A.D. in the proleptic Gregorian Calendar. """ _EPOCH = DotNetDateTimeEpoch() # The difference between January 1, 1 and January 1, 1970 in seconds. _DOTNET_TO_POSIX_BASE = ( ((1969 * 365) + (1969 // 4) - (1969 // 100) + (1969 // 400)) * definitions.SECONDS_PER_DAY) def __init__(self, precision=None, time_zone_offset=None, timestamp=None): """Initializes a .NET DateTime timestamp. Args: precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. timestamp (Optional[int]): .NET DateTime ticks. """ super(DotNetDateTime, self).__init__( precision=precision or definitions.PRECISION_100_NANOSECONDS, time_zone_offset=time_zone_offset) self._timestamp = timestamp or 0 @property def timestamp(self): """integer: .NET DateTime timestamp or None if not set.""" return self._timestamp def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if self._timestamp is not None: self._normalized_timestamp = ( decimal.Decimal(self._timestamp) / self._100_NANOSECONDS_PER_SECOND) self._normalized_timestamp -= self._DOTNET_TO_POSIX_BASE if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def CopyFromDateTimeString(self, time_string): """Copies a .NET DateTime timestamp from a string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Raises: ValueError: if the time string is invalid or not supported. """ date_time_values = self._CopyDateTimeFromString(time_string) year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) nanoseconds = date_time_values.get('nanoseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) if year > 9999: raise ValueError(f'Unsupported year value: {year:d}.') nanoseconds, _ = divmod(nanoseconds, 100) timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp += self._DOTNET_TO_POSIX_BASE timestamp *= self._100_NANOSECONDS_PER_SECOND timestamp += nanoseconds self._normalized_timestamp = None self._timestamp = timestamp self._time_zone_offset = time_zone_offset def CopyToDateTimeString(self): """Copies the .NET DateTime timestamp to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.######" or None if the timestamp is missing. """ if (self._timestamp is None or self._timestamp < 0 or self._timestamp > self._UINT64_MAX): return None timestamp, fraction_of_second = divmod( self._timestamp, self._100_NANOSECONDS_PER_SECOND) number_of_days, hours, minutes, seconds = self._GetTimeValues(timestamp) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}.{fraction_of_second:07d}') factory.Factory.RegisterDateTimeValues(DotNetDateTime) dfdatetime-20240504/dfdatetime/factory.py000066400000000000000000000034661461534226200202270ustar00rootroot00000000000000# -*- coding: utf-8 -*- """The date and time values factory.""" class Factory(object): """Date and time values factory.""" _date_time_values_types = {} @classmethod def DeregisterDateTimeValues(cls, date_time_values_type): """Deregisters a date and time values type. Args: date_time_values_type (type): date and time values type. Raises: KeyError: if date and time values type is not registered. """ class_name = date_time_values_type.__name__ if class_name not in cls._date_time_values_types: raise KeyError(f'Date and time values type: {class_name:s} not set.') del cls._date_time_values_types[class_name] @classmethod def NewDateTimeValues(cls, class_name, **kwargs): """Creates a new date and time values for the specific type indicator. Args: class_name (str): type indicator. kwargs (dict): keyword arguments depending on the date and time values. Returns: DateTimeValues: date and time values. Raises: KeyError: if date and time values is not registered. """ if class_name not in cls._date_time_values_types: raise KeyError(f'Date and time values type: {class_name:s} not set.') date_time_values_type = cls._date_time_values_types[class_name] return date_time_values_type(**kwargs) @classmethod def RegisterDateTimeValues(cls, date_time_values_type): """Registers a date and time values type. Args: date_time_values_type (type): date and time values type. Raises: KeyError: if date and time values is already registered. """ class_name = date_time_values_type.__name__ if class_name in cls._date_time_values_types: raise KeyError(f'Date and time values type: {class_name:s} already set.') cls._date_time_values_types[class_name] = date_time_values_type dfdatetime-20240504/dfdatetime/fake_time.py000066400000000000000000000102611461534226200204730ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Fake timestamp implementation.""" import decimal import time from dfdatetime import definitions from dfdatetime import interface from dfdatetime import posix_time class FakeTime(interface.DateTimeValues): """Fake timestamp. The fake timestamp is intended for testing purposes. On initialization it contains the current time in UTC in microsecond precision. Attributes: is_local_time (bool): True if the date and time value is in local time. """ _EPOCH = posix_time.PosixTimeEpoch() def __init__(self, precision=None, time_zone_offset=None): """Initializes a fake timestamp. Args: precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. """ # Note that time.time() and divmod return floating point values. timestamp, fraction_of_second = divmod(time.time(), 1) super(FakeTime, self).__init__( precision=precision or definitions.PRECISION_1_MICROSECOND, time_zone_offset=time_zone_offset) self._microseconds = int( fraction_of_second * definitions.MICROSECONDS_PER_SECOND) self._number_of_seconds = int(timestamp) def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if self._number_of_seconds is not None: self._normalized_timestamp = ( decimal.Decimal(self._microseconds) / definitions.MICROSECONDS_PER_SECOND) self._normalized_timestamp += decimal.Decimal(self._number_of_seconds) if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def CopyFromDateTimeString(self, time_string): """Copies a fake timestamp from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. """ date_time_values = self._CopyDateTimeFromString(time_string) year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) nanoseconds = date_time_values.get('nanoseconds', None) time_zone_offset = date_time_values.get('time_zone_offset', None) self._normalized_timestamp = None self._number_of_seconds = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) if nanoseconds is None: self._microseconds = None else: self._microseconds, _ = divmod(nanoseconds, 1000) self._time_zone_offset = time_zone_offset def CopyToDateTimeString(self): """Copies the fake timestamp to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss" or "YYYY-MM-DD hh:mm:ss.######" or None if the number of seconds is missing. """ if self._number_of_seconds is None: return None number_of_days, hours, minutes, seconds = self._GetTimeValues( self._number_of_seconds) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) date_time_string = ( f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}') if self._microseconds is not None: date_time_string = '.'.join([ date_time_string, f'{self._microseconds:06d}']) return date_time_string dfdatetime-20240504/dfdatetime/fat_date_time.py000066400000000000000000000244751461534226200213500ustar00rootroot00000000000000# -*- coding: utf-8 -*- """FAT date time implementation.""" import decimal from dfdatetime import definitions from dfdatetime import factory from dfdatetime import interface class FATDateTimeEpoch(interface.DateTimeEpoch): """FAT date time time epoch.""" def __init__(self): """Initializes a FAT date time epoch.""" super(FATDateTimeEpoch, self).__init__(1980, 1, 1) class FATDateTime(interface.DateTimeValues): """FAT date time. The FAT date time is mainly used in DOS/Windows file formats and FAT. The FAT date and time is a 32-bit value containing two 16-bit values: * The date (lower 16-bit). * bits 0 - 4: day of month, where 1 represents the first day * bits 5 - 8: month of year, where 1 represent January * bits 9 - 15: year since 1980 * The time of day (upper 16-bit). * bits 0 - 4: seconds (in 2 second intervals) * bits 5 - 10: minutes * bits 11 - 15: hours The FAT date time has no time zone information and is typically stored in the local time of the computer. Attributes: is_local_time (bool): True if the date and time value is in local time. """ _EPOCH = FATDateTimeEpoch() # The difference between January 1, 1980 and January 1, 1970 in seconds. _FAT_DATE_TO_POSIX_BASE = 315532800 def __init__(self, fat_date_time=None, precision=None, time_zone_offset=None): """Initializes a FAT date time. Args: fat_date_time (Optional[int]): FAT date time. precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. """ super(FATDateTime, self).__init__( precision=precision or definitions.PRECISION_2_SECONDS, time_zone_offset=time_zone_offset) self._fat_date_time = fat_date_time self._number_of_seconds = None if fat_date_time is not None: self._number_of_seconds = self._GetNumberOfSeconds(fat_date_time) @property def fat_date_time(self): """int: FAT date time or None if not set.""" return self._fat_date_time def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if self._number_of_seconds is not None and self._number_of_seconds >= 0: self._normalized_timestamp = ( decimal.Decimal(self._number_of_seconds) + self._FAT_DATE_TO_POSIX_BASE) if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def _GetNumberOfSeconds(self, fat_date_time): """Retrieves the number of seconds from a FAT date time. Args: fat_date_time (int): FAT date time. Returns: int: number of seconds since January 1, 1980 00:00:00. Raises: ValueError: if the month, day of month, hours, minutes or seconds value is out of bounds. """ day_of_month = fat_date_time & 0x1f month = (fat_date_time >> 5) & 0x0f year = (fat_date_time >> 9) & 0x7f days_per_month = self._GetDaysPerMonth(year, month) if day_of_month < 1 or day_of_month > days_per_month: raise ValueError('Day of month value out of bounds.') number_of_days = self._GetDayOfYear(1980 + year, month, day_of_month) number_of_days -= 1 for past_year in range(0, year): number_of_days += self._GetNumberOfDaysInYear(past_year) fat_date_time >>= 16 seconds = (fat_date_time & 0x1f) * 2 minutes = (fat_date_time >> 5) & 0x3f hours = (fat_date_time >> 11) & 0x1f if hours not in range(0, 24): raise ValueError('Hours value out of bounds.') if minutes not in range(0, 60): raise ValueError('Minutes value out of bounds.') if seconds not in range(0, 60): raise ValueError('Seconds value out of bounds.') number_of_seconds = (((hours * 60) + minutes) * 60) + seconds number_of_seconds += number_of_days * definitions.SECONDS_PER_DAY return number_of_seconds def CopyFromDateTimeString(self, time_string): """Copies a FAT date time from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Raises: ValueError: if the time string is invalid or not supported. """ date_time_values = self._CopyDateTimeFromString(time_string) year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) if year < 1980 or year > (1980 + 0x7f): raise ValueError(f'Year value not supported: {year!s}.') self._normalized_timestamp = None self._number_of_seconds = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) self._number_of_seconds -= self._FAT_DATE_TO_POSIX_BASE self._time_zone_offset = time_zone_offset def CopyToDateTimeString(self): """Copies the FAT date time to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss" or None if number of seconds is missing. """ if self._number_of_seconds is None: return None number_of_days, hours, minutes, seconds = self._GetTimeValues( self._number_of_seconds) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}') class FATTimestamp(interface.DateTimeValues): """FAT timestamp. The FAT timestamp is an unsigned integer that contains the number of 10 milli seconds intervals since 1980-01-01 00:00:00 (also known as the FAT date time epoch). Attributes: is_local_time (bool): True if the date and time value is in local time. """ _EPOCH = FATDateTimeEpoch() # The difference between January 1, 1980 and January 1, 1970 in seconds. _FAT_DATE_TO_POSIX_BASE = 315532800 def __init__(self, precision=None, time_zone_offset=None, timestamp=None): """Initializes a FAT timestamp. Args: precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. timestamp (Optional[int]): FAT timestamp. """ super(FATTimestamp, self).__init__( precision=precision or definitions.PRECISION_10_MILLISECONDS, time_zone_offset=time_zone_offset) self._timestamp = timestamp @property def timestamp(self): """int: FAT timestamp or None if not set.""" return self._timestamp def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if self._timestamp is not None: self._normalized_timestamp = ( (decimal.Decimal(self._timestamp) / 100) + self._FAT_DATE_TO_POSIX_BASE) if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def CopyFromDateTimeString(self, time_string): """Copies a FAT timestamp from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Raises: ValueError: if the time string is invalid or not supported. """ date_time_values = self._CopyDateTimeFromString(time_string) year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) nanoseconds = date_time_values.get('nanoseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) if year < 1980 or year > (1980 + 0x7f): raise ValueError(f'Year value not supported: {year!s}.') milliseconds, _ = divmod(nanoseconds, 10000000) timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp -= self._FAT_DATE_TO_POSIX_BASE timestamp *= 100 timestamp += milliseconds self._timestamp = timestamp self._time_zone_offset = time_zone_offset def CopyToDateTimeString(self): """Copies the FAT timestamp to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.######" or None if the timestamp is missing. """ if self._timestamp is None: return None timestamp, milliseconds = divmod(self._timestamp, 100) number_of_days, hours, minutes, seconds = self._GetTimeValues(timestamp) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}.{milliseconds:02d}') factory.Factory.RegisterDateTimeValues(FATDateTime) factory.Factory.RegisterDateTimeValues(FATTimestamp) dfdatetime-20240504/dfdatetime/filetime.py000066400000000000000000000114371461534226200203530ustar00rootroot00000000000000# -*- coding: utf-8 -*- """FILETIME timestamp implementation.""" import decimal from dfdatetime import definitions from dfdatetime import factory from dfdatetime import interface class FiletimeEpoch(interface.DateTimeEpoch): """FILETIME epoch.""" def __init__(self): """Initializes a FILETIME epoch.""" super(FiletimeEpoch, self).__init__(1601, 1, 1) class Filetime(interface.DateTimeValues): """FILETIME timestamp. The FILETIME timestamp is a 64-bit integer that contains the number of 100th nano seconds since 1601-01-01 00:00:00. Do not confuse this with the FILETIME structure that consists of 2 x 32-bit integers and is presumed to be unsigned. Attributes: is_local_time (bool): True if the date and time value is in local time. """ _EPOCH = FiletimeEpoch() # The difference between January 1, 1601 and January 1, 1970 in seconds. _FILETIME_TO_POSIX_BASE = 11644473600 def __init__(self, precision=None, time_zone_offset=None, timestamp=None): """Initializes a FILETIME timestamp. Args: precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. timestamp (Optional[int]): FILETIME timestamp. """ super(Filetime, self).__init__( precision=precision or definitions.PRECISION_100_NANOSECONDS, time_zone_offset=time_zone_offset) self._timestamp = timestamp @property def timestamp(self): """int: FILETIME timestamp or None if not set.""" return self._timestamp def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if (self._timestamp is not None and self._timestamp >= 0 and self._timestamp <= self._UINT64_MAX): self._normalized_timestamp = ( decimal.Decimal(self._timestamp) / self._100_NANOSECONDS_PER_SECOND) self._normalized_timestamp -= self._FILETIME_TO_POSIX_BASE if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def CopyFromDateTimeString(self, time_string): """Copies a FILETIME timestamp from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Raises: ValueError: if the time string is invalid or not supported. """ date_time_values = self._CopyDateTimeFromString(time_string) year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) nanoseconds = date_time_values.get('nanoseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) if year < 1601: raise ValueError(f'Year value not supported: {year!s}.') nanoseconds, _ = divmod(nanoseconds, 100) timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp += self._FILETIME_TO_POSIX_BASE timestamp *= self._100_NANOSECONDS_PER_SECOND timestamp += nanoseconds self._normalized_timestamp = None self._timestamp = timestamp self._time_zone_offset = time_zone_offset def CopyToDateTimeString(self): """Copies the FILETIME timestamp to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.#######" or None if the timestamp is missing or invalid. """ if (self._timestamp is None or self._timestamp < 0 or self._timestamp > self._UINT64_MAX): return None timestamp, fraction_of_second = divmod( self._timestamp, self._100_NANOSECONDS_PER_SECOND) number_of_days, hours, minutes, seconds = self._GetTimeValues(timestamp) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}.{fraction_of_second:07d}') factory.Factory.RegisterDateTimeValues(Filetime) dfdatetime-20240504/dfdatetime/golang_time.py000066400000000000000000000167601461534226200210460ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Golang time.Time timestamp implementation.""" import decimal import struct from dfdatetime import definitions from dfdatetime import factory from dfdatetime import interface class GolangTimeEpoch(interface.DateTimeEpoch): """Golang time.Time epoch.""" def __init__(self): """Initializes a Golang time.Time epoch.""" super(GolangTimeEpoch, self).__init__(1, 1, 1) class GolangTime(interface.DateTimeValues): """Golang time.Time timestamp. A Golang time.Time timestamp contains the number of nanoseconds since January 1, 1 UTC. Depending on the version of the timestamp, the time zone is stored in minutes or seconds relative to UTC. A serialized version 1 Golang time.Time timestamp is a 15 byte value that consists of: * byte 0 - version as an 8-bit integer. * bytes 1-8 - number of seconds since January 1, 1 as a big-endian signed integer. * bytes 9-12 - fraction of second, number of nanoseconds as a big-endian signed integer. * bytes 13-14 - time zone offset in minutes as a 16-bit big-endian integer, where -1 represents UTC. A serialized version 2 Golang time.Time timestamp is a 16 byte value that consists of: * byte 0 - version as an 8-bit integer. * bytes 1-8 - number of seconds since January 1, 1 as a big-endian signed integer. * bytes 9-12 - fraction of second, number of nanoseconds as a big-endian signed integer. * bytes 13-14 - time zone offset in minutes as a 16-bit big-endian integer, where -1 represents UTC. * byte 15 - time zone offset in seconds as an 8-bit integer. Attributes: is_local_time (bool): True if the date and time value is in local time """ # The delta between January 1, 1970 (unix epoch) and January 1, 1 # (Golang epoch). _GOLANG_TO_POSIX_BASE = ( ((1969 * 365) + (1969 // 4) - (1969 // 100) + (1969 // 400)) * definitions.SECONDS_PER_DAY) _EPOCH = GolangTimeEpoch() def __init__(self, golang_timestamp=None, precision=None): """Initializes a Golang time.Time timestamp. Args: golang_timestamp (Optional[bytes]): the Golang time.Time timestamp. precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. """ number_of_seconds, nanoseconds, time_zone_offset = (None, None, None) if golang_timestamp is not None: number_of_seconds, nanoseconds, time_zone_offset = ( self._GetNumberOfSeconds(golang_timestamp)) super(GolangTime, self).__init__( precision=precision or definitions.PRECISION_1_NANOSECOND, time_zone_offset=time_zone_offset) self._golang_timestamp = golang_timestamp self._nanoseconds = nanoseconds self._number_of_seconds = number_of_seconds @property def golang_timestamp(self): """int: Golang time.Time timestamp or None if not set.""" return self._golang_timestamp def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if (self._number_of_seconds is not None and self._number_of_seconds >= self._GOLANG_TO_POSIX_BASE and self._nanoseconds is not None and self._nanoseconds >= 0): self._normalized_timestamp = decimal.Decimal( self._number_of_seconds - GolangTime._GOLANG_TO_POSIX_BASE) if self._nanoseconds is not None and self._nanoseconds >= 0: self._normalized_timestamp += ( decimal.Decimal(self._nanoseconds) / definitions.NANOSECONDS_PER_SECOND) if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def _GetNumberOfSeconds(self, golang_timestamp): """Retrieves the number of seconds from a Golang time.Time timestamp. Args: golang_timestamp (bytes): the Golang time.Time timestamp. Returns: tuple[int, int, int]: number of seconds since January 1, 1 00:00:00, fraction of second in nanoseconds and time zone offset in minutes. Raises: ValueError: if the Golang time.Time timestamp could not be parsed. """ byte_size = len(golang_timestamp) if byte_size < 15: raise ValueError('Unsupported Golang time.Time timestamp.') version = golang_timestamp[0] if version not in (1, 2): raise ValueError( f'Unsupported Golang time.Time timestamp version: {version:d}.') if (version == 1 and byte_size != 15) or (version == 2 and byte_size != 16): raise ValueError('Unsupported Golang time.Time timestamp.') try: number_of_seconds, nanoseconds, time_zone_offset = struct.unpack( '>qih', golang_timestamp[1:15]) # TODO: add support for version 2 time zone offset in seconds except struct.error as exception: raise ValueError(( f'Unable to unpacked Golang time.Time timestamp with error: ' f'{exception!s}')) # A time zone offset of -1 minute is a special representation for UTC. if time_zone_offset == -1: time_zone_offset = 0 return number_of_seconds, nanoseconds, time_zone_offset def CopyFromDateTimeString(self, time_string): """Copies a date time value from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Raises: ValueError: if the time string is invalid or not supported. """ date_time_values = self._CopyDateTimeFromString(time_string) year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) nanoseconds = date_time_values.get('nanoseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) if year < 0: raise ValueError(f'Year value not supported: {year!s}.') seconds = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) seconds += self._GOLANG_TO_POSIX_BASE self._normalized_timestamp = None self._number_of_seconds = seconds self._nanoseconds = nanoseconds self._time_zone_offset = time_zone_offset def CopyToDateTimeString(self): """Copies the Golang time value to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.######" or None if the timestamp cannot be copied to a date and time string. """ if self._number_of_seconds is None or self._number_of_seconds < 0: return None number_of_days, hours, minutes, seconds = self._GetTimeValues( self._number_of_seconds) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}.{self._nanoseconds:09d}') factory.Factory.RegisterDateTimeValues(GolangTime) dfdatetime-20240504/dfdatetime/hfs_time.py000066400000000000000000000104331461534226200203460ustar00rootroot00000000000000# -*- coding: utf-8 -*- """HFS time implementation.""" import decimal from dfdatetime import definitions from dfdatetime import factory from dfdatetime import interface class HFSTimeEpoch(interface.DateTimeEpoch): """HFS time epoch.""" def __init__(self): """Initializes a HFS time epoch.""" super(HFSTimeEpoch, self).__init__(1904, 1, 1) class HFSTime(interface.DateTimeValues): """HFS timestamp. The HFS timestamp is an unsigned 32-bit integer that contains the number of seconds since 1904-01-01 00:00:00. Where in HFS the timestamp is typically in local time and in HFS+/HFSX in UTC. Attributes: is_local_time (bool): True if the date and time value is in local time. """ _EPOCH = HFSTimeEpoch() # The difference between Jan 1, 1904 and Jan 1, 1970 in seconds. _HFS_TO_POSIX_BASE = 2082844800 def __init__(self, precision=None, time_zone_offset=None, timestamp=None): """Initializes a HFS timestamp. Args: precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. timestamp (Optional[int]): HFS timestamp. """ super(HFSTime, self).__init__( precision=precision or definitions.PRECISION_1_SECOND, time_zone_offset=time_zone_offset) self._timestamp = timestamp @property def timestamp(self): """int: HFS timestamp or None if not set.""" return self._timestamp def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if (self._timestamp is not None and self._timestamp >= 0 and self._timestamp <= self._UINT32_MAX): self._normalized_timestamp = ( decimal.Decimal(self._timestamp) - self._HFS_TO_POSIX_BASE) if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def CopyFromDateTimeString(self, time_string): """Copies a HFS timestamp from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Raises: ValueError: if the time string is invalid or not supported. """ date_time_values = self._CopyDateTimeFromString(time_string) year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) if year < 1904 or year > 2040: raise ValueError('Year value not supported.') self._normalized_timestamp = None self._timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) self._timestamp += self._HFS_TO_POSIX_BASE self._time_zone_offset = time_zone_offset def CopyToDateTimeString(self): """Copies the HFS timestamp to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss" or None if the timestamp is missing or invalid. """ if (self._timestamp is None or self._timestamp < 0 or self._timestamp > self._UINT32_MAX): return None number_of_days, hours, minutes, seconds = self._GetTimeValues( self._timestamp) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}') factory.Factory.RegisterDateTimeValues(HFSTime) dfdatetime-20240504/dfdatetime/interface.py000066400000000000000000000755341461534226200205250ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Date and time interfaces.""" import abc import decimal from dfdatetime import definitions class DateTimeEpoch(object): """Date and time epoch interface. This is the super class of different epoch representations. Attributes: year (int): year that is the start of the epoch e.g. 1970. month (int): month that is the start of the epoch, where 1 represents January. day_of_month (int): day of the month that is the start of the epoch, where 1 represents the first day. """ def __init__(self, year, month, day_of_month): """Initializes a date time epoch. Args: year (int): year that is the start of the epoch e.g. 1970. month (int): month that is the start of the epoch, where 1 represents January. day_of_month (int): day of the month that is the start of the epoch, where 1 represents the first day. """ super(DateTimeEpoch, self).__init__() self.day_of_month = day_of_month self.month = month self.year = year class NormalizedTimeEpoch(DateTimeEpoch): """dfDateTime normalized time epoch.""" def __init__(self): """Initializes a dfDateTime normalized time epoch.""" super(NormalizedTimeEpoch, self).__init__(1970, 1, 1) class DateTimeValues(object): """Date and time values interface. This is the super class of different date and time representations. Attributes: is_local_time (bool): True if the date and time value is in local time. time_zone_hint (str): time zone hint, such as "Europe/Amsterdam", "CET" or "UTC+1", or None if not set. """ # pylint: disable=redundant-returns-doc _EPOCH_NORMALIZED_TIME = NormalizedTimeEpoch() _100_MILLISECONDS_PER_SECOND = 10 _10_MILLISECONDS_PER_SECOND = 100 _1_MILLISECOND_PER_SECOND = 1000 _100_MICROSECONDS_PER_SECOND = 10000 _10_MICROSECONDS_PER_SECOND = 100000 _1_MICROSECOND_PER_SECOND = 1000000 _100_NANOSECONDS_PER_SECOND = 10000000 _10_NANOSECONDS_PER_SECOND = 100000000 _1_NANOSECOND_PER_SECOND = definitions.NANOSECONDS_PER_SECOND _100_NANOSECONDS_PER_MICROSECOND = 10 _INT64_MIN = -(1 << 63) _INT64_MAX = (1 << 63) - 1 _UINT32_MAX = (1 << 32) - 1 _UINT60_MAX = (1 << 60) - 1 _UINT64_MAX = (1 << 64) - 1 _REMAINDER_MULTIPLIER = { definitions.PRECISION_1_MILLISECOND: _1_MILLISECOND_PER_SECOND, definitions.PRECISION_10_MILLISECONDS: _10_MILLISECONDS_PER_SECOND, definitions.PRECISION_100_MILLISECONDS: _100_MILLISECONDS_PER_SECOND, definitions.PRECISION_1_MICROSECOND: _1_MICROSECOND_PER_SECOND, definitions.PRECISION_10_MICROSECONDS: _10_MICROSECONDS_PER_SECOND, definitions.PRECISION_100_MICROSECONDS: _100_MICROSECONDS_PER_SECOND, definitions.PRECISION_1_NANOSECOND: _1_NANOSECOND_PER_SECOND, definitions.PRECISION_10_NANOSECONDS: _10_NANOSECONDS_PER_SECOND, definitions.PRECISION_100_NANOSECONDS: _100_NANOSECONDS_PER_SECOND} def __init__(self, is_delta=False, precision=None, time_zone_offset=None): """Initializes date time values. Args: is_delta (Optional[bool]): True if the date and time value is relative to another date and time value. precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. """ super(DateTimeValues, self).__init__() self._cached_date_time_values = None self._is_delta = is_delta self._normalized_timestamp = None self._precision = precision self._time_zone_offset = time_zone_offset self.is_local_time = False self.time_zone_hint = False @property def is_delta(self): """Is delta (bool): True if the date and time is relative to another.""" return self._is_delta @property def precision(self): """Precision (str): precision of the date and time value.""" return self._precision @property def time_zone_offset(self): """Time zone offset (int): time zone offset in minutes from UTC.""" return self._time_zone_offset @time_zone_offset.setter def time_zone_offset(self, time_zone_offset): """Sets the time zone offset. Args: time_zone_offset (int): time zone offset in number of minutes from UTC or None if not set. """ self._normalized_timestamp = None self._time_zone_offset = time_zone_offset def __eq__(self, other): """Determines if the date time values are equal to other. Args: other (DateTimeValues): date time values to compare against. Returns: bool: True if the date time values are equal to other. """ if not isinstance(other, DateTimeValues): return False normalized_timestamp = self._GetNormalizedTimestamp() other_normalized_timestamp = other._GetNormalizedTimestamp() # pylint: disable=protected-access if normalized_timestamp is None and other_normalized_timestamp is not None: return False if normalized_timestamp is not None and other_normalized_timestamp is None: return False return normalized_timestamp == other_normalized_timestamp def __ge__(self, other): """Determines if the date time values are greater than or equal to other. Args: other (DateTimeValues): date time values to compare against. Returns: bool: True if the date time values are greater than or equal to other. Raises: ValueError: if other is not an instance of DateTimeValues. """ if not isinstance(other, DateTimeValues): raise ValueError('Other not an instance of DateTimeValues') normalized_timestamp = self._GetNormalizedTimestamp() other_normalized_timestamp = other._GetNormalizedTimestamp() # pylint: disable=protected-access if normalized_timestamp is None: return other_normalized_timestamp is None if other_normalized_timestamp is None: return True return normalized_timestamp >= other_normalized_timestamp def __gt__(self, other): """Determines if the date time values are greater than other. Args: other (DateTimeValues): date time values to compare against. Returns: bool: True if the date time values are greater than other. Raises: ValueError: if other is not an instance of DateTimeValues. """ if not isinstance(other, DateTimeValues): raise ValueError('Other not an instance of DateTimeValues') normalized_timestamp = self._GetNormalizedTimestamp() other_normalized_timestamp = other._GetNormalizedTimestamp() # pylint: disable=protected-access if normalized_timestamp is None: return False if other_normalized_timestamp is None: return True return normalized_timestamp > other_normalized_timestamp def __le__(self, other): """Determines if the date time values are greater than or equal to other. Args: other (DateTimeValues): date time values to compare against. Returns: bool: True if the date time values are greater than or equal to other. Raises: ValueError: if other is not an instance of DateTimeValues. """ if not isinstance(other, DateTimeValues): raise ValueError('Other not an instance of DateTimeValues') normalized_timestamp = self._GetNormalizedTimestamp() other_normalized_timestamp = other._GetNormalizedTimestamp() # pylint: disable=protected-access if normalized_timestamp is None: return True if other_normalized_timestamp is None: return False return normalized_timestamp <= other_normalized_timestamp def __lt__(self, other): """Determines if the date time values are less than other. Args: other (DateTimeValues): date time values to compare against. Returns: bool: True if the date time values are less than other. Raises: ValueError: if other is not an instance of DateTimeValues. """ if not isinstance(other, DateTimeValues): raise ValueError('Other not an instance of DateTimeValues') normalized_timestamp = self._GetNormalizedTimestamp() other_normalized_timestamp = other._GetNormalizedTimestamp() # pylint: disable=protected-access if normalized_timestamp is None: return other_normalized_timestamp is not None if other_normalized_timestamp is None: return False return normalized_timestamp < other_normalized_timestamp def __ne__(self, other): """Determines if the date time values are not equal to other. Args: other (DateTimeValues): date time values to compare against. Returns: bool: True if the date time values are not equal to other. """ if not isinstance(other, DateTimeValues): return True normalized_timestamp = self._GetNormalizedTimestamp() other_normalized_timestamp = other._GetNormalizedTimestamp() # pylint: disable=protected-access if normalized_timestamp is None and other_normalized_timestamp is not None: return True if normalized_timestamp is not None and other_normalized_timestamp is None: return True return normalized_timestamp != other_normalized_timestamp def _CopyDateFromString(self, date_string): """Copies a date from a string. Args: date_string (str): date value formatted as: YYYY-MM-DD Returns: tuple[int, int, int]: year, month, day of month. Raises: ValueError: if the date string is invalid or not supported. """ date_string_length = len(date_string) # The date string should at least contain 'YYYY-MM-DD'. if date_string_length < 10: raise ValueError('Date string too short.') if date_string[4] != '-' or date_string[7] != '-': raise ValueError('Invalid date string.') try: year = int(date_string[0:4], 10) except ValueError: raise ValueError('Unable to parse year.') try: month = int(date_string[5:7], 10) except ValueError: raise ValueError('Unable to parse month.') try: day_of_month = int(date_string[8:10], 10) except ValueError: raise ValueError('Unable to parse day of month.') days_per_month = self._GetDaysPerMonth(year, month) if day_of_month < 1 or day_of_month > days_per_month: raise ValueError('Day of month value out of bounds.') return year, month, day_of_month def _CopyDateTimeFromString(self, time_string): """Copies a date and time from a string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Returns: dict[str, int]: date and time values, such as year, month, day of month, hours, minutes, seconds, nanoseconds, time zone offset in minutes. Raises: ValueError: if the time string is invalid or not supported. """ if not time_string: raise ValueError('Invalid time string.') time_string_length = len(time_string) year, month, day_of_month = self._CopyDateFromString(time_string) if time_string_length <= 10: return { 'year': year, 'month': month, 'day_of_month': day_of_month} # If a time of day is specified the time string it should at least # contain 'YYYY-MM-DD hh:mm:ss'. if time_string[10] != ' ': raise ValueError( 'Invalid time string - space missing as date and time separator.') hours, minutes, seconds, nanoseconds, time_zone_offset = ( self._CopyTimeFromString(time_string[11:])) date_time_values = { 'year': year, 'month': month, 'day_of_month': day_of_month, 'hours': hours, 'minutes': minutes, 'seconds': seconds} if nanoseconds is not None: date_time_values['nanoseconds'] = nanoseconds if time_zone_offset is not None: date_time_values['time_zone_offset'] = time_zone_offset return date_time_values def _CopyTimeFromString(self, time_string): """Copies a time from a string. Args: time_string (str): time value formatted as: hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The seconds fraction and time zone offset are optional. Returns: tuple[int, int, int, int, int]: hours, minutes, seconds, nanoseconds, time zone offset in minutes. Raises: ValueError: if the time string is invalid or not supported. """ time_string_length = len(time_string) # The time string should at least contain 'hh:mm:ss'. if time_string_length < 8: raise ValueError('Time string too short.') if time_string[2] != ':' or time_string[5] != ':': raise ValueError('Invalid time string.') try: hours = int(time_string[0:2], 10) except ValueError: raise ValueError('Unable to parse hours.') if hours not in range(0, 24): raise ValueError(f'Hours value: {hours:d} out of bounds.') try: minutes = int(time_string[3:5], 10) except ValueError: raise ValueError('Unable to parse minutes.') if minutes not in range(0, 60): raise ValueError(f'Minutes value: {minutes:d} out of bounds.') try: seconds = int(time_string[6:8], 10) except ValueError: raise ValueError('Unable to parse day of seconds.') # TODO: support a leap second? if seconds not in range(0, 60): raise ValueError(f'Seconds value: {seconds:d} out of bounds.') nanoseconds = None time_zone_offset = None time_zone_string_index = 8 while time_zone_string_index < time_string_length: if time_string[time_zone_string_index] in ('+', '-'): break time_zone_string_index += 1 # The calculations that follow rely on the time zone string index # to point beyond the string in case no time zone offset was defined. if time_zone_string_index == time_string_length - 1: time_zone_string_index += 1 if time_string_length > 8 and time_string[8] == '.': time_fraction_length = time_zone_string_index - 9 if time_fraction_length not in (3, 6, 9): raise ValueError('Invalid time string.') try: time_fraction = time_string[9:time_zone_string_index] time_fraction = int(time_fraction, 10) except ValueError: raise ValueError('Unable to parse time fraction.') if time_fraction_length == 3: time_fraction *= 1000000 elif time_fraction_length == 6: time_fraction *= 1000 nanoseconds = time_fraction if time_zone_string_index < time_string_length: if (time_string_length - time_zone_string_index != 6 or time_string[time_zone_string_index + 3] != ':'): raise ValueError('Invalid time string.') try: hours_from_utc = int(time_string[ time_zone_string_index + 1:time_zone_string_index + 3]) except ValueError: raise ValueError('Unable to parse time zone hours offset.') if hours_from_utc not in range(0, 15): raise ValueError('Time zone hours offset value out of bounds.') try: minutes_from_utc = int(time_string[ time_zone_string_index + 4:time_zone_string_index + 6]) except ValueError: raise ValueError('Unable to parse time zone minutes offset.') if minutes_from_utc not in range(0, 60): raise ValueError('Time zone minutes offset value out of bounds.') # pylint: disable=invalid-unary-operand-type time_zone_offset = (hours_from_utc * 60) + minutes_from_utc if time_string[time_zone_string_index] == '-': time_zone_offset = -time_zone_offset return hours, minutes, seconds, nanoseconds, time_zone_offset def _GetDateValues( self, number_of_days, epoch_year, epoch_month, epoch_day_of_month): """Determines date values. Args: number_of_days (int): number of days since epoch. epoch_year (int): year that is the start of the epoch e.g. 1970. epoch_month (int): month that is the start of the epoch, where 1 represents January. epoch_day_of_month (int): day of month that is the start of the epoch, where 1 represents the first day. Returns: tuple[int, int, int]: year, month, day of month. Raises: ValueError: if the epoch year, month or day of month values are out of bounds. """ if epoch_year < 0: raise ValueError(f'Epoch year value: {epoch_year:d} out of bounds.') if epoch_month not in range(1, 13): raise ValueError(f'Epoch month value: {epoch_month:d} out of bounds.') epoch_days_per_month = self._GetDaysPerMonth(epoch_year, epoch_month) if epoch_day_of_month < 1 or epoch_day_of_month > epoch_days_per_month: raise ValueError( f'Epoch day of month value: {epoch_day_of_month:d} out of bounds.') before_epoch = number_of_days < 0 year = epoch_year month = epoch_month if before_epoch: month -= 1 if month <= 0: month = 12 year -= 1 number_of_days += epoch_day_of_month if before_epoch: number_of_days *= -1 # Align with the start of the year. while month > 1: days_per_month = self._GetDaysPerMonth(year, month) if number_of_days < days_per_month: break if before_epoch: month -= 1 else: month += 1 if month > 12: month = 1 year += 1 number_of_days -= days_per_month # Align with the start of the next century. _, remainder = divmod(year, 100) for _ in range(remainder, 100): days_in_year = self._GetNumberOfDaysInYear(year) if number_of_days < days_in_year: break if before_epoch: year -= 1 else: year += 1 number_of_days -= days_in_year days_in_century = self._GetNumberOfDaysInCentury(year) while number_of_days > days_in_century: if before_epoch: year -= 100 else: year += 100 number_of_days -= days_in_century days_in_century = self._GetNumberOfDaysInCentury(year) days_in_year = self._GetNumberOfDaysInYear(year) while number_of_days > days_in_year: if before_epoch: year -= 1 else: year += 1 number_of_days -= days_in_year days_in_year = self._GetNumberOfDaysInYear(year) days_per_month = self._GetDaysPerMonth(year, month) while number_of_days > days_per_month: if before_epoch: month -= 1 else: month += 1 if month <= 0: month = 12 year -= 1 elif month > 12: month = 1 year += 1 number_of_days -= days_per_month days_per_month = self._GetDaysPerMonth(year, month) if before_epoch: days_per_month = self._GetDaysPerMonth(year, month) number_of_days = days_per_month - number_of_days elif number_of_days == 0: number_of_days = 31 month = 12 year -= 1 return year, month, number_of_days def _GetDateValuesWithEpoch(self, number_of_days, date_time_epoch): """Determines date values. Args: number_of_days (int): number of days since epoch. date_time_epoch (DateTimeEpoch): date and time of the epoch. Returns: tuple[int, int, int]: year, month, day of month. """ return self._GetDateValues( number_of_days, date_time_epoch.year, date_time_epoch.month, date_time_epoch.day_of_month) def _GetDateWithTimeOfDay(self): """Retrieves the date with time of day. Note that the date and time are adjusted to UTC. Returns: tuple[int, int, int, int, int, int]: year, month, day of month, hours, minutes, seconds or (None, None, None, None, None, None) if the date and time values do not represent a date or time of day. """ normalized_timestamp = self._GetNormalizedTimestamp() if normalized_timestamp is None: return None, None, None, None, None, None if (not self._cached_date_time_values or self._cached_date_time_values[0] != normalized_timestamp): number_of_days, hours, minutes, seconds = self._GetTimeValues( normalized_timestamp) try: year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH_NORMALIZED_TIME) except ValueError: return None, None, None, None, None, None self._cached_date_time_values = ( normalized_timestamp, year, month, day_of_month, hours, minutes, seconds) return self._cached_date_time_values[1:] def _GetDayOfYear(self, year, month, day_of_month): """Retrieves the day of the year for a specific day of a month in a year. Args: year (int): year e.g. 1970. month (int): month, where 1 represents January. day_of_month (int): day of the month, where 1 represents the first day. Returns: int: day of year. Raises: ValueError: if the month or day of month value is out of bounds. """ if month not in range(1, 13): raise ValueError('Month value out of bounds.') days_per_month = self._GetDaysPerMonth(year, month) if day_of_month < 1 or day_of_month > days_per_month: raise ValueError('Day of month value out of bounds.') day_of_year = day_of_month for past_month in range(1, month): day_of_year += self._GetDaysPerMonth(year, past_month) return day_of_year def _GetDaysPerMonth(self, year, month): """Retrieves the number of days in a month of a specific year. Args: year (int): year e.g. 1970. month (int): month, where 1 represents January. Returns: int: number of days in the month. Raises: ValueError: if the month value is out of bounds. """ if month not in range(1, 13): raise ValueError('Month value out of bounds.') days_per_month = definitions.DAYS_PER_MONTH[month - 1] if month == 2 and (self._is_delta or self._IsLeapYear(year)): days_per_month += 1 return days_per_month @abc.abstractmethod def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ def _GetNumberOfDaysInCentury(self, year): """Retrieves the number of days in a century. Args: year (int): year in the century e.g. 1970. Returns: int: number of (remaining) days in the century. Raises: ValueError: if the year value is out of bounds. """ if year < 0: raise ValueError('Year value out of bounds.') year, _ = divmod(year, 100) year *= 100 number_of_days = definitions.DAYS_PER_CENTURY.get(year, None) if number_of_days is not None: return number_of_days if self._IsLeapYear(year): return 36525 return 36524 def _GetNumberOfDaysInYear(self, year): """Retrieves the number of days in a specific year. Args: year (int): year e.g. 1970. Returns: int: number of days in the year. """ number_of_days = definitions.DAYS_PER_YEAR.get(year, None) if number_of_days is not None: return number_of_days if self._IsLeapYear(year): return 366 return 365 def _GetNumberOfSecondsFromElements( self, year, month, day_of_month, hours, minutes, seconds): """Retrieves the number of seconds from the date and time elements. Args: year (int): year e.g. 1970. month (int): month, where 1 represents January. day_of_month (int): day of the month, where 1 represents the first day. hours (int): hours. minutes (int): minutes. seconds (int): seconds. Returns: int: number of seconds since January 1, 1970 00:00:00 or None if year, month or day of month are not set. Raises: ValueError: if the time elements are invalid. """ if not month or not day_of_month: return None if hours is None: hours = 0 elif hours not in range(0, 24): raise ValueError(f'Hours value: {hours!s} out of bounds.') if minutes is None: minutes = 0 elif minutes not in range(0, 60): raise ValueError(f'Minutes value: {minutes!s} out of bounds.') # TODO: support a leap second? if seconds is None: seconds = 0 elif seconds not in range(0, 60): raise ValueError(f'Seconds value: {seconds!s} out of bounds.') number_of_days = definitions.DAYS_PER_YEAR_IN_POSIX_EPOCH.get(year, None) if number_of_days is None: raise ValueError(f'Year value: {year!s} out of bounds.') number_of_days += sum( definitions.DAYS_PER_MONTH[index] for index in range(month - 1)) if month > 2 and self._IsLeapYear(year): number_of_days += 1 days_per_month = self._GetDaysPerMonth(year, month) if day_of_month < 1 or day_of_month > days_per_month: raise ValueError(f'Day of month value: {day_of_month:d} out of bounds.') number_of_days += day_of_month - 1 number_of_hours = (number_of_days * 24) + hours number_of_minutes = (number_of_hours * 60) + minutes number_of_seconds = (number_of_minutes * 60) + seconds return number_of_seconds def _GetTimeValues(self, number_of_seconds): """Determines time values. Args: number_of_seconds (int|decimal.Decimal): number of seconds. Returns: tuple[int, int, int, int]: days, hours, minutes, seconds. """ number_of_seconds = int(number_of_seconds) number_of_minutes, seconds = divmod(number_of_seconds, 60) number_of_hours, minutes = divmod(number_of_minutes, 60) number_of_days, hours = divmod(number_of_hours, 24) return number_of_days, hours, minutes, seconds def _IsLeapYear(self, year): """Determines if a year is a leap year. Args: year (int): year e.g. 1970. Returns: bool: True if the year is a leap year. """ # pylint: disable=consider-using-ternary return (year % 4 == 0 and year % 100 != 0) or year % 400 == 0 @abc.abstractmethod def CopyFromDateTimeString(self, time_string): """Copies a date time value from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Raises: ValueError: if the time string is invalid or not supported. """ def CopyToPosixTimestamp(self): """Copies the date time value to a POSIX timestamp. Returns: int: a POSIX timestamp in seconds or None if no timestamp is available. """ normalized_timestamp = self._GetNormalizedTimestamp() if normalized_timestamp is None: return None return int(normalized_timestamp) def CopyToPosixTimestampWithFractionOfSecond(self): """Copies the date time value to a POSIX timestamp with fraction of second. Returns: tuple[int, int]: a POSIX timestamp in seconds with fraction of second or None, None if no timestamp is available. """ normalized_timestamp = self._GetNormalizedTimestamp() if normalized_timestamp is None: return None, None remainder_multiplier = self._REMAINDER_MULTIPLIER.get(self._precision, None) if remainder_multiplier: remainder = int((normalized_timestamp % 1) * remainder_multiplier) else: remainder = None return int(normalized_timestamp), remainder @abc.abstractmethod def CopyToDateTimeString(self): """Copies the date time value to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.######" or None if the timestamp cannot be copied to a date and time string. """ def CopyToDateTimeStringISO8601(self): """Copies the date time value to an ISO 8601 date and time string. Returns: str: date and time value formatted as an ISO 8601 date and time string or None if the timestamp cannot be copied to a date and time string. """ date_time_string = self.CopyToDateTimeString() if date_time_string: date_time_string = date_time_string.replace(' ', 'T') if self._time_zone_offset is not None or not self.is_local_time: time_zone_offset_hours, time_zone_offset_minutes = divmod( self._time_zone_offset or 0, 60) if time_zone_offset_hours >= 0: time_zone_offset_sign = '+' else: time_zone_offset_sign = '-' time_zone_offset_hours *= -1 time_zone_string = ( f'{time_zone_offset_hours:02d}:{time_zone_offset_minutes:02d}') date_time_string = time_zone_offset_sign.join([ date_time_string, time_zone_string]) return date_time_string def GetDate(self): """Retrieves the date represented by the date and time values. Note that the date is adjusted to UTC. Returns: tuple[int, int, int]: year, month, day of month or (None, None, None) if the date and time values do not represent a date. """ year, month, day_of_month, _, _, _ = self._GetDateWithTimeOfDay() return year, month, day_of_month def GetDateWithTimeOfDay(self): """Retrieves the date with time of day. Note that the date and time are adjusted to UTC. Returns: tuple[int, int, int, int, int, int]: year, month, day of month, hours, minutes, seconds or (None, None, None, None, None, None) if the date and time values do not represent a date or time of day. """ return self._GetDateWithTimeOfDay() # TODO: remove this method when there is no more need for it in Plaso. def GetPlasoTimestamp(self): """Retrieves a timestamp that is compatible with Plaso. Returns: int: a POSIX timestamp in microseconds or None if no timestamp is available. Raises: ValueError: if the timestamp cannot be determined. """ normalized_timestamp = self._GetNormalizedTimestamp() if normalized_timestamp is None: return None normalized_timestamp *= definitions.MICROSECONDS_PER_SECOND try: normalized_timestamp = normalized_timestamp.quantize( 1, rounding=decimal.ROUND_HALF_UP) except decimal.InvalidOperation as exception: raise ValueError( f'Unable to round normalized timestamp with error: {exception!s}') return int(normalized_timestamp) def GetTimeOfDay(self): """Retrieves the time of day represented by the date and time values. Note that the time is adjusted to UTC. Returns: tuple[int, int, int]: hours, minutes, seconds or (None, None, None) if the date and time values do not represent a time of day. """ _, _, _, hours, minutes, seconds = self._GetDateWithTimeOfDay() return hours, minutes, seconds dfdatetime-20240504/dfdatetime/java_time.py000066400000000000000000000036711461534226200205150ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Java java.util.Date timestamp implementation.""" import decimal from dfdatetime import definitions from dfdatetime import factory from dfdatetime import posix_time class JavaTime(posix_time.PosixTimeInMilliseconds): """Java java.util.Date timestamp. The Java java.util.Date timestamp is a signed integer that contains the number of milliseconds since 1970-01-01 00:00:00 (also known as the POSIX epoch). Negative values represent date and times predating the POSIX epoch. Also see: https://docs.oracle.com/javase/8/docs/api/java/util/Date.html Attributes: is_local_time (bool): True if the date and time value is in local time. """ def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if (self._timestamp is not None and self._timestamp >= self._INT64_MIN and self._timestamp <= self._INT64_MAX): self._normalized_timestamp = ( decimal.Decimal(self._timestamp) / definitions.MILLISECONDS_PER_SECOND) if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def CopyToDateTimeString(self): """Copies the POSIX timestamp to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.######" or None if the timestamp is missing. """ if (self._timestamp is None or self._timestamp < self._INT64_MIN or self._timestamp > self._INT64_MAX): return None return super(JavaTime, self).CopyToDateTimeString() factory.Factory.RegisterDateTimeValues(JavaTime) dfdatetime-20240504/dfdatetime/ole_automation_date.py000066400000000000000000000117051461534226200225670ustar00rootroot00000000000000# -*- coding: utf-8 -*- """OLE automation date (or Floatingtime or Application time) implementation.""" import decimal from dfdatetime import definitions from dfdatetime import factory from dfdatetime import interface class OLEAutomationDateEpoch(interface.DateTimeEpoch): """OLE automation date epoch.""" def __init__(self): """Initializes a OLE automation date epoch.""" super(OLEAutomationDateEpoch, self).__init__(1899, 12, 30) class OLEAutomationDate(interface.DateTimeValues): """OLE Automation date. The OLE Automation date is a floating point value that contains the number of days since 1899-12-30 (also known as the OLE Automation date epoch), and the fractional part represents the fraction of a day since midnight. Negative values represent date and times predating the OLE Automation date epoch. Also see: https://learn.microsoft.com/en-us/dotnet/api/system.datetime.tooadate?view=net-8.0 Attributes: is_local_time (bool): True if the date and time value is in local time. """ _EPOCH = OLEAutomationDateEpoch() # The difference between December 30, 1899 and January 1, 1970 in days. _OLE_AUTOMATION_DATE_TO_POSIX_BASE = 25569 def __init__(self, precision=None, time_zone_offset=None, timestamp=None): """Initializes an OLE Automation date. Args: precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. timestamp (Optional[float]): OLE Automation date. """ super(OLEAutomationDate, self).__init__( precision=precision or definitions.PRECISION_1_MICROSECOND, time_zone_offset=time_zone_offset) self._timestamp = timestamp @property def timestamp(self): """float: OLE Automation date timestamp or None if not set.""" return self._timestamp def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if self._timestamp is not None: self._normalized_timestamp = ( decimal.Decimal(self._timestamp) - self._OLE_AUTOMATION_DATE_TO_POSIX_BASE) self._normalized_timestamp *= definitions.SECONDS_PER_DAY if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def CopyFromDateTimeString(self, time_string): """Copies an OLE Automation date from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Raises: ValueError: if the time string is invalid or not supported. """ date_time_values = self._CopyDateTimeFromString(time_string) year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) nanoseconds = date_time_values.get('nanoseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp = float(timestamp) timestamp += float(nanoseconds) / definitions.NANOSECONDS_PER_SECOND timestamp /= definitions.SECONDS_PER_DAY timestamp += self._OLE_AUTOMATION_DATE_TO_POSIX_BASE self._normalized_timestamp = None self._timestamp = timestamp self._time_zone_offset = time_zone_offset def CopyToDateTimeString(self): """Copies the OLE Automation date to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.######" or None if the timestamp is missing. """ if self._timestamp is None: return None timestamp = self._timestamp * definitions.SECONDS_PER_DAY number_of_days, hours, minutes, seconds = self._GetTimeValues( int(timestamp)) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) microseconds = int((timestamp % 1) * definitions.MICROSECONDS_PER_SECOND) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}.{microseconds:06d}') factory.Factory.RegisterDateTimeValues(OLEAutomationDate) dfdatetime-20240504/dfdatetime/posix_time.py000066400000000000000000000377711461534226200207460ustar00rootroot00000000000000# -*- coding: utf-8 -*- """POSIX timestamp implementation.""" import decimal from dfdatetime import definitions from dfdatetime import factory from dfdatetime import interface class PosixTimeEpoch(interface.DateTimeEpoch): """POSIX time epoch.""" def __init__(self): """Initializes a POSIX time epoch.""" super(PosixTimeEpoch, self).__init__(1970, 1, 1) class PosixTime(interface.DateTimeValues): """POSIX timestamp. The POSIX timestamp is a signed integer that contains the number of seconds since 1970-01-01 00:00:00 (also known as the POSIX epoch). Negative values represent date and times predating the POSIX epoch. The POSIX timestamp was initially 32-bit though 64-bit variants are known to be used. Attributes: is_local_time (bool): True if the date and time value is in local time. """ _EPOCH = PosixTimeEpoch() def __init__(self, precision=None, time_zone_offset=None, timestamp=None): """Initializes a POSIX timestamp. Args: precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. timestamp (Optional[int]): POSIX timestamp. """ super(PosixTime, self).__init__( precision=precision or definitions.PRECISION_1_SECOND, time_zone_offset=time_zone_offset) self._timestamp = timestamp @property def timestamp(self): """int: POSIX timestamp or None if not set.""" return self._timestamp def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if self._timestamp is not None: self._normalized_timestamp = decimal.Decimal(self._timestamp) if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def CopyFromDateTimeString(self, time_string): """Copies a POSIX timestamp from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. """ date_time_values = self._CopyDateTimeFromString(time_string) year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) self._timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) self._time_zone_offset = time_zone_offset def CopyToDateTimeString(self): """Copies the POSIX timestamp to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss" or None if the timestamp is missing. """ if self._timestamp is None: return None number_of_days, hours, minutes, seconds = self._GetTimeValues( self._timestamp) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}') class PosixTimeInMilliseconds(interface.DateTimeValues): """POSIX timestamp in milliseconds. Variant of the POSIX timestamp in milliseconds. Attributes: is_local_time (bool): True if the date and time value is in local time. """ _EPOCH = PosixTimeEpoch() def __init__(self, precision=None, time_zone_offset=None, timestamp=None): """Initializes a POSIX timestamp in milliseconds. Args: precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. timestamp (Optional[int]): POSIX timestamp in milliseconds. """ super(PosixTimeInMilliseconds, self).__init__( precision=precision or definitions.PRECISION_1_MILLISECOND, time_zone_offset=time_zone_offset) self._timestamp = timestamp @property def timestamp(self): """int: POSIX timestamp in milliseconds or None if not set.""" return self._timestamp def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if self._timestamp is not None: self._normalized_timestamp = ( decimal.Decimal(self._timestamp) / definitions.MILLISECONDS_PER_SECOND) if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def CopyFromDateTimeString(self, time_string): """Copies a POSIX timestamp from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. """ date_time_values = self._CopyDateTimeFromString(time_string) year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) nanoseconds = date_time_values.get('nanoseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) milliseconds, _ = divmod( nanoseconds, definitions.NANOSECONDS_PER_MILLISECOND) timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp *= definitions.MILLISECONDS_PER_SECOND timestamp += milliseconds self._timestamp = timestamp self._time_zone_offset = time_zone_offset def CopyToDateTimeString(self): """Copies the POSIX timestamp to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.######" or None if the timestamp is missing. """ if self._timestamp is None: return None timestamp, milliseconds = divmod( self._timestamp, definitions.MILLISECONDS_PER_SECOND) number_of_days, hours, minutes, seconds = self._GetTimeValues(timestamp) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}.{milliseconds:03d}') class PosixTimeInMicroseconds(interface.DateTimeValues): """POSIX timestamp in microseconds. Variant of the POSIX timestamp in microseconds. Attributes: is_local_time (bool): True if the date and time value is in local time. """ _EPOCH = PosixTimeEpoch() def __init__(self, precision=None, time_zone_offset=None, timestamp=None): """Initializes a POSIX timestamp in microseconds. Args: precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. timestamp (Optional[int]): POSIX timestamp in microseconds. """ super(PosixTimeInMicroseconds, self).__init__( precision=precision or definitions.PRECISION_1_MICROSECOND, time_zone_offset=time_zone_offset) self._timestamp = timestamp @property def timestamp(self): """int: POSIX timestamp in microseconds or None if not set.""" return self._timestamp def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if self._timestamp is not None: self._normalized_timestamp = ( decimal.Decimal(self._timestamp) / definitions.MICROSECONDS_PER_SECOND) if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def CopyFromDateTimeString(self, time_string): """Copies a POSIX timestamp from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. """ date_time_values = self._CopyDateTimeFromString(time_string) year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) nanoseconds = date_time_values.get('nanoseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) milliseconds, _ = divmod( nanoseconds, definitions.NANOSECONDS_PER_MICROSECOND) timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp *= definitions.MICROSECONDS_PER_SECOND timestamp += milliseconds self._timestamp = timestamp self._time_zone_offset = time_zone_offset def CopyToDateTimeString(self): """Copies the POSIX timestamp to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.######" or None if the timestamp is missing. """ if self._timestamp is None: return None timestamp, microseconds = divmod( self._timestamp, definitions.MICROSECONDS_PER_SECOND) number_of_days, hours, minutes, seconds = self._GetTimeValues(timestamp) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}.{microseconds:06d}') class PosixTimeInNanoseconds(interface.DateTimeValues): """POSIX timestamp in nanoseconds. Variant of the POSIX timestamp in nanoseconds. Attributes: is_local_time (bool): True if the date and time value is in local time. """ _EPOCH = PosixTimeEpoch() def __init__(self, precision=None, time_zone_offset=None, timestamp=None): """Initializes a POSIX timestamp in nanoseconds. Args: precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. timestamp (Optional[int]): POSIX timestamp in nanoseconds. """ super(PosixTimeInNanoseconds, self).__init__( precision=precision or definitions.PRECISION_1_NANOSECOND, time_zone_offset=time_zone_offset) self._timestamp = timestamp @property def timestamp(self): """int: POSIX timestamp or None if not set.""" return self._timestamp def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if self._timestamp is not None: self._normalized_timestamp = ( decimal.Decimal(self._timestamp) / definitions.NANOSECONDS_PER_SECOND) if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def _CopyFromDateTimeString(self, time_string): """Copies a POSIX timestamp from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. """ date_time_values = self._CopyDateTimeFromString(time_string) year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) nanoseconds = date_time_values.get('nanoseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp *= definitions.NANOSECONDS_PER_SECOND timestamp += nanoseconds self._normalized_timestamp = None self._timestamp = timestamp self._time_zone_offset = time_zone_offset def CopyFromDateTimeString(self, time_string): """Copies a POSIX timestamp from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. """ self._CopyFromDateTimeString(time_string) def _CopyToDateTimeString(self): """Copies the POSIX timestamp to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.#########" or None if the timestamp is missing or invalid. """ if self._timestamp is None: return None timestamp, nanoseconds = divmod( self._timestamp, definitions.NANOSECONDS_PER_SECOND) number_of_days, hours, minutes, seconds = self._GetTimeValues(timestamp) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}.{nanoseconds:09d}') def CopyToDateTimeString(self): """Copies the POSIX timestamp to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.#########" or None if the timestamp is missing or invalid. """ return self._CopyToDateTimeString() factory.Factory.RegisterDateTimeValues(PosixTime) factory.Factory.RegisterDateTimeValues(PosixTimeInMilliseconds) factory.Factory.RegisterDateTimeValues(PosixTimeInMicroseconds) factory.Factory.RegisterDateTimeValues(PosixTimeInNanoseconds) dfdatetime-20240504/dfdatetime/precisions.py000066400000000000000000000332211461534226200207260ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Date and time precision helpers.""" import decimal from dfdatetime import definitions class DateTimePrecisionHelper(object): """Date time precision helper interface. This is the super class of different date and time precision helpers. Time precision helpers provide functionality for converting date and time values between different precisions. """ # pylint: disable=missing-raises-doc,redundant-returns-doc @classmethod def CopyNanosecondsToFractionOfSecond(cls, nanoseconds): """Copies the number of nanoseconds to a fraction of second value. Args: nanoseconds (int): number of nanoseconds. Returns: decimal.Decimal: fraction of second, which must be a value between 0.0 and 1.0. """ raise NotImplementedError() @classmethod def CopyToDateTimeString(cls, time_elements_tuple, fraction_of_second): """Copies the time elements and fraction of second to a string. Args: time_elements_tuple (tuple[int, int, int, int, int, int]): time elements, contains year, month, day of month, hours, minutes and seconds. fraction_of_second (decimal.Decimal): fraction of second, which must be a value between 0.0 and 1.0. Returns: str: date and time value formatted as: YYYY-MM-DD hh:mm:ss with fraction of second part that corresponds to the precision. """ raise NotImplementedError() class SecondsPrecisionHelper(DateTimePrecisionHelper): """Seconds precision helper.""" @classmethod def CopyNanosecondsToFractionOfSecond(cls, nanoseconds): """Copies the number of nanoseconds to a fraction of second value. Args: nanoseconds (int): number of nanoseconds. Returns: decimal.Decimal: fraction of second, which must be a value between 0.0 and 1.0. For the seconds precision helper this will always be 0.0. Raises: ValueError: if the number of nanoseconds is out of bounds. """ if nanoseconds < 0 or nanoseconds >= definitions.NANOSECONDS_PER_SECOND: raise ValueError( f'Number of nanoseconds value: {nanoseconds:d} out of bounds.') return decimal.Decimal(0.0) @classmethod def CopyToDateTimeString(cls, time_elements_tuple, fraction_of_second): """Copies the time elements and fraction of second to a string. Args: time_elements_tuple (tuple[int, int, int, int, int, int]): time elements, contains year, month, day of month, hours, minutes and seconds. fraction_of_second (decimal.Decimal): fraction of second, which must be a value between 0.0 and 1.0. Returns: str: date and time value formatted as: YYYY-MM-DD hh:mm:ss Raises: ValueError: if the fraction of second is out of bounds. """ if fraction_of_second < 0.0 or fraction_of_second >= 1.0: raise ValueError( f'Fraction of second value: {fraction_of_second:f} out of bounds.') year, month, day_of_month, hours, minutes, seconds = time_elements_tuple return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}') class CentisecondsPrecisionHelper(DateTimePrecisionHelper): """Centiseconds (10 ms) precision helper.""" @classmethod def CopyNanosecondsToFractionOfSecond(cls, nanoseconds): """Copies the number of nanoseconds to a fraction of second value. Args: nanoseconds (int): number of nanoseconds. Returns: decimal.Decimal: fraction of second, which must be a value between 0.0 and 1.0. Raises: ValueError: if the number of nanoseconds is out of bounds. """ if nanoseconds < 0 or nanoseconds >= definitions.NANOSECONDS_PER_SECOND: raise ValueError( f'Number of nanoseconds value: {nanoseconds:d} out of bounds.') centiseconds, _ = divmod( nanoseconds, definitions.NANOSECONDS_PER_CENTISECOND) return decimal.Decimal(centiseconds) / definitions.CENTISECONDS_PER_SECOND @classmethod def CopyToDateTimeString(cls, time_elements_tuple, fraction_of_second): """Copies the time elements and fraction of second to a string. Args: time_elements_tuple (tuple[int, int, int, int, int, int]): time elements, contains year, month, day of month, hours, minutes and seconds. fraction_of_second (decimal.Decimal): fraction of second, which must be a value between 0.0 and 1.0. Returns: str: date and time value formatted as: YYYY-MM-DD hh:mm:ss.## Raises: ValueError: if the fraction of second is out of bounds. """ if fraction_of_second < 0.0 or fraction_of_second >= 1.0: raise ValueError( f'Fraction of second value: {fraction_of_second:f} out of bounds.') year, month, day_of_month, hours, minutes, seconds = time_elements_tuple centiseconds = int(fraction_of_second * definitions.CENTISECONDS_PER_SECOND) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}.{centiseconds:02d}') class MillisecondsPrecisionHelper(DateTimePrecisionHelper): """Milliseconds precision helper.""" @classmethod def CopyNanosecondsToFractionOfSecond(cls, nanoseconds): """Copies the number of nanoseconds to a fraction of second value. Args: nanoseconds (int): number of nanoseconds. Returns: decimal.Decimal: fraction of second, which must be a value between 0.0 and 1.0. Raises: ValueError: if the number of nanoseconds is out of bounds. """ if nanoseconds < 0 or nanoseconds >= definitions.NANOSECONDS_PER_SECOND: raise ValueError( f'Number of nanoseconds value: {nanoseconds:d} out of bounds.') milliseconds, _ = divmod( nanoseconds, definitions.NANOSECONDS_PER_MILLISECOND) return decimal.Decimal(milliseconds) / definitions.MILLISECONDS_PER_SECOND @classmethod def CopyToDateTimeString(cls, time_elements_tuple, fraction_of_second): """Copies the time elements and fraction of second to a string. Args: time_elements_tuple (tuple[int, int, int, int, int, int]): time elements, contains year, month, day of month, hours, minutes and seconds. fraction_of_second (decimal.Decimal): fraction of second, which must be a value between 0.0 and 1.0. Returns: str: date and time value formatted as: YYYY-MM-DD hh:mm:ss.### Raises: ValueError: if the fraction of second is out of bounds. """ if fraction_of_second < 0.0 or fraction_of_second >= 1.0: raise ValueError( f'Fraction of second value: {fraction_of_second:f} out of bounds.') year, month, day_of_month, hours, minutes, seconds = time_elements_tuple milliseconds = int(fraction_of_second * definitions.MILLISECONDS_PER_SECOND) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}.{milliseconds:03d}') class DecimillisecondsPrecisionHelper(DateTimePrecisionHelper): """Decimilliseconds (100 microseconds) precision helper.""" @classmethod def CopyNanosecondsToFractionOfSecond(cls, nanoseconds): """Copies the number of nanoseconds to a fraction of second value. Args: nanoseconds (int): number of nanoseconds. Returns: decimal.Decimal: fraction of second, which must be a value between 0.0 and 1.0. Raises: ValueError: if the number of nanoseconds is out of bounds. """ if nanoseconds < 0 or nanoseconds >= definitions.NANOSECONDS_PER_SECOND: raise ValueError( f'Number of nanoseconds value: {nanoseconds:d} out of bounds.') decimiliseconds, _ = divmod( nanoseconds, definitions.NANOSECONDS_PER_DECIMILISECOND) return ( decimal.Decimal(decimiliseconds) / definitions.DECIMICROSECONDS_PER_SECOND) @classmethod def CopyToDateTimeString(cls, time_elements_tuple, fraction_of_second): """Copies the time elements and fraction of second to a string. Args: time_elements_tuple (tuple[int, int, int, int, int, int]): time elements, contains year, month, day of month, hours, minutes and seconds. fraction_of_second (decimal.Decimal): fraction of second, which must be a value between 0.0 and 1.0. Returns: str: date and time value formatted as: YYYY-MM-DD hh:mm:ss.#### Raises: ValueError: if the fraction of second is out of bounds. """ if fraction_of_second < 0.0 or fraction_of_second >= 1.0: raise ValueError( f'Fraction of second value: {fraction_of_second:f} out of bounds.') year, month, day_of_month, hours, minutes, seconds = time_elements_tuple decimicroseconds = int( fraction_of_second * definitions.DECIMICROSECONDS_PER_SECOND) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}.{decimicroseconds:04d}') class MicrosecondsPrecisionHelper(DateTimePrecisionHelper): """Microseconds precision helper.""" @classmethod def CopyNanosecondsToFractionOfSecond(cls, nanoseconds): """Copies the number of nanoseconds to a fraction of second value. Args: nanoseconds (int): number of nanoseconds. Returns: decimal.Decimal: fraction of second, which must be a value between 0.0 and 1.0. Raises: ValueError: if the number of nanoseconds is out of bounds. """ if nanoseconds < 0 or nanoseconds >= definitions.NANOSECONDS_PER_SECOND: raise ValueError( f'Number of nanoseconds value: {nanoseconds:d} out of bounds.') microseconds, _ = divmod( nanoseconds, definitions.NANOSECONDS_PER_MICROSECOND) return decimal.Decimal(microseconds) / definitions.MICROSECONDS_PER_SECOND @classmethod def CopyToDateTimeString(cls, time_elements_tuple, fraction_of_second): """Copies the time elements and fraction of second to a string. Args: time_elements_tuple (tuple[int, int, int, int, int, int]): time elements, contains year, month, day of month, hours, minutes and seconds. fraction_of_second (decimal.Decimal): fraction of second, which must be a value between 0.0 and 1.0. Returns: str: date and time value formatted as: YYYY-MM-DD hh:mm:ss.###### Raises: ValueError: if the fraction of second is out of bounds. """ if fraction_of_second < 0.0 or fraction_of_second >= 1.0: raise ValueError( f'Fraction of second value: {fraction_of_second:f} out of bounds.') year, month, day_of_month, hours, minutes, seconds = time_elements_tuple microseconds = int(fraction_of_second * definitions.MICROSECONDS_PER_SECOND) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}.{microseconds:06d}') class NanosecondsPrecisionHelper(DateTimePrecisionHelper): """Nanoseconds precision helper.""" @classmethod def CopyNanosecondsToFractionOfSecond(cls, nanoseconds): """Copies the number of nanoseconds to a fraction of second value. Args: nanoseconds (int): number of nanoseconds. Returns: decimal.Decimal: fraction of second, which must be a value between 0.0 and 1.0. Raises: ValueError: if the number of nanoseconds is out of bounds. """ if nanoseconds < 0 or nanoseconds >= definitions.NANOSECONDS_PER_SECOND: raise ValueError( f'Number of nanoseconds value: {nanoseconds:d} out of bounds.') return decimal.Decimal(nanoseconds) / definitions.NANOSECONDS_PER_SECOND @classmethod def CopyToDateTimeString(cls, time_elements_tuple, fraction_of_second): """Copies the time elements and fraction of second to a string. Args: time_elements_tuple (tuple[int, int, int, int, int, int]): time elements, contains year, month, day of month, hours, minutes and seconds. fraction_of_second (decimal.Decimal): fraction of second, which must be a value between 0.0 and 1.0. Returns: str: date and time value formatted as: YYYY-MM-DD hh:mm:ss.###### Raises: ValueError: if the fraction of second is out of bounds. """ if fraction_of_second < 0.0 or fraction_of_second >= 1.0: raise ValueError( f'Fraction of second value: {fraction_of_second:f} out of bounds.') year, month, day_of_month, hours, minutes, seconds = time_elements_tuple nanoseconds = int(fraction_of_second * definitions.NANOSECONDS_PER_SECOND) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}.{nanoseconds:09d}') class PrecisionHelperFactory(object): """Date time precision helper factory.""" _PRECISION_CLASSES = { definitions.PRECISION_10_MILLISECONDS: CentisecondsPrecisionHelper, definitions.PRECISION_100_MICROSECONDS: DecimillisecondsPrecisionHelper, definitions.PRECISION_1_MICROSECOND: MicrosecondsPrecisionHelper, definitions.PRECISION_1_MILLISECOND: MillisecondsPrecisionHelper, definitions.PRECISION_1_NANOSECOND: NanosecondsPrecisionHelper, definitions.PRECISION_1_SECOND: SecondsPrecisionHelper} @classmethod def CreatePrecisionHelper(cls, precision): """Creates a precision helper. Args: precision (str): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. Returns: class: date time precision helper class. Raises: ValueError: if the precision value is unsupported. """ precision_helper_class = cls._PRECISION_CLASSES.get(precision, None) if not precision_helper_class: raise ValueError(f'Unsupported precision: {precision!s}') return precision_helper_class dfdatetime-20240504/dfdatetime/rfc2579_date_time.py000066400000000000000000000177551461534226200217020ustar00rootroot00000000000000# -*- coding: utf-8 -*- """RFC2579 date-time implementation.""" import decimal from dfdatetime import definitions from dfdatetime import factory from dfdatetime import interface class RFC2579DateTime(interface.DateTimeValues): """RFC2579 date-time. The RFC2579 date-time structure is 11 bytes of size and contains: struct { uin16_t year, uint8_t month, uint8_t day_of_month, uint8_t hours, uint8_t minutes, uint8_t seconds, uint8_t deciseconds, char direction_from_utc, uint8_t hours_from_utc, uint8_t minutes_from_utc } Also see: https://datatracker.ietf.org/doc/html/rfc2579 Attributes: year (int): year, 0 through 65536. month (int): month of year, 1 through 12. day_of_month (int): day of month, 1 through 31. hours (int): hours, 0 through 23. minutes (int): minutes, 0 through 59. seconds (int): seconds, 0 through 59, where 60 is used to represent a leap-second. deciseconds (int): deciseconds, 0 through 9. """ # TODO: make attributes read-only. # pylint: disable=missing-type-doc def __init__(self, precision=None, rfc2579_date_time_tuple=None): """Initializes a RFC2579 date-time. Args: precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. rfc2579_date_time_tuple: (Optional[tuple[int, int, int, int, int, int, int, str, int, int]]): RFC2579 date-time time, contains year, month, day of month, hours, minutes, seconds and deciseconds, and time zone offset in hours and minutes from UTC. Raises: ValueError: if the system time is invalid. """ super(RFC2579DateTime, self).__init__( precision=precision or definitions.PRECISION_100_MILLISECONDS) self._day_of_month = None self._deciseconds = None self._hours = None self._minutes = None self._month = None self._number_of_seconds = None self._seconds = None self._year = None if rfc2579_date_time_tuple: if len(rfc2579_date_time_tuple) < 10: raise ValueError( 'Invalid RFC2579 date-time tuple 10 elements required.') if rfc2579_date_time_tuple[0] < 0 or rfc2579_date_time_tuple[0] > 65536: raise ValueError('Year value out of bounds.') if rfc2579_date_time_tuple[1] not in range(1, 13): raise ValueError('Month value out of bounds.') days_per_month = self._GetDaysPerMonth( rfc2579_date_time_tuple[0], rfc2579_date_time_tuple[1]) if (rfc2579_date_time_tuple[2] < 1 or rfc2579_date_time_tuple[2] > days_per_month): raise ValueError('Day of month value out of bounds.') if rfc2579_date_time_tuple[3] not in range(0, 24): raise ValueError('Hours value out of bounds.') if rfc2579_date_time_tuple[4] not in range(0, 60): raise ValueError('Minutes value out of bounds.') # TODO: support a leap second? if rfc2579_date_time_tuple[5] not in range(0, 60): raise ValueError('Seconds value out of bounds.') if rfc2579_date_time_tuple[6] < 0 or rfc2579_date_time_tuple[6] > 9: raise ValueError('Deciseconds value out of bounds.') if rfc2579_date_time_tuple[7] not in ('+', '-'): raise ValueError('Direction from UTC value out of bounds.') if rfc2579_date_time_tuple[8] not in range(0, 14): raise ValueError('Hours from UTC value out of bounds.') if rfc2579_date_time_tuple[9] not in range(0, 60): raise ValueError('Minutes from UTC value out of bounds.') time_zone_offset = ( (rfc2579_date_time_tuple[8] * 60) + rfc2579_date_time_tuple[9]) if rfc2579_date_time_tuple[7] == '-': time_zone_offset = -time_zone_offset self._time_zone_offset = time_zone_offset self._year = rfc2579_date_time_tuple[0] self._month = rfc2579_date_time_tuple[1] self._day_of_month = rfc2579_date_time_tuple[2] self._hours = rfc2579_date_time_tuple[3] self._minutes = rfc2579_date_time_tuple[4] self._seconds = rfc2579_date_time_tuple[5] self._deciseconds = rfc2579_date_time_tuple[6] self._number_of_seconds = self._GetNumberOfSecondsFromElements( self._year, self._month, self._day_of_month, self._hours, self._minutes, self._seconds) def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if self._number_of_seconds is not None: self._normalized_timestamp = ( decimal.Decimal(self._deciseconds) / definitions.DECISECONDS_PER_SECOND) self._normalized_timestamp += decimal.Decimal(self._number_of_seconds) if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp @property def deciseconds(self): """int: number of deciseconds or None if not set.""" return self._deciseconds @property def day_of_month(self): """int: day of month or None if not set.""" return self._day_of_month @property def hours(self): """int: number of hours or None if not set.""" return self._hours @property def minutes(self): """int: number of minutes or None if not set.""" return self._minutes @property def month(self): """int: month or None if not set.""" return self._month @property def seconds(self): """int: number of seconds or None if not set.""" return self._seconds @property def year(self): """int: year or None if not set.""" return self._year def CopyFromDateTimeString(self, time_string): """Copies a RFC2579 date-time from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Raises: ValueError: if the date string is invalid or not supported. """ date_time_values = self._CopyDateTimeFromString(time_string) year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) nanoseconds = date_time_values.get('nanoseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) deciseconds, _ = divmod( nanoseconds, definitions.NANOSECONDS_PER_DECISECOND) if year < 0 or year > 65536: raise ValueError(f'Unsupported year value: {year:d}.') self._normalized_timestamp = None self._number_of_seconds = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) self._time_zone_offset = time_zone_offset self._year = year self._month = month self._day_of_month = day_of_month self._hours = hours self._minutes = minutes self._seconds = seconds self._deciseconds = deciseconds def CopyToDateTimeString(self): """Copies the RFC2579 date-time to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.#" or None if the number of seconds is missing. """ if self._number_of_seconds is None: return None return (f'{self._year:04d}-{self._month:02d}-{self._day_of_month:02d} ' f'{self._hours:02d}:{self._minutes:02d}:{self._seconds:02d}' f'.{self._deciseconds:01d}') factory.Factory.RegisterDateTimeValues(RFC2579DateTime) dfdatetime-20240504/dfdatetime/semantic_time.py000066400000000000000000000215671461534226200214030ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Semantic time implementation.""" from dfdatetime import factory from dfdatetime import interface class SemanticTime(interface.DateTimeValues): """Semantic time. Semantic time is term to describe date and time values that have specific meaning such as: "Never", "Yesterday", "Not set". Attributes: is_local_time (bool): True if the date and time value is in local time. """ # pylint: disable=redundant-returns-doc _SORT_ORDER = 50 def __init__(self, string=None): """Initializes a semantic time. Args: string (str): semantic representation of the time, such as: "Never", "Not set". """ super(SemanticTime, self).__init__() self._string = string @property def string(self): """str: semantic representation of the time, such as: "Never".""" return self._string def __eq__(self, other): """Determines if the date time values are equal to other. Args: other (DateTimeValues): date time values to compare against. Returns: bool: True if the date time values are equal to other. """ if not isinstance(other, SemanticTime): return False return self._SORT_ORDER == other._SORT_ORDER # pylint: disable=protected-access def __ge__(self, other): """Determines if the date time values are greater than or equal to other. Args: other (DateTimeValues): date time values to compare against. Returns: bool: True if the date time values are greater than or equal to other. Raises: ValueError: if other is not an instance of DateTimeValues. """ if not isinstance(other, interface.DateTimeValues): raise ValueError('Other not an instance of DateTimeValues') if not isinstance(other, SemanticTime): return False return self._SORT_ORDER >= other._SORT_ORDER # pylint: disable=protected-access def __gt__(self, other): """Determines if the date time values are greater than other. Args: other (DateTimeValues): date time values to compare against. Returns: bool: True if the date time values are greater than other. Raises: ValueError: if other is not an instance of DateTimeValues. """ if not isinstance(other, interface.DateTimeValues): raise ValueError('Other not an instance of DateTimeValues') if not isinstance(other, SemanticTime): return False return self._SORT_ORDER > other._SORT_ORDER # pylint: disable=protected-access def __le__(self, other): """Determines if the date time values are greater than or equal to other. Args: other (DateTimeValues): date time values to compare against. Returns: bool: True if the date time values are greater than or equal to other. Raises: ValueError: if other is not an instance of DateTimeValues. """ if not isinstance(other, interface.DateTimeValues): raise ValueError('Other not an instance of DateTimeValues') if not isinstance(other, SemanticTime): return True return self._SORT_ORDER <= other._SORT_ORDER # pylint: disable=protected-access def __lt__(self, other): """Determines if the date time values are less than other. Args: other (DateTimeValues): date time values to compare against. Returns: bool: True if the date time values are less than other. Raises: ValueError: if other is not an instance of DateTimeValues. """ if not isinstance(other, interface.DateTimeValues): raise ValueError('Other not an instance of DateTimeValues') if not isinstance(other, SemanticTime): return True return self._SORT_ORDER < other._SORT_ORDER # pylint: disable=protected-access def __ne__(self, other): """Determines if the date time values are not equal to other. Args: other (DateTimeValues): date time values to compare against. Returns: bool: True if the date time values are not equal to other. """ if not isinstance(other, SemanticTime): return True return self._SORT_ORDER != other._SORT_ORDER # pylint: disable=protected-access def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ return None def CopyFromDateTimeString(self, time_string): """Copies semantic time from a date and time string. Args: time_string (str): semantic representation of the time, such as: "Never", "Not set". Raises: ValueError: because semantic time cannot be copied from a string. """ self._string = time_string def CopyToDateTimeString(self): """Copies the date time value to a date and time string. Returns: str: semantic representation of the time, such as: "Never", "Not set". """ return self._string def CopyToDateTimeStringISO8601(self): """Copies the date time value to an ISO 8601 date and time string. Returns: str: date and time value formatted as an ISO 8601 date and time string, which always be None since semantic time cannot be represented in ISO 8601. """ return None def GetPlasoTimestamp(self): """Retrieves a timestamp that is compatible with plaso. Returns: int: a POSIX timestamp in microseconds, which will always be 0. """ return 0 class InvalidTime(SemanticTime): """Semantic time that represents invalid.""" _SORT_ORDER = 1 def __init__(self): """Initializes a semantic time that represents invalid.""" super(InvalidTime, self).__init__(string='Invalid') class Never(SemanticTime): """Semantic time that represents never.""" _SORT_ORDER = 99 def __init__(self): """Initializes a semantic time that represents never.""" super(Never, self).__init__(string='Never') def __eq__(self, other): """Determines if the date time values are equal to other. Args: other (DateTimeValues): date time values to compare against. Returns: bool: True if the date time values are equal to other. """ return isinstance(other, Never) def __ge__(self, other): """Determines if the date time values are greater than or equal to other. Args: other (DateTimeValues): date time values to compare against. Returns: bool: True if the date time values are greater than or equal to other. Raises: ValueError: if other is not an instance of DateTimeValues. """ if not isinstance(other, interface.DateTimeValues): raise ValueError('Other not an instance of DateTimeValues') return True def __gt__(self, other): """Determines if the date time values are greater than other. Args: other (DateTimeValues): date time values to compare against. Returns: bool: True if the date time values are greater than other. Raises: ValueError: if other is not an instance of DateTimeValues. """ if not isinstance(other, interface.DateTimeValues): raise ValueError('Other not an instance of DateTimeValues') return not isinstance(other, Never) def __le__(self, other): """Determines if the date time values are less than or equal to other. Args: other (DateTimeValues): date time values to compare against. Returns: bool: True if the date time values are greater than or equal to other. Raises: ValueError: if other is not an instance of DateTimeValues. """ if not isinstance(other, interface.DateTimeValues): raise ValueError('Other not an instance of DateTimeValues') return isinstance(other, Never) def __lt__(self, other): """Determines if the date time values are less than other. Args: other (DateTimeValues): date time values to compare against. Returns: bool: True if the date time values are less than other. Raises: ValueError: if other is not an instance of DateTimeValues. """ if not isinstance(other, interface.DateTimeValues): raise ValueError('Other not an instance of DateTimeValues') return False def __ne__(self, other): """Determines if the date time values are not equal to other. Args: other (DateTimeValues): date time values to compare against. Returns: bool: True if the date time values are not equal to other. """ return not isinstance(other, Never) class NotSet(SemanticTime): """Semantic time that represents not set.""" _SORT_ORDER = 2 def __init__(self): """Initializes a semantic time that represents not set.""" super(NotSet, self).__init__(string='Not set') factory.Factory.RegisterDateTimeValues(SemanticTime) factory.Factory.RegisterDateTimeValues(InvalidTime) factory.Factory.RegisterDateTimeValues(Never) factory.Factory.RegisterDateTimeValues(NotSet) dfdatetime-20240504/dfdatetime/serializer.py000066400000000000000000000166371461534226200207350ustar00rootroot00000000000000# -*- coding: utf-8 -*- """The date and time values serializer.""" from dfdatetime import factory from dfdatetime import interface class Serializer(object): """Date and time values serializer.""" @classmethod def ConvertDictToDateTimeValues(cls, json_dict): """Converts a JSON dict into a date time values object. This method is deprecated use ConvertJSONToDateTimeValues instead. The dictionary of the JSON serialized objects consists of: { '__type__': 'DateTimeValues' '__class_name__': 'RFC2579DateTime' ... } Here '__type__' indicates the object base type. In this case this should be 'DateTimeValues'. The rest of the elements of the dictionary make up the date time values object properties. Args: json_dict (dict[str, object]): JSON serialized objects. Returns: dfdatetime.DateTimeValues: date and time values. """ return cls.ConvertJSONToDateTimeValues(json_dict) @classmethod def ConvertDateTimeValuesToDict(cls, date_time_values): """Converts a date and time values object into a JSON dictionary. This method is deprecated use ConvertDateTimeValuesToJSON instead. The resulting dictionary of the JSON serialized objects consists of: { '__type__': 'DateTimeValues' '__class_name__': 'RFC2579DateTime' ... } Here '__type__' indicates the object base type. In this case 'DateTimeValues'. The rest of the elements of the dictionary make up the date and time value object properties. Args: date_time_values (dfdatetime.DateTimeValues): date and time values. Returns: dict[str, object]: JSON serialized objects. Raises: TypeError: if object is not an instance of DateTimeValues. """ if not isinstance(date_time_values, interface.DateTimeValues): raise TypeError return cls.ConvertDateTimeValuesToJSON(date_time_values) @classmethod def ConvertDateTimeValuesToJSON(cls, date_time_values): """Converts a date and time values object into a JSON dictionary. The resulting dictionary of the JSON serialized objects consists of: { '__type__': 'DateTimeValues' '__class_name__': 'RFC2579DateTime' ... } Here '__type__' indicates the object base type. In this case 'DateTimeValues'. The rest of the elements of the dictionary make up the date and time value object properties. Args: date_time_values (dfdatetime.DateTimeValues): date and time values. Returns: dict[str, object]: JSON serialized objects. """ class_name = type(date_time_values).__name__ json_dict = { '__class_name__': class_name, '__type__': 'DateTimeValues'} if hasattr(date_time_values, 'timestamp'): json_dict['timestamp'] = date_time_values.timestamp elif hasattr(date_time_values, 'string'): json_dict['string'] = date_time_values.string elif class_name == 'FATDateTime': json_dict['fat_date_time'] = date_time_values.fat_date_time elif class_name == 'GolangTime': json_dict['golang_timestamp'] = date_time_values.golang_timestamp elif class_name == 'RFC2579DateTime': time_zone_hours, time_zone_minutes = divmod( date_time_values.time_zone_offset, 60) if date_time_values.time_zone_offset < 0: time_zone_sign = '-' time_zone_hours *= -1 else: time_zone_sign = '+' json_dict['rfc2579_date_time_tuple'] = ( date_time_values.year, date_time_values.month, date_time_values.day_of_month, date_time_values.hours, date_time_values.minutes, date_time_values.seconds, date_time_values.deciseconds, time_zone_sign, time_zone_hours, time_zone_minutes) elif class_name == 'Systemtime': json_dict['system_time_tuple'] = ( date_time_values.year, date_time_values.month, date_time_values.day_of_week, date_time_values.day_of_month, date_time_values.hours, date_time_values.minutes, date_time_values.seconds, date_time_values.milliseconds) elif class_name == 'TimeElements': json_dict['time_elements_tuple'] = ( date_time_values.year, date_time_values.month, date_time_values.day_of_month, date_time_values.hours, date_time_values.minutes, date_time_values.seconds) elif class_name == 'TimeElementsInMilliseconds': json_dict['time_elements_tuple'] = ( date_time_values.year, date_time_values.month, date_time_values.day_of_month, date_time_values.hours, date_time_values.minutes, date_time_values.seconds, date_time_values.milliseconds) elif class_name == 'TimeElementsInMicroseconds': json_dict['time_elements_tuple'] = ( date_time_values.year, date_time_values.month, date_time_values.day_of_month, date_time_values.hours, date_time_values.minutes, date_time_values.seconds, date_time_values.microseconds) if date_time_values.time_zone_offset is not None and class_name not in ( 'GolangTime', 'RFC2579DateTime'): json_dict['time_zone_offset'] = date_time_values.time_zone_offset if date_time_values.is_delta and class_name in ( 'TimeElements', 'TimeElementsInMilliseconds', 'TimeElementsInMicroseconds'): json_dict['is_delta'] = True if date_time_values.is_local_time: json_dict['is_local_time'] = True if date_time_values.time_zone_hint: json_dict['time_zone_hint'] = date_time_values.time_zone_hint return json_dict @classmethod def ConvertJSONToDateTimeValues(cls, json_dict): """Converts a JSON dict into a date time values object. The dictionary of the JSON serialized objects consists of: { '__type__': 'DateTimeValues' '__class_name__': 'RFC2579DateTime' ... } Here '__type__' indicates the object base type. In this case this should be 'DateTimeValues'. The rest of the elements of the dictionary make up the date time values object properties. Args: json_dict (dict[str, object]): JSON serialized objects. Returns: dfdatetime.DateTimeValues: date and time values. """ class_name = json_dict.get('__class_name__', None) if class_name: del json_dict['__class_name__'] # Remove the class type from the JSON dict since we cannot pass it. del json_dict['__type__'] if class_name not in ( 'TimeElements', 'TimeElementsInMilliseconds', 'TimeElementsInMicroseconds'): is_delta = json_dict.get('is_delta', None) if is_delta is not None: del json_dict['is_delta'] is_local_time = json_dict.get('is_local_time', None) if is_local_time is not None: del json_dict['is_local_time'] time_zone_hint = json_dict.get('time_zone_hint', None) if time_zone_hint is not None: del json_dict['time_zone_hint'] if class_name in ('InvalidTime', 'Never', 'NotSet'): string = json_dict.get('string', None) if string is not None: del json_dict['string'] if class_name in ('GolangTime', 'RFC2579DateTime'): time_zone_offset = json_dict.get('time_zone_offset', None) if time_zone_offset is not None: del json_dict['time_zone_offset'] date_time = factory.Factory.NewDateTimeValues(class_name, **json_dict) if is_local_time: date_time.is_local_time = is_local_time if time_zone_hint: date_time.time_zone_hint = time_zone_hint return date_time dfdatetime-20240504/dfdatetime/systemtime.py000066400000000000000000000162211461534226200207540ustar00rootroot00000000000000# -*- coding: utf-8 -*- """SYSTEMTIME structure implementation.""" import decimal from dfdatetime import definitions from dfdatetime import factory from dfdatetime import interface class Systemtime(interface.DateTimeValues): """SYSTEMTIME structure. The SYSTEMTIME structure is 16 bytes of size and contains: struct { WORD year, WORD month, WORD day_of_week, WORD day_of_month, WORD hour, WORD minute, WORD second, WORD millisecond } """ def __init__( self, precision=None, system_time_tuple=None, time_zone_offset=None): """Initializes a SYSTEMTIME structure. Args: precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. system_time_tuple (Optional[tuple[int, int, int, int, int, int, int, int]]): system time, contains year, month, day of week, day of month, hours, minutes, seconds and milliseconds. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. Raises: ValueError: if the system time is invalid. """ super(Systemtime, self).__init__( precision=precision or definitions.PRECISION_1_MILLISECOND, time_zone_offset=time_zone_offset) self._number_of_seconds = None self._day_of_month = None self._day_of_week = None self._hours = None self._milliseconds = None self._minutes = None self._month = None self._seconds = None self._year = None if system_time_tuple: if len(system_time_tuple) < 8: raise ValueError('Invalid system time tuple 8 elements required.') if system_time_tuple[0] < 1601 or system_time_tuple[0] > 30827: raise ValueError('Year value out of bounds.') if system_time_tuple[1] not in range(1, 13): raise ValueError('Month value out of bounds.') if system_time_tuple[2] not in range(0, 7): raise ValueError('Day of week value out of bounds.') days_per_month = self._GetDaysPerMonth( system_time_tuple[0], system_time_tuple[1]) if system_time_tuple[3] < 1 or system_time_tuple[3] > days_per_month: raise ValueError('Day of month value out of bounds.') if system_time_tuple[4] not in range(0, 24): raise ValueError('Hours value out of bounds.') if system_time_tuple[5] not in range(0, 60): raise ValueError('Minutes value out of bounds.') # TODO: support a leap second? if system_time_tuple[6] not in range(0, 60): raise ValueError('Seconds value out of bounds.') if system_time_tuple[7] < 0 or system_time_tuple[7] > 999: raise ValueError('Milliseconds value out of bounds.') self._day_of_month = system_time_tuple[3] self._day_of_week = system_time_tuple[2] self._hours = system_time_tuple[4] self._milliseconds = system_time_tuple[7] self._minutes = system_time_tuple[5] self._month = system_time_tuple[1] self._seconds = system_time_tuple[6] self._year = system_time_tuple[0] self._number_of_seconds = self._GetNumberOfSecondsFromElements( self._year, self._month, self._day_of_month, self._hours, self._minutes, self._seconds) @property def day_of_month(self): """day_of_month (int): day of month, 1 through 31.""" return self._day_of_month @property def day_of_week(self): """day_of_week (int): day of week, 0 through 6.""" return self._day_of_week @property def hours(self): """Hours (int): hours, 0 through 23.""" return self._hours @property def milliseconds(self): """Milliseconds (int): milliseconds, 0 through 999.""" return self._milliseconds @property def minutes(self): """Minutes (int): minutes, 0 through 59.""" return self._minutes @property def month(self): """Month (int): month of year, 1 through 12.""" return self._month @property def seconds(self): """Seconds (int): seconds, 0 through 59.""" return self._seconds @property def year(self): """Year (int): year, 1601 through 30827.""" return self._year def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if self._number_of_seconds is not None: self._normalized_timestamp = ( decimal.Decimal(self._milliseconds) / definitions.MILLISECONDS_PER_SECOND) self._normalized_timestamp += decimal.Decimal(self._number_of_seconds) if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def CopyFromDateTimeString(self, time_string): """Copies a SYSTEMTIME structure from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Raises: ValueError: if the date string is invalid or not supported. """ date_time_values = self._CopyDateTimeFromString(time_string) year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) nanoseconds = date_time_values.get('nanoseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) milliseconds, _ = divmod( nanoseconds, definitions.NANOSECONDS_PER_MILLISECOND) if year < 1601 or year > 30827: raise ValueError(f'Unsupported year value: {year:d}.') self._normalized_timestamp = None self._number_of_seconds = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) self._time_zone_offset = time_zone_offset self._year = year self._month = month self._day_of_month = day_of_month # TODO: calculate day of week on demand. self._day_of_week = None self._hours = hours self._minutes = minutes self._seconds = seconds self._milliseconds = milliseconds def CopyToDateTimeString(self): """Copies the SYSTEMTIME structure to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.###" or None if the number of seconds is missing. """ if self._number_of_seconds is None: return None return (f'{self._year:04d}-{self._month:02d}-{self._day_of_month:02d} ' f'{self._hours:02d}:{self._minutes:02d}:{self._seconds:02d}' f'.{self._milliseconds:03d}') factory.Factory.RegisterDateTimeValues(Systemtime) dfdatetime-20240504/dfdatetime/time_elements.py000066400000000000000000001451451461534226200214130ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Time elements implementation.""" import decimal from dfdatetime import definitions from dfdatetime import factory from dfdatetime import interface from dfdatetime import precisions class TimeElements(interface.DateTimeValues): """Time elements. Time elements contain separate values for year, month, day of month, hours, minutes and seconds. Attributes: is_local_time (bool): True if the date and time value is in local time. """ # Maps the RFC 822, RFC 1123 and RFC 2822 definitions to their corresponding # integer values. _RFC_MONTH_MAPPINGS = { 'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'Jun': 6, 'Jul': 7, 'Aug': 8, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12} _RFC_TIME_ZONE_MAPPINGS = { 'UT': 0, 'GMT': 0, 'EST': -5, 'EDT': -4, 'CST': -6, 'CDT': -5, 'MST': -7, 'MDT': -6, 'PST': -8, 'PDT': -7, 'A': -1, 'B': -2, 'C': -3, 'D': -4, 'E': -5, 'F': -6, 'G': -7, 'H': -8, 'I': -9, 'K': -10, 'L': -11, 'M': -12, 'N': 1, 'O': 2, 'P': 3, 'Q': 4, 'R': 5, 'S': 6, 'T': 7, 'U': 8, 'V': 9, 'W': 10, 'X': 11, 'Y': 12, 'Z': 0} _RFC_WEEKDAYS = frozenset(['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']) def __init__( self, is_delta=False, precision=None, time_elements_tuple=None, time_zone_offset=None): """Initializes time elements. Args: is_delta (Optional[bool]): True if the date and time value is relative to another date and time value. precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_elements_tuple (Optional[tuple[int, int, int, int, int, int]]): time elements, contains year, month, day of month, hours, minutes and seconds. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. Raises: ValueError: if the time elements tuple is invalid. """ super(TimeElements, self).__init__( is_delta=is_delta, precision=precision or definitions.PRECISION_1_SECOND, time_zone_offset=time_zone_offset) self._number_of_seconds = None self._time_elements_tuple = time_elements_tuple if time_elements_tuple: number_of_elements = len(time_elements_tuple) if number_of_elements < 6: raise ValueError(( f'Invalid time elements tuple at least 6 elements required,' f'got: {number_of_elements:d}')) self._number_of_seconds = self._GetNumberOfSecondsFromElements( time_elements_tuple[0], time_elements_tuple[1], time_elements_tuple[2], time_elements_tuple[3], time_elements_tuple[4], time_elements_tuple[5]) def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if self._number_of_seconds is not None: self._normalized_timestamp = decimal.Decimal(self._number_of_seconds) if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def _CopyDateTimeFromStringISO8601(self, time_string): """Copies a date and time from an ISO 8601 date and time string. Args: time_string (str): time value formatted as: hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The fraction of second and time zone offset are optional. Returns: dict[str, int]: date and time values, such as year, month, day of month, hours, minutes, seconds, nanoseconds, time zone offset in minutes. Raises: ValueError: if the time string is invalid or not supported. """ if not time_string: raise ValueError('Invalid time string.') time_string_length = len(time_string) year, month, day_of_month = self._CopyDateFromString(time_string) if time_string_length <= 10: return { 'year': year, 'month': month, 'day_of_month': day_of_month} # If a time of day is specified the time string it should at least # contain 'YYYY-MM-DDThh'. if time_string[10] != 'T': raise ValueError('Invalid time string - missing date and time separator.') hours, minutes, seconds, nanoseconds, time_zone_offset = ( self._CopyTimeFromStringISO8601(time_string[11:])) date_time_values = { 'year': year, 'month': month, 'day_of_month': day_of_month, 'hours': hours, 'minutes': minutes, 'seconds': seconds} if nanoseconds is not None: date_time_values['nanoseconds'] = nanoseconds if time_zone_offset is not None: date_time_values['time_zone_offset'] = time_zone_offset return date_time_values def _CopyDateTimeFromStringRFC822(self, time_string): """Copies a date and time from a RFC 822 date and time string. Args: time_string (str): date and time value formatted as: DAY, D MONTH YY hh:mm:ss ZONE Where weekday (DAY) and seconds (ss) are optional and day of month (D) can consist of 1 or 2 digits. Returns: dict[str, int]: date and time values, such as year, month, day of month, hours, minutes, seconds, time zone offset in minutes. Raises: ValueError: if the time string is invalid or not supported. """ if not time_string: raise ValueError('Invalid time string.') string_segments = time_string.split(' ') if len(string_segments) not in (5, 6): raise ValueError('Unsupported number of time string segments.') weekday_string = string_segments[0] if weekday_string.endswith(','): weekday_string = weekday_string[:-1] if weekday_string not in self._RFC_WEEKDAYS: raise ValueError(f'Invalid weekday: {weekday_string:s}.') string_segments.pop(0) day_of_month_string = string_segments[0] day_of_month = 0 if len(day_of_month_string) in (1, 2): try: day_of_month = int(day_of_month_string, 10) except ValueError: pass if day_of_month == 0: raise ValueError(f'Invalid day of month: {day_of_month_string:s}.') month_string = string_segments[1] month = self._RFC_MONTH_MAPPINGS.get(month_string) if not month: raise ValueError(f'Invalid month: {month_string:s}.') year_string = string_segments[2] year = None if len(year_string) == 2: try: year = int(year_string, 10) except ValueError: pass if year is None: raise ValueError(f'Invalid year: {0:s}.') year += 1900 hours, minutes, seconds, time_zone_offset = self._CopyTimeFromStringRFC( string_segments[3], string_segments[4]) date_time_values = { 'year': year, 'month': month, 'day_of_month': day_of_month, 'hours': hours, 'minutes': minutes, 'time_zone_offset': time_zone_offset} if seconds is not None: date_time_values['seconds'] = seconds return date_time_values def _CopyDateTimeFromStringRFC1123(self, time_string): """Copies a date and time from a RFC 1123 date and time string. Args: time_string (str): date and time value formatted as: DAY, D MONTH YYYY hh:mm:ss ZONE Where weekday (DAY) and seconds (ss) are optional and day of month (D) can consist of 1 or 2 digits. Returns: dict[str, int]: date and time values, such as year, month, day of month, hours, minutes, seconds, time zone offset in minutes. Raises: ValueError: if the time string is invalid or not supported. """ if not time_string: raise ValueError('Invalid time string.') string_segments = time_string.split(' ') if len(string_segments) not in (5, 6): raise ValueError('Unsupported number of time string segments.') weekday_string = string_segments[0] if weekday_string.endswith(','): weekday_string = weekday_string[:-1] if weekday_string not in self._RFC_WEEKDAYS: raise ValueError(f'Invalid weekday: {weekday_string:s}.') string_segments.pop(0) day_of_month_string = string_segments[0] day_of_month = 0 if len(day_of_month_string) in (1, 2): try: day_of_month = int(day_of_month_string, 10) except ValueError: pass if day_of_month == 0: raise ValueError(f'Invalid day of month: {day_of_month_string:s}.') month_string = string_segments[1] month = self._RFC_MONTH_MAPPINGS.get(month_string) if not month: raise ValueError(f'Invalid month: {month_string:s}.') year_string = string_segments[2] year = None if len(year_string) == 4: try: year = int(year_string, 10) except ValueError: pass if year is None: raise ValueError(f'Invalid year: {year_string:s}.') hours, minutes, seconds, time_zone_offset = self._CopyTimeFromStringRFC( string_segments[3], string_segments[4]) date_time_values = { 'year': year, 'month': month, 'day_of_month': day_of_month, 'hours': hours, 'minutes': minutes, 'time_zone_offset': time_zone_offset} if seconds is not None: date_time_values['seconds'] = seconds return date_time_values def _CopyFromDateTimeValues(self, date_time_values): """Copies time elements from date and time values. Args: date_time_values (dict[str, int]): date and time values, such as year, month, day of month, hours, minutes, seconds, nanoseconds, time zone offset in minutes. """ year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) self._normalized_timestamp = None self._number_of_seconds = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) self._time_elements_tuple = ( year, month, day_of_month, hours, minutes, seconds) self._time_zone_offset = time_zone_offset def _CopyTimeFromStringISO8601(self, time_string): """Copies a time from an ISO 8601 time string. Args: time_string (str): time value formatted as: hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The fraction of second and time zone offset are optional. Returns: tuple[int, int, int, int, int]: hours, minutes, seconds, nanoseconds, time zone offset in minutes. Raises: ValueError: if the time string is invalid or not supported. """ if time_string.endswith('Z'): time_string = ''.join([time_string[:-1], '+00:00']) time_string_length = len(time_string) # The time string should at least contain 'hh'. if time_string_length < 2: raise ValueError('Time string too short.') try: hours = int(time_string[0:2], 10) except ValueError: raise ValueError('Unable to parse hours.') if hours not in range(0, 24): raise ValueError(f'Hours value: {hours:d} out of bounds.') minutes = None seconds = None nanoseconds = None time_zone_offset = None time_string_index = 2 # Minutes are either specified as 'hhmm', 'hh:mm' or as a fractional part # 'hh[.,]###'. if (time_string_index + 1 < time_string_length and time_string[time_string_index] not in ('.', ',')): if time_string[time_string_index] == ':': time_string_index += 1 if time_string_index + 2 > time_string_length: raise ValueError('Time string too short.') try: minutes = time_string[time_string_index:time_string_index + 2] minutes = int(minutes, 10) except ValueError: raise ValueError('Unable to parse minutes.') time_string_index += 2 # Seconds are either specified as 'hhmmss', 'hh:mm:ss' or as a fractional # part 'hh:mm[.,]###' or 'hhmm[.,]###'. if (time_string_index + 1 < time_string_length and time_string[time_string_index] not in ('.', ',')): if time_string[time_string_index] == ':': time_string_index += 1 if time_string_index + 2 > time_string_length: raise ValueError('Time string too short.') try: seconds = time_string[time_string_index:time_string_index + 2] seconds = int(seconds, 10) except ValueError: raise ValueError('Unable to parse day of seconds.') time_string_index += 2 time_zone_string_index = time_string_index while time_zone_string_index < time_string_length: if time_string[time_zone_string_index] in ('+', '-'): break time_zone_string_index += 1 # The calculations that follow rely on the time zone string index # to point beyond the string in case no time zone offset was defined. if time_zone_string_index == time_string_length - 1: time_zone_string_index += 1 if (time_string_length > time_string_index and time_string[time_string_index] in ('.', ',')): time_string_index += 1 time_fraction_length = time_zone_string_index - time_string_index try: time_fraction = time_string[time_string_index:time_zone_string_index] time_fraction = int(time_fraction, 10) time_fraction = ( decimal.Decimal(time_fraction) / decimal.Decimal(10 ** time_fraction_length)) except ValueError: raise ValueError('Unable to parse time fraction.') if minutes is None: time_fraction *= 60 minutes = int(time_fraction) time_fraction -= minutes if seconds is None: time_fraction *= 60 seconds = int(time_fraction) time_fraction -= seconds time_fraction *= definitions.NANOSECONDS_PER_SECOND nanoseconds = int(time_fraction) if minutes is not None and minutes not in range(0, 60): raise ValueError(f'Minutes value: {minutes:d} out of bounds.') # TODO: support a leap second? if seconds is not None and seconds not in range(0, 60): raise ValueError(f'Seconds value: {seconds:d} out of bounds.') if time_zone_string_index < time_string_length: if (time_string_length - time_zone_string_index != 6 or time_string[time_zone_string_index + 3] != ':'): raise ValueError('Invalid time string.') try: hours_from_utc = int(time_string[ time_zone_string_index + 1:time_zone_string_index + 3]) except ValueError: raise ValueError('Unable to parse time zone hours offset.') if hours_from_utc not in range(0, 15): raise ValueError('Time zone hours offset value out of bounds.') try: minutes_from_utc = int(time_string[ time_zone_string_index + 4:time_zone_string_index + 6]) except ValueError: raise ValueError('Unable to parse time zone minutes offset.') if minutes_from_utc not in range(0, 60): raise ValueError('Time zone minutes offset value out of bounds.') # pylint: disable=invalid-unary-operand-type time_zone_offset = (hours_from_utc * 60) + minutes_from_utc if time_string[time_zone_string_index] == '-': time_zone_offset = -time_zone_offset return hours, minutes, seconds, nanoseconds, time_zone_offset def _CopyTimeFromStringRFC(self, time_string, time_zone_string): """Copies a time from a RFC 822, RFC 1123 or RFC 2822 time string. Args: time_string (str): time value formatted as: hh:mm[:ss], where seconds (ss) are optional. time_zone_string (str): time zone value formatted as predefined time zone indicator or [+-]HHMM Returns: tuple[int, int, int, int]: hours, minutes, seconds, time zone offset in minutes. Raises: ValueError: if the time string is invalid or not supported. """ time_string_length = len(time_string) # The time string should at least contain 'hh:mm'. if time_string_length < 5: raise ValueError('Time string too short.') if time_string_length > 8: raise ValueError('Time string too long.') if time_string[2] != ':': raise ValueError('Invalid hours and minutes separator.') try: hours = int(time_string[0:2], 10) except ValueError: raise ValueError('Unable to parse hours.') if hours not in range(0, 24): raise ValueError(f'Hours value: {hours:d} out of bounds.') try: minutes = int(time_string[3:5], 10) except ValueError: raise ValueError('Unable to parse minutes.') if minutes not in range(0, 60): raise ValueError(f'Minutes value: {minutes:d} out of bounds.') seconds = None if time_string_length > 5: if time_string_length < 8: raise ValueError('Time string too short.') if time_string[5] != ':': raise ValueError('Invalid minutes and seconds separator.') try: seconds = int(time_string[6:8], 10) except ValueError: raise ValueError('Unable to parse seconds.') if seconds not in range(0, 60): raise ValueError(f'Seconds value: {seconds:d} out of bounds.') if time_string_length < 5: raise ValueError('Time string too short.') time_zone_string_length = len(time_zone_string) if time_zone_string_length > 5: raise ValueError('Time zone string too long.') if time_zone_string_length < 5: hours_from_utc = self._RFC_TIME_ZONE_MAPPINGS.get(time_zone_string, None) minutes_from_utc = 0 if hours_from_utc is None: raise ValueError(f'Invalid time zone: {time_zone_string:s}.') else: if time_zone_string[0] not in ('+', '-'): raise ValueError(f'Invalid time zone: {time_zone_string:s}.') try: hours_from_utc = int(time_zone_string[1:3], 10) except ValueError: raise ValueError('Unable to parse time zone hours offset.') if hours_from_utc not in range(0, 15): raise ValueError('Time zone hours offset value out of bounds.') try: minutes_from_utc = int(time_zone_string[3:5], 10) except ValueError: raise ValueError('Unable to parse time zone minutes offset.') if minutes_from_utc not in range(0, 60): raise ValueError('Time zone minutes offset value out of bounds.') time_zone_offset = (hours_from_utc * 60) + minutes_from_utc if time_zone_string[0] == '-': time_zone_offset = -time_zone_offset return hours, minutes, seconds, time_zone_offset @property def day_of_month(self): """int: day of month or None if not set.""" if not self._time_elements_tuple: return None return self._time_elements_tuple[2] @property def hours(self): """int: number of hours or None if not set.""" if not self._time_elements_tuple: return None return self._time_elements_tuple[3] @property def minutes(self): """int: number of minutes or None if not set.""" if not self._time_elements_tuple: return None return self._time_elements_tuple[4] @property def month(self): """int: month or None if not set.""" if not self._time_elements_tuple: return None return self._time_elements_tuple[1] @property def seconds(self): """int: number of seconds or None if not set.""" if not self._time_elements_tuple: return None return self._time_elements_tuple[5] @property def year(self): """int: year or None if not set.""" if not self._time_elements_tuple: return None return self._time_elements_tuple[0] def CopyFromDatetime(self, datetime_object): """Copies time elements from a Python datetime object. A naive datetime object is considered in local time. Args: datetime_object (datetime.datetime): Python datetime object. """ year, month, day_of_month, hours, minutes, seconds, _, _, _ = ( datetime_object.utctimetuple()) date_time_values = { 'year': year, 'month': month, 'day_of_month': day_of_month, 'hours': hours, 'minutes': minutes, 'seconds': seconds} self._CopyFromDateTimeValues(date_time_values) self.is_local_time = bool(datetime_object.tzinfo is None) def CopyFromDateTimeString(self, time_string): """Copies time elements from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. """ date_time_values = self._CopyDateTimeFromString(time_string) self._CopyFromDateTimeValues(date_time_values) def CopyFromStringISO8601(self, time_string): """Copies time elements from an ISO 8601 date and time string. Currently not supported: * Duration notation: "P..." * Week notation "2016-W33" * Date with week number notation "2016-W33-3" * Date without year notation "--08-17" * Ordinal date notation "2016-230" Args: time_string (str): date and time value formatted as: YYYY-MM-DDThh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Raises: ValueError: if the time string is invalid or not supported. """ date_time_values = self._CopyDateTimeFromStringISO8601(time_string) self._CopyFromDateTimeValues(date_time_values) def CopyFromStringRFC822(self, time_string): """Copies time elements from a RFC 822 date and time string. Args: time_string (str): date and time value formatted as: DAY, D MONTH YY hh:mm:ss ZONE Where weekday (DAY) and seconds (ss) are optional and day of month (D) can consist of 1 or 2 digits. Raises: ValueError: if the time string is invalid or not supported. """ date_time_values = self._CopyDateTimeFromStringRFC822(time_string) self._CopyFromDateTimeValues(date_time_values) def CopyFromStringRFC1123(self, time_string): """Copies time elements from a RFC 1123 date and time string. Args: time_string (str): date and time value formatted as: DAY, D MONTH YYYY hh:mm:ss ZONE Where weekday (DAY) and seconds (ss) are optional and day of month (D) can consist of 1 or 2 digits. Raises: ValueError: if the time string is invalid or not supported. """ date_time_values = self._CopyDateTimeFromStringRFC1123(time_string) self._CopyFromDateTimeValues(date_time_values) def CopyFromStringTuple(self, time_elements_tuple): """Copies time elements from string-based time elements tuple. Args: time_elements_tuple (Optional[tuple[str, str, str, str, str, str]]): time elements, contains year, month, day of month, hours, minutes and seconds. Raises: ValueError: if the time elements tuple is invalid. """ number_of_elements = len(time_elements_tuple) if number_of_elements < 6: raise ValueError(( f'Invalid time elements tuple at least 6 elements required,' f'got: {number_of_elements:d}')) year_string = time_elements_tuple[0] month_string = time_elements_tuple[1] day_of_month_string = time_elements_tuple[2] hours_string = time_elements_tuple[3] minutes_string = time_elements_tuple[4] seconds_string = time_elements_tuple[5] try: year = int(year_string, 10) except (TypeError, ValueError): raise ValueError(f'Invalid year value: {year_string!s}') try: month = int(month_string, 10) except (TypeError, ValueError): raise ValueError(f'Invalid month value: {month_string!s}') try: day_of_month = int(day_of_month_string, 10) except (TypeError, ValueError): raise ValueError(f'Invalid day of month value: {day_of_month_string!s}') try: hours = int(hours_string, 10) except (TypeError, ValueError): raise ValueError(f'Invalid hours value: {hours_string!s}') try: minutes = int(minutes_string, 10) except (TypeError, ValueError): raise ValueError(f'Invalid minutes value: {minutes_string!s}') try: seconds = int(seconds_string, 10) except (TypeError, ValueError): raise ValueError(f'Invalid seconds value: {seconds_string!s}') self._normalized_timestamp = None self._number_of_seconds = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) self._time_elements_tuple = ( year, month, day_of_month, hours, minutes, seconds) def CopyToDateTimeString(self): """Copies the time elements to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss" or None if time elements are missing. """ if self._number_of_seconds is None: return None year, month, day_of_month, hours, minutes, seconds = ( self._time_elements_tuple) return ( f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}') def NewFromDeltaAndDate(self, year, month, day_of_month): """Creates a new time elements instance from a date time delta and a date. Args: year (int): year. month (int): month, where 1 represents January and 0 if not set. day_of_month (int): day of month, where 1 represents the first day and 0 if not set. Returns: TimeElements: time elements or None if time elements are missing. Raises: ValueError: if the instance is not a date time delta. """ if not self._is_delta: raise ValueError('Not a date time delta.') if self._time_elements_tuple is None: return None delta_year, delta_month, delta_day_of_month, hours, minutes, seconds = ( self._time_elements_tuple) time_elements_tuple = ( year + delta_year, month + delta_month, day_of_month + delta_day_of_month, hours, minutes, seconds) date_time = TimeElements( precision=self._precision, time_elements_tuple=time_elements_tuple, time_zone_offset=self._time_zone_offset) date_time.is_local_time = self.is_local_time return date_time def NewFromDeltaAndYear(self, year): """Creates a new time elements instance from a date time delta and a year. Args: year (int): year. Returns: TimeElements: time elements or None if time elements are missing. Raises: ValueError: if the instance is not a date time delta. """ return self.NewFromDeltaAndDate(year, 0, 0) class TimeElementsWithFractionOfSecond(TimeElements): """Time elements with a fraction of second interface. Attributes: fraction_of_second (decimal.Decimal): fraction of second, which must be a value between 0.0 and 1.0. is_local_time (bool): True if the date and time value is in local time. """ def __init__( self, fraction_of_second=None, is_delta=False, precision=None, time_elements_tuple=None, time_zone_offset=None): """Initializes time elements. Args: fraction_of_second (Optional[decimal.Decimal]): fraction of second, which must be a value between 0.0 and 1.0. is_delta (Optional[bool]): True if the date and time value is relative to another date and time value. precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_elements_tuple (Optional[tuple[int, int, int, int, int, int]]): time elements, contains year, month, day of month, hours, minutes and seconds. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. Raises: ValueError: if the time elements tuple is invalid or fraction of second value is out of bounds. """ if fraction_of_second is not None: if fraction_of_second < 0.0 or fraction_of_second >= 1.0: raise ValueError( f'Fraction of second value: {fraction_of_second:f} out of bounds.') super(TimeElementsWithFractionOfSecond, self).__init__( is_delta=is_delta, precision=precision or definitions.PRECISION_1_SECOND, time_elements_tuple=time_elements_tuple, time_zone_offset=time_zone_offset) self.fraction_of_second = fraction_of_second def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if (self._number_of_seconds is not None and self.fraction_of_second is not None): self._normalized_timestamp = ( decimal.Decimal(self._number_of_seconds) + self.fraction_of_second) if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def _CopyFromDateTimeValues(self, date_time_values): """Copies time elements from date and time values. Args: date_time_values (dict[str, int]): date and time values, such as year, month, day of month, hours, minutes, seconds, nanoseconds, time zone offset in minutes. Raises: ValueError: if no helper can be created for the current precision. """ year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) nanoseconds = date_time_values.get('nanoseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) precision_helper = precisions.PrecisionHelperFactory.CreatePrecisionHelper( self._precision) fraction_of_second = precision_helper.CopyNanosecondsToFractionOfSecond( nanoseconds) self._normalized_timestamp = None self._number_of_seconds = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) self._time_elements_tuple = ( year, month, day_of_month, hours, minutes, seconds) self._time_zone_offset = time_zone_offset self.fraction_of_second = fraction_of_second def CopyFromDatetime(self, datetime_object): """Copies time elements from a Python datetime object. A naive datetime object is considered in local time. Args: datetime_object (datetime.datetime): Python datetime object. """ super(TimeElementsWithFractionOfSecond, self).CopyFromDatetime( datetime_object) precision_helper = precisions.PrecisionHelperFactory.CreatePrecisionHelper( self._precision) fraction_of_second = precision_helper.CopyNanosecondsToFractionOfSecond( datetime_object.microsecond * definitions.NANOSECONDS_PER_MICROSECOND) self.fraction_of_second = fraction_of_second def CopyFromStringTuple(self, time_elements_tuple): """Copies time elements from string-based time elements tuple. Args: time_elements_tuple (Optional[tuple[str, str, str, str, str, str, str]]): time elements, contains year, month, day of month, hours, minutes, seconds and fraction of seconds. Raises: ValueError: if the time elements tuple is invalid. """ number_of_elements = len(time_elements_tuple) if number_of_elements < 7: raise ValueError(( f'Invalid time elements tuple at least 7 elements required,' f'got: {number_of_elements:d}')) super(TimeElementsWithFractionOfSecond, self).CopyFromStringTuple( time_elements_tuple) fraction_of_second_string = time_elements_tuple[6] try: fraction_of_second = decimal.Decimal(fraction_of_second_string) except (TypeError, ValueError): raise ValueError( f'Invalid fraction of second value: {fraction_of_second_string!s}') if fraction_of_second < 0.0 or fraction_of_second >= 1.0: raise ValueError( f'Fraction of second value: {fraction_of_second:f} out of bounds.') self.fraction_of_second = fraction_of_second def CopyToDateTimeString(self): """Copies the time elements to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss" or "YYYY-MM-DD hh:mm:ss.######" or None if time elements are missing. Raises: ValueError: if the precision value is unsupported. """ if self._number_of_seconds is None or self.fraction_of_second is None: return None precision_helper = precisions.PrecisionHelperFactory.CreatePrecisionHelper( self._precision) return precision_helper.CopyToDateTimeString( self._time_elements_tuple, self.fraction_of_second) def NewFromDeltaAndDate(self, year, month, day_of_month): """Creates a new time elements instance from a date time delta and a date. Args: year (int): year. month (int): month, where 1 represents January and 0 if not set. day_of_month (int): day of month, where 1 represents the first day and 0 if not set. Returns: TimeElementsWithFractionOfSecond: time elements or None if time elements are missing. Raises: ValueError: if the instance is not a date time delta. """ if not self._is_delta: raise ValueError('Not a date time delta.') if self._time_elements_tuple is None: return None delta_year, delta_month, delta_day_of_month, hours, minutes, seconds = ( self._time_elements_tuple) time_elements_tuple = ( year + delta_year, month + delta_month, day_of_month + delta_day_of_month, hours, minutes, seconds) return TimeElementsWithFractionOfSecond( fraction_of_second=self.fraction_of_second, precision=self._precision, time_elements_tuple=time_elements_tuple, time_zone_offset=self._time_zone_offset) def NewFromDeltaAndYear(self, year): """Creates a new time elements instance from a date time delta and a year. Args: year (int): year. Returns: TimeElementsWithFractionOfSecond: time elements or None if time elements are missing. Raises: ValueError: if the instance is not a date time delta. """ return self.NewFromDeltaAndDate(year, 0, 0) class TimeElementsInMilliseconds(TimeElementsWithFractionOfSecond): """Time elements in milliseconds. Attributes: fraction_of_second (decimal.Decimal): fraction of second, which must be a value between 0.0 and 1.0. is_local_time (bool): True if the date and time value is in local time. precision (str): precision of the date of the date and time value, that represents 1 millisecond (PRECISION_1_MILLISECOND). """ def __init__( self, is_delta=False, precision=None, time_elements_tuple=None, time_zone_offset=None): """Initializes time elements. Args: is_delta (Optional[bool]): True if the date and time value is relative to another date and time value. precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_elements_tuple (Optional[tuple[int, int, int, int, int, int, int]]): time elements, contains year, month, day of month, hours, minutes, seconds and milliseconds. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. Raises: ValueError: if the time elements tuple is invalid. """ fraction_of_second = None if time_elements_tuple: number_of_elements = len(time_elements_tuple) if number_of_elements < 7: raise ValueError(( f'Invalid time elements tuple at least 7 elements required,' f'got: {number_of_elements:d}')) milliseconds = time_elements_tuple[6] time_elements_tuple = time_elements_tuple[:6] if (milliseconds < 0 or milliseconds >= definitions.MILLISECONDS_PER_SECOND): raise ValueError('Invalid number of milliseconds.') fraction_of_second = ( decimal.Decimal(milliseconds) / definitions.MILLISECONDS_PER_SECOND) super(TimeElementsInMilliseconds, self).__init__( fraction_of_second=fraction_of_second, is_delta=is_delta, precision=precision or definitions.PRECISION_1_MILLISECOND, time_elements_tuple=time_elements_tuple, time_zone_offset=time_zone_offset) @property def milliseconds(self): """int: number of milliseconds.""" return int(self.fraction_of_second * definitions.MILLISECONDS_PER_SECOND) def CopyFromStringTuple(self, time_elements_tuple): """Copies time elements from string-based time elements tuple. Args: time_elements_tuple (Optional[tuple[str, str, str, str, str, str, str]]): time elements, contains year, month, day of month, hours, minutes, seconds and milliseconds. Raises: ValueError: if the time elements tuple is invalid. """ number_of_elements = len(time_elements_tuple) if len(time_elements_tuple) < 7: raise ValueError(( f'Invalid time elements tuple at least 7 elements required,' f'got: {number_of_elements:d}')) year, month, day_of_month, hours, minutes, seconds, milliseconds = ( time_elements_tuple) try: milliseconds = int(milliseconds, 10) except (TypeError, ValueError): raise ValueError(f'Invalid millisecond value: {milliseconds!s}') if milliseconds < 0 or milliseconds >= definitions.MILLISECONDS_PER_SECOND: raise ValueError('Invalid number of milliseconds.') fraction_of_second = ( decimal.Decimal(milliseconds) / definitions.MILLISECONDS_PER_SECOND) time_elements_tuple = ( year, month, day_of_month, hours, minutes, seconds, str(fraction_of_second)) super(TimeElementsInMilliseconds, self).CopyFromStringTuple( time_elements_tuple) def NewFromDeltaAndDate(self, year, month, day_of_month): """Creates a new time elements instance from a date time delta and a date. Args: year (int): year. month (int): month, where 1 represents January and 0 if not set. day_of_month (int): day of month, where 1 represents the first day and 0 if not set. Returns: TimeElementsInMilliseconds: time elements or None if time elements are missing. Raises: ValueError: if the instance is not a date time delta. """ if not self._is_delta: raise ValueError('Not a date time delta.') if self._time_elements_tuple is None: return None delta_year, delta_month, delta_day_of_month, hours, minutes, seconds = ( self._time_elements_tuple) time_elements_tuple = ( year + delta_year, month + delta_month, day_of_month + delta_day_of_month, hours, minutes, seconds, self.milliseconds) return TimeElementsInMilliseconds( precision=self._precision, time_elements_tuple=time_elements_tuple, time_zone_offset=self._time_zone_offset) def NewFromDeltaAndYear(self, year): """Creates a new time elements instance from a date time delta and a year. Args: year (int): year. Returns: TimeElementsInMilliseconds: time elements or None if time elements are missing. Raises: ValueError: if the instance is not a date time delta. """ return self.NewFromDeltaAndDate(year, 0, 0) class TimeElementsInMicroseconds(TimeElementsWithFractionOfSecond): """Time elements in microseconds. Attributes: fraction_of_second (decimal.Decimal): fraction of second, which must be a value between 0.0 and 1.0. is_local_time (bool): True if the date and time value is in local time. precision (str): precision of the date of the date and time value, that represents 1 microsecond (PRECISION_1_MICROSECOND). """ def __init__( self, is_delta=False, precision=None, time_elements_tuple=None, time_zone_offset=None): """Initializes time elements. Args: is_delta (Optional[bool]): True if the date and time value is relative to another date and time value. precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_elements_tuple (Optional[tuple[int, int, int, int, int, int, int]]): time elements, contains year, month, day of month, hours, minutes, seconds and microseconds. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. Raises: ValueError: if the time elements tuple is invalid. """ fraction_of_second = None if time_elements_tuple: number_of_elements = len(time_elements_tuple) if number_of_elements < 7: raise ValueError(( f'Invalid time elements tuple at least 7 elements required,' f'got: {number_of_elements:d}')) microseconds = time_elements_tuple[6] time_elements_tuple = time_elements_tuple[:6] if (microseconds < 0 or microseconds >= definitions.MICROSECONDS_PER_SECOND): raise ValueError('Invalid number of microseconds.') fraction_of_second = ( decimal.Decimal(microseconds) / definitions.MICROSECONDS_PER_SECOND) super(TimeElementsInMicroseconds, self).__init__( fraction_of_second=fraction_of_second, is_delta=is_delta, precision=precision or definitions.PRECISION_1_MICROSECOND, time_elements_tuple=time_elements_tuple, time_zone_offset=time_zone_offset) @property def microseconds(self): """int: number of microseconds.""" return int(self.fraction_of_second * definitions.MICROSECONDS_PER_SECOND) def CopyFromStringTuple(self, time_elements_tuple): """Copies time elements from string-based time elements tuple. Args: time_elements_tuple (Optional[tuple[str, str, str, str, str, str, str]]): time elements, contains year, month, day of month, hours, minutes, seconds and microseconds. Raises: ValueError: if the time elements tuple is invalid. """ number_of_elements = len(time_elements_tuple) if len(time_elements_tuple) < 7: raise ValueError(( f'Invalid time elements tuple at least 7 elements required,' f'got: {number_of_elements:d}')) year, month, day_of_month, hours, minutes, seconds, microseconds = ( time_elements_tuple) try: microseconds = int(microseconds, 10) except (TypeError, ValueError): raise ValueError(f'Invalid microsecond value: {microseconds!s}') if microseconds < 0 or microseconds >= definitions.MICROSECONDS_PER_SECOND: raise ValueError('Invalid number of microseconds.') fraction_of_second = ( decimal.Decimal(microseconds) / definitions.MICROSECONDS_PER_SECOND) time_elements_tuple = ( year, month, day_of_month, hours, minutes, seconds, str(fraction_of_second)) super(TimeElementsInMicroseconds, self).CopyFromStringTuple( time_elements_tuple) def NewFromDeltaAndDate(self, year, month, day_of_month): """Creates a new time elements instance from a date time delta and a year. Args: year (int): year. month (int): month, where 1 represents January and 0 if not set. day_of_month (int): day of month, where 1 represents the first day and 0 if not set. Returns: TimeElementsInMicroseconds: time elements or None if time elements are missing. Raises: ValueError: if the instance is not a date time delta. """ if not self._is_delta: raise ValueError('Not a date time delta.') if self._time_elements_tuple is None: return None delta_year, delta_month, delta_day_of_month, hours, minutes, seconds = ( self._time_elements_tuple) time_elements_tuple = ( year + delta_year, month + delta_month, day_of_month + delta_day_of_month, hours, minutes, seconds, self.microseconds) return TimeElementsInMicroseconds( precision=self._precision, time_elements_tuple=time_elements_tuple, time_zone_offset=self._time_zone_offset) def NewFromDeltaAndYear(self, year): """Creates a new time elements instance from a date time delta and a year. Args: year (int): year. Returns: TimeElementsInMicroseconds: time elements or None if time elements are missing. Raises: ValueError: if the instance is not a date time delta. """ return self.NewFromDeltaAndDate(year, 0, 0) class TimeElementsInNanoseconds(TimeElementsWithFractionOfSecond): """Time elements in nanoseconds. Attributes: fraction_of_second (decimal.Decimal): fraction of second, which must be a value between 0.0 and 1.0. is_local_time (bool): True if the date and time value is in local time. precision (str): precision of the date of the date and time value, that represents 1 nanosecond (PRECISION_1_NANOSECOND). """ def __init__( self, is_delta=False, precision=None, time_elements_tuple=None, time_zone_offset=None): """Initializes time elements. Args: is_delta (Optional[bool]): True if the date and time value is relative to another date and time value. precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_elements_tuple (Optional[tuple[int, int, int, int, int, int, int]]): time elements, contains year, month, day of month, hours, minutes, seconds and nanoseconds. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. Raises: ValueError: if the time elements tuple is invalid. """ fraction_of_second = None if time_elements_tuple: number_of_elements = len(time_elements_tuple) if number_of_elements < 7: raise ValueError(( f'Invalid time elements tuple at least 7 elements required,' f'got: {number_of_elements:d}')) nanoseconds = time_elements_tuple[6] time_elements_tuple = time_elements_tuple[:6] if (nanoseconds < 0 or nanoseconds >= definitions.NANOSECONDS_PER_SECOND): raise ValueError('Invalid number of nanoseconds.') fraction_of_second = ( decimal.Decimal(nanoseconds) / definitions.NANOSECONDS_PER_SECOND) super(TimeElementsInNanoseconds, self).__init__( fraction_of_second=fraction_of_second, is_delta=is_delta, precision=precision or definitions.PRECISION_1_NANOSECOND, time_elements_tuple=time_elements_tuple, time_zone_offset=time_zone_offset) @property def nanoseconds(self): """int: number of nanoseconds.""" return int(self.fraction_of_second * definitions.NANOSECONDS_PER_SECOND) def CopyFromStringTuple(self, time_elements_tuple): """Copies time elements from string-based time elements tuple. Args: time_elements_tuple (Optional[tuple[str, str, str, str, str, str, str]]): time elements, contains year, month, day of month, hours, minutes, seconds and nanoseconds. Raises: ValueError: if the time elements tuple is invalid. """ number_of_elements = len(time_elements_tuple) if len(time_elements_tuple) < 7: raise ValueError(( f'Invalid time elements tuple at least 7 elements required,' f'got: {number_of_elements:d}')) year, month, day_of_month, hours, minutes, seconds, nanoseconds = ( time_elements_tuple) try: nanoseconds = int(nanoseconds, 10) except (TypeError, ValueError): raise ValueError(f'Invalid nanosecond value: {nanoseconds!s}') if nanoseconds < 0 or nanoseconds >= definitions.NANOSECONDS_PER_SECOND: raise ValueError('Invalid number of nanoseconds.') fraction_of_second = ( decimal.Decimal(nanoseconds) / definitions.NANOSECONDS_PER_SECOND) time_elements_tuple = ( year, month, day_of_month, hours, minutes, seconds, str(fraction_of_second)) super(TimeElementsInNanoseconds, self).CopyFromStringTuple( time_elements_tuple) def NewFromDeltaAndDate(self, year, month, day_of_month): """Creates a new time elements instance from a date time delta and a year. Args: year (int): year. month (int): month, where 1 represents January and 0 if not set. day_of_month (int): day of month, where 1 represents the first day and 0 if not set. Returns: TimeElementsInNanoseconds: time elements or None if time elements are missing. Raises: ValueError: if the instance is not a date time delta. """ if not self._is_delta: raise ValueError('Not a date time delta.') if self._time_elements_tuple is None: return None delta_year, delta_month, delta_day_of_month, hours, minutes, seconds = ( self._time_elements_tuple) time_elements_tuple = ( year + delta_year, month + delta_month, day_of_month + delta_day_of_month, hours, minutes, seconds, self.nanoseconds) return TimeElementsInNanoseconds( precision=self._precision, time_elements_tuple=time_elements_tuple, time_zone_offset=self._time_zone_offset) def NewFromDeltaAndYear(self, year): """Creates a new time elements instance from a date time delta and a year. Args: year (int): year. Returns: TimeElementsInNanoseconds: time elements or None if time elements are missing. Raises: ValueError: if the instance is not a date time delta. """ return self.NewFromDeltaAndDate(year, 0, 0) factory.Factory.RegisterDateTimeValues(TimeElements) factory.Factory.RegisterDateTimeValues(TimeElementsInMilliseconds) factory.Factory.RegisterDateTimeValues(TimeElementsInMicroseconds) factory.Factory.RegisterDateTimeValues(TimeElementsInNanoseconds) dfdatetime-20240504/dfdatetime/uuid_time.py000066400000000000000000000117441461534226200205420ustar00rootroot00000000000000# -*- coding: utf-8 -*- """UUID version 1 time implementation.""" import decimal from dfdatetime import definitions from dfdatetime import factory from dfdatetime import interface class UUIDTimeEpoch(interface.DateTimeEpoch): """UUID version 1 time epoch.""" def __init__(self): """Initializes an UUID version 1 time epoch.""" super(UUIDTimeEpoch, self).__init__(1582, 10, 15) class UUIDTime(interface.DateTimeValues): """UUID version 1 timestamp. The UUID version 1 timestamp is an unsigned 60-bit value that contains the number of 100th nano seconds intervals since 1582-10-15 00:00:00. Also see: https://en.wikipedia.org/wiki/Universally_unique_identifier Attributes: is_local_time (bool): True if the date and time value is in local time. """ _EPOCH = UUIDTimeEpoch() # The difference between October 15, 1582 and January 1, 1970 in seconds. _UUID_TO_POSIX_BASE = 12219292800 def __init__(self, precision=None, time_zone_offset=None, timestamp=None): """Initializes an UUID version 1 timestamp. Args: precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. timestamp (Optional[int]): UUID version 1 timestamp. Raises: ValueError: if the UUID version 1 timestamp is invalid. """ if timestamp and (timestamp < 0 or timestamp > self._UINT60_MAX): raise ValueError('Invalid UUID version 1 timestamp.') super(UUIDTime, self).__init__( precision=precision or definitions.PRECISION_100_NANOSECONDS, time_zone_offset=time_zone_offset) self._timestamp = timestamp @property def timestamp(self): """int: UUID timestamp or None if not set.""" return self._timestamp def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if (self._timestamp is not None and self._timestamp >= 0 and self._timestamp <= self._UINT60_MAX): self._normalized_timestamp = ( decimal.Decimal(self._timestamp) / self._100_NANOSECONDS_PER_SECOND) self._normalized_timestamp -= self._UUID_TO_POSIX_BASE if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def CopyFromDateTimeString(self, time_string): """Copies an UUID timestamp from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Raises: ValueError: if the time string is invalid or not supported. """ date_time_values = self._CopyDateTimeFromString(time_string) year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) nanoseconds = date_time_values.get('nanoseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) if year < 1582: raise ValueError('Year value not supported.') nanoseconds, _ = divmod(nanoseconds, 100) timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp += self._UUID_TO_POSIX_BASE timestamp *= self._100_NANOSECONDS_PER_SECOND timestamp += nanoseconds self._normalized_timestamp = None self._timestamp = timestamp self._time_zone_offset = time_zone_offset def CopyToDateTimeString(self): """Copies the UUID timestamp to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.#######" or None if the timestamp is missing or invalid. """ if (self._timestamp is None or self._timestamp < 0 or self._timestamp > self._UINT60_MAX): return None timestamp, fraction_of_second = divmod( self._timestamp, self._100_NANOSECONDS_PER_SECOND) number_of_days, hours, minutes, seconds = self._GetTimeValues(timestamp) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}.{fraction_of_second:07d}') factory.Factory.RegisterDateTimeValues(UUIDTime) dfdatetime-20240504/dfdatetime/webkit_time.py000066400000000000000000000112201461534226200210460ustar00rootroot00000000000000# -*- coding: utf-8 -*- """WebKit time implementation.""" import decimal from dfdatetime import definitions from dfdatetime import factory from dfdatetime import interface class WebKitTimeEpoch(interface.DateTimeEpoch): """WebKit time epoch.""" def __init__(self): """Initializes a WebKit time epoch.""" super(WebKitTimeEpoch, self).__init__(1601, 1, 1) class WebKitTime(interface.DateTimeValues): """WebKit timestamp. The WebKit timestamp is a signed 64-bit integer that contains the number of microseconds since 1601-01-01 00:00:00. Attributes: is_local_time (bool): True if the date and time value is in local time. """ _EPOCH = WebKitTimeEpoch() # The difference between January 1, 1601 and January 1, 1970 in seconds. _WEBKIT_TO_POSIX_BASE = 11644473600 def __init__(self, precision=None, time_zone_offset=None, timestamp=None): """Initializes a WebKit timestamp. Args: precision (Optional[str]): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. timestamp (Optional[int]): WebKit timestamp. """ super(WebKitTime, self).__init__( precision=precision or definitions.PRECISION_1_MICROSECOND, time_zone_offset=time_zone_offset) self._timestamp = timestamp @property def timestamp(self): """decimal.Decimal: WebKit timestamp or None if not set.""" return self._timestamp def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: float: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ if self._normalized_timestamp is None: if (self._timestamp is not None and self._timestamp >= self._INT64_MIN and self._timestamp <= self._INT64_MAX): self._normalized_timestamp = ( decimal.Decimal(self._timestamp) / definitions.MICROSECONDS_PER_SECOND) self._normalized_timestamp -= self._WEBKIT_TO_POSIX_BASE if self._time_zone_offset: self._normalized_timestamp -= self._time_zone_offset * 60 return self._normalized_timestamp def CopyFromDateTimeString(self, time_string): """Copies a WebKit timestamp from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3, 6 or 9 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Raises: ValueError: if the time string is invalid or not supported. """ date_time_values = self._CopyDateTimeFromString(time_string) year = date_time_values.get('year', 0) month = date_time_values.get('month', 0) day_of_month = date_time_values.get('day_of_month', 0) hours = date_time_values.get('hours', 0) minutes = date_time_values.get('minutes', 0) seconds = date_time_values.get('seconds', 0) nanoseconds = date_time_values.get('nanoseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', None) microseconds, _ = divmod( nanoseconds, definitions.NANOSECONDS_PER_MICROSECOND) timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp += self._WEBKIT_TO_POSIX_BASE timestamp *= definitions.MICROSECONDS_PER_SECOND timestamp += microseconds self._normalized_timestamp = None self._timestamp = timestamp self._time_zone_offset = time_zone_offset def CopyToDateTimeString(self): """Copies the WebKit timestamp to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.######" or None if the timestamp is missing or invalid. """ if (self._timestamp is None or self._timestamp < self._INT64_MIN or self._timestamp > self._INT64_MAX): return None timestamp, microseconds = divmod( self._timestamp, definitions.MICROSECONDS_PER_SECOND) number_of_days, hours, minutes, seconds = self._GetTimeValues(timestamp) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) return (f'{year:04d}-{month:02d}-{day_of_month:02d} ' f'{hours:02d}:{minutes:02d}:{seconds:02d}.{microseconds:06d}') factory.Factory.RegisterDateTimeValues(WebKitTime) dfdatetime-20240504/docs/000077500000000000000000000000001461534226200150175ustar00rootroot00000000000000dfdatetime-20240504/docs/conf.py000066400000000000000000000117731461534226200163270ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Sphinx build configuration file.""" import os import sys from sphinx.ext import apidoc from docutils import nodes from docutils import transforms # Change PYTHONPATH to include dfdatetime module and dependencies. sys.path.insert(0, os.path.abspath('..')) import dfdatetime # pylint: disable=wrong-import-position import utils.dependencies # pylint: disable=wrong-import-position # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. needs_sphinx = '2.0.1' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'recommonmark', 'sphinx.ext.autodoc', 'sphinx.ext.coverage', 'sphinx.ext.doctest', 'sphinx.ext.napoleon', 'sphinx.ext.viewcode', 'sphinx_markdown_tables', 'sphinx_rtd_theme', ] # We cannot install architecture dependent Python modules on readthedocs, # therefore we mock most imports. pip_installed_modules = set() dependency_helper = utils.dependencies.DependencyHelper( dependencies_file=os.path.join('..', 'dependencies.ini'), test_dependencies_file=os.path.join('..', 'test_dependencies.ini')) modules_to_mock = set(dependency_helper.dependencies.keys()) modules_to_mock = modules_to_mock.difference(pip_installed_modules) autodoc_mock_imports = sorted(modules_to_mock) # Options for the Sphinx Napoleon extension, which reads Google-style # docstrings. napoleon_google_docstring = True napoleon_numpy_docstring = False napoleon_include_init_with_doc = True napoleon_include_private_with_doc = False napoleon_include_special_with_doc = True # General information about the project. # pylint: disable=redefined-builtin project = 'dfDateTime' copyright = 'The dfDateTime authors' version = dfdatetime.__version__ release = dfdatetime.__version__ # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The master toctree document. master_doc = 'index' # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'sphinx_rtd_theme' # Output file base name for HTML help builder. htmlhelp_basename = 'dfdatetimedoc' # -- Options linkcheck ---------------------------------------------------- linkcheck_ignore = [ ] # -- Code to rewrite links for readthedocs -------------------------------- # This function is a Sphinx core event callback, the format of which is detailed # here: https://www.sphinx-doc.org/en/master/extdev/appapi.html#events # pylint: disable=unused-argument def RunSphinxAPIDoc(app): """Runs sphinx-apidoc to auto-generate documentation. Args: app (sphinx.application.Sphinx): Sphinx application. Required by the the Sphinx event callback API. """ current_directory = os.path.abspath(os.path.dirname(__file__)) module_path = os.path.join(current_directory, '..', 'dfdatetime') api_directory = os.path.join(current_directory, 'sources', 'api') apidoc.main(['-o', api_directory, module_path, '--force']) class MarkdownLinkFixer(transforms.Transform): """Transform definition to parse .md references to internal pages.""" default_priority = 1000 _URI_PREFIXES = [] def _FixLinks(self, node): """Corrects links to .md files not part of the documentation. Args: node (docutils.nodes.Node): docutils node. Returns: docutils.nodes.Node: docutils node, with correct URIs outside of Markdown pages outside the documentation. """ if isinstance(node, nodes.reference) and 'refuri' in node: reference_uri = node['refuri'] for uri_prefix in self._URI_PREFIXES: if (reference_uri.startswith(uri_prefix) and not ( reference_uri.endswith('.asciidoc') or reference_uri.endswith('.md'))): node['refuri'] = reference_uri + '.md' break return node def _Traverse(self, node): """Traverses the document tree rooted at node. Args: node (docutils.nodes.Node): docutils node. """ self._FixLinks(node) for child_node in node.children: self._Traverse(child_node) # pylint: disable=arguments-differ def apply(self): """Applies this transform on document tree.""" self._Traverse(self.document) # pylint: invalid-name def setup(app): """Called at Sphinx initialization. Args: app (sphinx.application.Sphinx): Sphinx application. """ # Triggers sphinx-apidoc to generate API documentation. app.connect('builder-inited', RunSphinxAPIDoc) app.add_config_value( 'recommonmark_config', {'enable_auto_toc_tree': True}, True) app.add_transform(MarkdownLinkFixer) dfdatetime-20240504/docs/index.rst000066400000000000000000000011211461534226200166530ustar00rootroot00000000000000Welcome to the dfDateTime documentation ======================================== dfDateTime, or Digital Forensics date and time, provides date and time objects to preserve accuracy and precision. The source code is available from the `project page `__. .. toctree:: :maxdepth: 2 sources/user/index .. toctree:: :maxdepth: 2 Date and time values .. toctree:: :maxdepth: 2 API documentation Indices and tables ================== * :ref:`genindex` * :ref:`modindex` dfdatetime-20240504/docs/requirements.txt000066400000000000000000000001661461534226200203060ustar00rootroot00000000000000certifi >= 2023.11.17 docutils Markdown recommonmark sphinx >= 4.1.0 sphinx-markdown-tables sphinx-rtd-theme >= 0.5.1 dfdatetime-20240504/docs/sources/000077500000000000000000000000001461534226200165025ustar00rootroot00000000000000dfdatetime-20240504/docs/sources/Date-and-time-values.md000066400000000000000000000406101461534226200226730ustar00rootroot00000000000000# Date and time values ## Terminology **Calendar**: a system of organising days. **Epoch**: a reference point from which time is measured. **Leap second**: a leap second is a one-second adjustment that is occasionally applied to Coordinated Universal Time (UTC) in order to keep its time of day close to the mean solar time. ### Also see * [Wikipedia: Calendar](https://en.wikipedia.org/wiki/Calendar) * [Wikipedia: Epoch](https://en.wikipedia.org/wiki/Epoch_(reference_date)) * [Wikipedia: Leap second](https://en.wikipedia.org/wiki/Leap_second) ## Accuracy and precision * **accuracy** is used to describe the closeness of a measurement to the true value; * **precision** is the closeness of agreement among a set of results. In NTFS data and time values are stored using a [FILETIME structure](https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime). The FILETIME structure stores a date and time value as a 64-bit integer that represents the number of 100-nanosecond intervals since "January 1, 1601 00:00:00.0000000 UTC". This provides an upper bound of a 100-nanosecond interval **data granularity**. For disambiguation we'll refer to this as **datetime storage granularity**. However this does not have to mean that the actual value stored within the FILETIME structure has a 100-nanosecond interval data granularity. E.g. the FILETIME MSDN article also mentions that on NTFS, the access time has a **resolution** of 1 hour. For disambiguation the term resolution is comparable with that of [Display resolution](https://en.wikipedia.org/wiki/Display_resolution) or [Audio resolution](https://en.wikipedia.org/wiki/Audio_bit_depth), we'll refer to this as **datetime value granularity**. The FILETIME MSDN article also mentions that some values should be interpreted not as date and time values e.g. 0xFFFFFFFF can be used to specify that a file's previous access time should be preserved, or 0 to specify that the date and time value is not set. For disambiguation we'll refer to this as **datetime value semantics** (in other words meaning or representation). ### Digital forensics significance A large part of digital forensic analysis is about the interpretation of computer data. If date and time values are converted from a storage format to another storage format or a string representation we are likely to change granularity and semantics in the process. If the analyst is aware of this the impact should be minimal, however if we wish to have automation assisted analysis we'll have to preserve concepts of granularity and semantics and represent them in a machine process-able manner. So that the automation can distinguish between a FILETIME date and time value that is not set versus a FILETIME date and time value that represents "January 1, 1601 00:00:00.0000000 UTC". Another aspect relevant to digital forensic analysis is how to represent a datetime value with a value granularity of 1 day (e.g. FAT access time) in a timeline of microseconds, where the analyst is only interested in the events that occurred within a specific hour on that day. Technically the access time should be included in the resulting timeline since the access could have occurred during that specific hour. ### References * [Wikipedia: Accuracy and precision](https://en.wikipedia.org/wiki/Accuracy_and_precision) * [Wikipedia: Computer forensics](https://en.wikipedia.org/wiki/Computer_forensics) * [Wikipedia: Granularity - Data and information](https://en.wikipedia.org/wiki/Granularity#Data_and_information) * [Wikipedia: Semantics](https://en.wikipedia.org/wiki/Semantics) * [FILETIME structure](https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime) * [File Times](https://learn.microsoft.com/en-us/windows/win32/sysinfo/file-times) * [Precision and accuracy of DateTime](https://learn.microsoft.com/en-us/archive/blogs/ericlippert/precision-and-accuracy-of-datetime), by Eric Lippert, April 8, 2010 ## .NET DateTime ### Characteristics Attribute | Description --- | --- Supported date range | 0001-01-01 00:00:00 through 9999-12-31 23:59:59 Storage granularity | 100 nanoseconds Time zone | externally represented, typically UTC ### Format Offset | Size | Description --- | --- | --- 0 | 4 or 8 | timestamp, little endian integer value containing the number of 100 nanosecond intervals since January 1, 0001 00:00:00.0000000 ### Also see * [Microsoft: DateTime struct](https://learn.microsoft.com/en-us/dotnet/api/system.datetime?view=net-6.0) * [Microsoft: DateTime.Ticks](https://learn.microsoft.com/en-us/dotnet/api/system.datetime.ticks?view=net-8.0) ## APFS timestamp ### Characteristics Attribute | Description --- | --- Supported date range | 1677-09-21 00:12:43.145224192 through 2262-04-11 23:47:16.854775807 Storage granularity | 1 nanosecond Time zone | externally represented, typically UTC ### Format Offset | Size | Description --- | --- | --- 0 | 8 | timestamp, integer value containing the number of nanoseconds before (when negative) or after (when positive) 1970-01-01 00:00:00.000 (or POSIX or Unix epoch) ## Cocoa timestamp ### Characteristics Attribute | Description --- | --- Supported date range | ... Storage granularity | 1 second with higher granularity in fractional part Time zone | externally represented, typically UTC ### Format Offset | Size | Description --- | --- | --- 0 | 4 or 8 | timestamp, floating point value containing the number of seconds since 2001-01-01 00:00:00 ### Also see * [Apple Developer: Dates](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/DatesAndTimes/Articles/dtDates.html) * [Apple Developer: NSDate](https://developer.apple.com/documentation/foundation/nsdate) ## Delphi TDateTime ### Characteristics Attribute | Description --- | --- Supported date range | ... through 9999-12-31 23:59:59.999 Storage granularity | 1 day with higher granularity in fractional part Time zone | externally represented, typically UTC ### Format Offset | Size | Description --- | --- | --- 0 | 4 | timestamp, floating point value containing the number of days since 1899-12-30 00:00:00 ### Also see * Embarcadero Delphi documentation on System.TDateTime ## FAT date and time ### Characteristics Attribute | Description --- | --- Supported date range | 1980-01-01 00:00:00 through 2107-12-31 23:59:58 Storage granularity | 2-second intervals Time zone | externally represented, typically local time Sometimes 2099-12-31 23:59:58 is defined as the upper bound of DOS date and time. Also see: [Time formatting and storage bugs - Year 2100](https://en.wikipedia.org/wiki/Time_formatting_and_storage_bugs#Year_2100) ### Format The FAT (or DOS) date is a 16-bit value that consists of: Offset | Size | Description --- | --- | --- 0.0 | 5 bits | day of month, where 1 is the first day of the month 0.5 | 4 bits | month, where January is represented by 1 1.1 | 7 bits | year, ranging from 1980 through The FAT (or DOS) time is a 16-bit value that consists of: Offset | Size | Description --- | --- | --- 0.0 | 5 bits | number of 2-second intervals, ranging from 0 through 29 0.5 | 6 bits | minutes, ranging from 0 through 59 1.2 | 5 bits | hours, ranging from 0 through 23 ### Also see * [Wikipedia: File Allocation Table](https://en.wikipedia.org/wiki/File_Allocation_Table) * [DosDateTimeToFileTime function](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-dosdatetimetofiletime) ## FAT timestamp ### Characteristics Attribute | Description --- | --- Supported date range | 1980-01-01 00:00:00.00 through 2107-12-31 23:59:58.99 Storage granularity | 10 milliseconds Time zone | externally represented ### Format Offset | Size | Description --- | --- | --- 0 | 8 | timestamp, integer value containing the number of 10 milliseconds intervals after 1980-01-01 00:00:00.00 (or FAT date time epoch) ## FILETIME ### Characteristics Attribute | Description --- | --- Supported date range | 1601-01-01 00:00:00.0000000 through ... Storage granularity | 100-nanosecond intervals Time zone | externally represented, typically UTC The actual upper bound of the supported date range is unclear. ### Format The FILETIME structure is 8 bytes of size and consists of: Offset | Size | Description --- | --- | --- 0 | 4 | lower 32-bit of the 64-bit timestamp 4 | 4 | upper 32-bit of the 64-bit timestamp The FILETIME should be treated as a structure when stored and passed to Windows API functions. However it can be combined into a 64-bit integer, which will be indicated as a FILETIME timestamp, containing the number of seconds since 1601-01-01 00:00:00.0000000. An empty (or unset) FILETIME timestamp can be represented by 0, however the meaning can differ depending on the context in which the timestamp is used. Certain values of FILETIME such as { 0xffffffff, 0xffffffff } for the SetFileTime Windows API function are overloaded with a special meaning. ### Also see * [MSDN: FILETIME](https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime) ## Golang time.Time timestamp ### Characteristics Attribute | Description --- | --- Supported date range | 0001-01-01 00:00:00.0000000 through ... Storage granularity | 1 nanosecond Time zone | internally represented. The granularity of the time zone value depends on the version of the timestamp. Version 1 timestamps are stored in minutes and version 2 timestamps add a seconds component. *Note: Version 2 is currently not supported.* ### Format Offset | Size | Description --- | --- | --- 0 | 1 | version (known values are 1 or 2) 1 | 8 | seconds since January, 1, 1 stored as a 64-bit big-endian signed integer 9 | 4 | fraction of second, in nanoseconds stored as a 32-bit big-endian signed integer 13 | 2 | time zone offset in minutes as a 16-bit big-endian signed integer. 15 | 1 | time zone offset in seconds (only for version 2) A value of -1 is a special value when the Time instance is initialised as UTC (e.g. ```time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC)```). ### Also see * [Golang time.go](https://cs.opensource.google/go/go/+/master:src/time/time.go) ## HFS timestamp Sometimes a distinction is made between HFS and HFS+ timestamps is that the former is stored in local time and the latter in UTC. Note that this behavior is highly depending on the context in which the timestamp is used. ### Characteristics Attribute | Description --- | --- Supported date range | 1904-01-01 00:00:00 through 2040-02-06 06:28:15 Storage granularity | 1 second Time zone | externally represented ### Format Offset | Size | Description --- | --- | --- 0 | 4 | timestamp, integer value containing the number of seconds since 1904-01-01 00:00:00 An empty (or unset) HFS timestamp can be represented by 0, however the meaning can differ depending on the context in which the timestamp is used. ### Also see * [Wikipedia: HFS Plus](https://en.wikipedia.org/wiki/HFS_Plus) * [Technical Note TN1150 - HFS Plus Volume Format](https://developer.apple.com/library/archive/technotes/tn/tn1150.html), see section "HFS Plus Dates" ## Java timestamp ### Characteristics Attribute | Description --- | --- Supported date range | ... Storage granularity | 1 millisecond Time zone | externally represented ### Format Offset | Size | Description --- | --- | --- 0 | 8 | timestamp, integer value containing the number of milliseconds before (when negative) or after (when positive) 1970-01-01 00:00:00.000 (or POSIX or Unix epoch) ### Also see * [Class java.util.Date](https://docs.oracle.com/javase/8/docs/api/java/util/Date.html) ## OLE Automated date The OLE Automated date is also known as Floatingtime or Application time. ### Characteristics Attribute | Description --- | --- Supported date range | ... Storage granularity | 1 day with fragment Time zone | externally represented ### Format Offset | Size | Description --- | --- | --- 0 | 8 | timestamp, floating-point value containing the number of days before (when negative) or after (when positive) 1889-12-30. The fractional part represents the fraction of a day since midnight. ## POSIX timestamp There multiple variants of the POSIX timestamp: * the `time_t` has different sizes and signs on different platforms * variants exist that store the timestamp in milliseconds (Java), microsecond and nanoseconds (APFS) precision instead of second precision ### Characteristics Attribute | Description --- | --- Supported date range | 1901-12-13 20:45:52 through 2038-01-19 03:14:07 (32-bit) Storage granularity | 1 second Time zone | externally represented ### Format Offset | Size | Description --- | --- | --- 0 | 4 or 8 | timestamp, integer value containing the number of seconds before (when negative) or after (when positive) 1970-01-01 00:00:00 (or POSIX or Unix epoch) An empty (or unset) POSIX timestamp can be represented by 0, however the meaning can differ depending on the context in which the timestamp is used. ### Also see * [Wikipedia: UNIX time](https://en.wikipedia.org/wiki/Unix_time) ## RFC2579 date-time ### Characteristics Attribute | Description --- | --- Supported date range | 0-01-01 00:00:00.0 through 65536-12-31 23:59:59.9 Storage granularity | 1 decisecond (100 milliseconds) Time zone | internally represented as [+-]hh:mm from UTC ### Format The RFC2579 date-time structure is 11 bytes of size and consists of: Offset | Size | Description --- | --- | --- 0 | 2 | year, ranging from 0 through 65536 2 | 1 | month, where January is represented by 1 3 | 1 | day of month, where 1 is the first day of the month 4 | 1 | hours, ranging from 0 through 23 5 | 1 | minutes, ranging from 0 through 59 6 | 1 | seconds, ranging from 0 through 59 7 | 1 | deciseconds, ranging from 0 through 9 8 | 1 | direction from UTC, "+" or "-" 9 | 1 | hours from UTC, ranging from 0 through 13 10 | 1 | minutes from UTC, ranging from 0 through 59 ### Also see * [RFC2579](https://datatracker.ietf.org/doc/html/rfc2579) ## SYSTEMTIME ### Characteristics Attribute | Description --- | --- Supported date range | 1601-01-01 00:00:00.000 through 30827-12-31 23:59:59.999 Storage granularity | 1 millisecond Time zone | externally represented ### Format The SYSTEMTIME structure is 16 bytes of size and consists of: Offset | Size | Description --- | --- | --- 0 | 2 | year, ranging from 1601 through 30827 2 | 2 | month, where January is represented by 1 4 | 2 | day of week, staring with Sunday represented by 0 6 | 2 | day of month, where 1 is the first day of the month 8 | 2 | hours, ranging from 0 through 23 10 | 2 | minutes, ranging from 0 through 59 12 | 2 | seconds, ranging from 0 through 59 14 | 2 | milliseconds, ranging from 0 through 999 An empty (or unset) SYSTEMTIME can be represented by 16x 0-byte values. ### Also see * [MSDN: SYSTEMTIME](https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime) ## Time element strings ### ISO 8601 / RFC 3339 Example `2020-05-31T00:00:47.044800+00:00` ### RFC 822 Example `Tue, 15 Nov 94 08:12:31 GMT` ### RFC 1123 Example `Tue, 15 Nov 1994 08:12:31 GMT` ### RFC 2822 ### Also see * [RFC 822 - Date and time specification](https://datatracker.ietf.org/doc/html/rfc822#section-5) * [RFC 1123 - RFC-822 date and time specification](https://datatracker.ietf.org/doc/html/rfc1123#section-5) * [RFC 2822 - Date and time specification](https://datatracker.ietf.org/doc/html/rfc2822#section-3.3) * [RFC 3339 - Internet date/time format](https://datatracker.ietf.org/doc/html/rfc3339#section-5.6) ## UUID version 1 time ### Characteristics Attribute | Description --- | --- Supported date range | 1582-10-15 00:00:00.0000000 through ... Storage granularity | 100-nanosecond intervals Time zone | externally represented, typically UTC ### Format Offset | Size | Description --- | --- | --- 0 | 60-bits | integer value, containing the number of 100-nanosecond intervals since 1582-10-15 00:00:00 7.4 | 4-bits | version 8.0 | 16-bits | UUID version (variant) and clock sequence 10.0 | 48-bits | node identifier (typically a MAC address in UUID version 1) ### Also see * [Wikipedia: Universally unique identifier](https://en.wikipedia.org/wiki/Universally_unique_identifier) ## WebKit timestamp ### Characteristics Attribute | Description --- | --- Supported date range | ... Storage granularity | 1 microsecond Time zone | externally represented, typically UTC ### Format Offset | Size | Description --- | --- | --- 0 | 8 | timestamp, signed integer value containing the number of microseconds before (when negative) or after (when positive) 1601-01-01 00:00:00.000000 ### Also see * [Chromium source: time.h](https://chromium.googlesource.com/chromium/src/base/+/refs/heads/main/time/time.h#5) dfdatetime-20240504/docs/sources/api/000077500000000000000000000000001461534226200172535ustar00rootroot00000000000000dfdatetime-20240504/docs/sources/api/dfdatetime.rst000066400000000000000000000075511461534226200221230ustar00rootroot00000000000000dfdatetime package ================== Submodules ---------- dfdatetime.apfs\_time module ---------------------------- .. automodule:: dfdatetime.apfs_time :members: :undoc-members: :show-inheritance: dfdatetime.cocoa\_time module ----------------------------- .. automodule:: dfdatetime.cocoa_time :members: :undoc-members: :show-inheritance: dfdatetime.decorators module ---------------------------- .. automodule:: dfdatetime.decorators :members: :undoc-members: :show-inheritance: dfdatetime.definitions module ----------------------------- .. automodule:: dfdatetime.definitions :members: :undoc-members: :show-inheritance: dfdatetime.delphi\_date\_time module ------------------------------------ .. automodule:: dfdatetime.delphi_date_time :members: :undoc-members: :show-inheritance: dfdatetime.dotnet\_datetime module ---------------------------------- .. automodule:: dfdatetime.dotnet_datetime :members: :undoc-members: :show-inheritance: dfdatetime.factory module ------------------------- .. automodule:: dfdatetime.factory :members: :undoc-members: :show-inheritance: dfdatetime.fake\_time module ---------------------------- .. automodule:: dfdatetime.fake_time :members: :undoc-members: :show-inheritance: dfdatetime.fat\_date\_time module --------------------------------- .. automodule:: dfdatetime.fat_date_time :members: :undoc-members: :show-inheritance: dfdatetime.filetime module -------------------------- .. automodule:: dfdatetime.filetime :members: :undoc-members: :show-inheritance: dfdatetime.golang\_time module ------------------------------ .. automodule:: dfdatetime.golang_time :members: :undoc-members: :show-inheritance: dfdatetime.hfs\_time module --------------------------- .. automodule:: dfdatetime.hfs_time :members: :undoc-members: :show-inheritance: dfdatetime.interface module --------------------------- .. automodule:: dfdatetime.interface :members: :undoc-members: :show-inheritance: dfdatetime.java\_time module ---------------------------- .. automodule:: dfdatetime.java_time :members: :undoc-members: :show-inheritance: dfdatetime.ole\_automation\_date module --------------------------------------- .. automodule:: dfdatetime.ole_automation_date :members: :undoc-members: :show-inheritance: dfdatetime.posix\_time module ----------------------------- .. automodule:: dfdatetime.posix_time :members: :undoc-members: :show-inheritance: dfdatetime.precisions module ---------------------------- .. automodule:: dfdatetime.precisions :members: :undoc-members: :show-inheritance: dfdatetime.rfc2579\_date\_time module ------------------------------------- .. automodule:: dfdatetime.rfc2579_date_time :members: :undoc-members: :show-inheritance: dfdatetime.semantic\_time module -------------------------------- .. automodule:: dfdatetime.semantic_time :members: :undoc-members: :show-inheritance: dfdatetime.serializer module ---------------------------- .. automodule:: dfdatetime.serializer :members: :undoc-members: :show-inheritance: dfdatetime.systemtime module ---------------------------- .. automodule:: dfdatetime.systemtime :members: :undoc-members: :show-inheritance: dfdatetime.time\_elements module -------------------------------- .. automodule:: dfdatetime.time_elements :members: :undoc-members: :show-inheritance: dfdatetime.uuid\_time module ---------------------------- .. automodule:: dfdatetime.uuid_time :members: :undoc-members: :show-inheritance: dfdatetime.webkit\_time module ------------------------------ .. automodule:: dfdatetime.webkit_time :members: :undoc-members: :show-inheritance: Module contents --------------- .. automodule:: dfdatetime :members: :undoc-members: :show-inheritance: dfdatetime-20240504/docs/sources/api/modules.rst000066400000000000000000000001031461534226200214470ustar00rootroot00000000000000dfdatetime ========== .. toctree:: :maxdepth: 4 dfdatetime dfdatetime-20240504/docs/sources/user/000077500000000000000000000000001461534226200174605ustar00rootroot00000000000000dfdatetime-20240504/docs/sources/user/Installation-instructions.md000066400000000000000000000031761461534226200252140ustar00rootroot00000000000000# Installation instructions ## pip **Note that using pip outside virtualenv is not recommended since it ignores your systems package manager. If you aren't comfortable debugging package installation issues, this is not the option for you.** Create and activate a virtualenv: ```bash virtualenv dfdatetimeenv cd dfdatetimeenv source ./bin/activate ``` Upgrade pip and install dfDateTime dependencies: ```bash pip install --upgrade pip pip install dfdatetime ``` To deactivate the virtualenv run: ```bash deactivate ``` ## Ubuntu 22.04 LTS To install dfDateTime from the [GIFT Personal Package Archive (PPA)](https://launchpad.net/~gift): ```bash sudo add-apt-repository ppa:gift/stable ``` Update and install dfDateTime: ```bash sudo apt-get update sudo apt-get install python3-dfdatetime ``` ## Windows The [l2tbinaries](https://github.com/log2timeline/l2tbinaries) contains the necessary packages for running dfDateTime. l2tbinaries provides the following branches: * main; branch intended for the "packaged release" of dfDateTime and dependencies; * staging; branch intended for testing pre-releases of dfDateTime; * dev; branch intended for the "development release" of dfDateTime; * testing; branch intended for testing newly created packages. The l2tdevtools project provides [an update script](https://github.com/log2timeline/l2tdevtools/wiki/Update-script) to ease the process of keeping the dependencies up to date. The script requires [pywin32](https://github.com/mhammond/pywin32/releases). To install the release versions of the dependencies run: ``` set PYTHONPATH=. C:\Python3\python.exe tools\update.py --preset dfdatetime ``` dfdatetime-20240504/docs/sources/user/index.rst000066400000000000000000000004441461534226200213230ustar00rootroot00000000000000############### Getting started ############### To be able to use dfDateTime you first need to install it. There are multiple ways to install dfDateTime, check the following instructions for more detail. .. toctree:: :maxdepth: 2 Installation instructions dfdatetime-20240504/pyproject.toml000066400000000000000000000004501461534226200170020ustar00rootroot00000000000000[build-system] requires = ["setuptools", "wheel"] build-backend = "setuptools.build_meta" [tool.docformatter] black = false non-cap = ["dfDateTime", "dfImageTools", "dfVFS", "dfWinReg", "dtFabric", "iMessage", "iOS", "iPod", "mDNS"] non-strict = false wrap-summaries = 80 wrap-descriptions = 80 dfdatetime-20240504/requirements.txt000066400000000000000000000000001461534226200173410ustar00rootroot00000000000000dfdatetime-20240504/run_tests.py000077500000000000000000000017721461534226200165010ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Script to run the tests.""" from __future__ import print_function import sys import unittest # Change PYTHONPATH to include dependencies. sys.path.insert(0, '.') import utils.dependencies # pylint: disable=wrong-import-position if __name__ == '__main__': print(f'Using Python version {sys.version!s}') fail_unless_has_test_file = '--fail-unless-has-test-file' in sys.argv setattr(unittest, 'fail_unless_has_test_file', fail_unless_has_test_file) if fail_unless_has_test_file: # Remove --fail-unless-has-test-file otherwise it will conflict with # the argparse tests. sys.argv.remove('--fail-unless-has-test-file') dependency_helper = utils.dependencies.DependencyHelper() if not dependency_helper.CheckTestDependencies(): sys.exit(1) test_suite = unittest.TestLoader().discover('tests', pattern='*.py') test_results = unittest.TextTestRunner(verbosity=2).run(test_suite) if not test_results.wasSuccessful(): sys.exit(1) dfdatetime-20240504/setup.cfg000066400000000000000000000015301461534226200157070ustar00rootroot00000000000000[metadata] name = dfdatetime version = 20240504 description = Digital Forensics date and time (dfDateTime). long_description = dfDateTime, or Digital Forensics date and time, provides date and time objects to preserve accuracy and precision. long_description_content_type = text/plain url = https://github.com/log2timeline/dfdatetime maintainer = Log2Timeline maintainers maintainer_email = log2timeline-maintainers@googlegroups.com license = Apache License, Version 2.0 license_files = ACKNOWLEDGEMENTS AUTHORS LICENSE README classifiers = Development Status :: 3 - Alpha Programming Language :: Python [options] install_requires = file:requirements.txt package_dir = dfdatetime = dfdatetime packages = find: python_requires = >=3.8 [options.packages.find] exclude = docs tests tests.* utils where = . [bdist_wheel] universal = 1 dfdatetime-20240504/setup.py000077500000000000000000000002001461534226200155740ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Installation and deployment script.""" from setuptools import setup setup() dfdatetime-20240504/test_dependencies.ini000066400000000000000000000000001461534226200202430ustar00rootroot00000000000000dfdatetime-20240504/test_requirements.txt000066400000000000000000000000001461534226200204000ustar00rootroot00000000000000dfdatetime-20240504/tests/000077500000000000000000000000001461534226200152315ustar00rootroot00000000000000dfdatetime-20240504/tests/__init__.py000066400000000000000000000000301461534226200173330ustar00rootroot00000000000000# -*- coding: utf-8 -*- dfdatetime-20240504/tests/apfs_time.py000066400000000000000000000042531461534226200175560ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the APFS timestamp implementation.""" import decimal import unittest from dfdatetime import apfs_time class APFSTimeTest(unittest.TestCase): """Tests for the APFS timestamp.""" # pylint: disable=protected-access def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" apfs_time_object = apfs_time.APFSTime(timestamp=1281643591987654321) normalized_timestamp = apfs_time_object._GetNormalizedTimestamp() self.assertEqual( normalized_timestamp, decimal.Decimal('1281643591.987654321')) apfs_time_object = apfs_time.APFSTime( time_zone_offset=60, timestamp=1281643591987654321) normalized_timestamp = apfs_time_object._GetNormalizedTimestamp() self.assertEqual( normalized_timestamp, decimal.Decimal('1281639991.987654321')) apfs_time_object = apfs_time.APFSTime(timestamp=1281643591987654321) apfs_time_object.time_zone_offset = 60 normalized_timestamp = apfs_time_object._GetNormalizedTimestamp() self.assertEqual( normalized_timestamp, decimal.Decimal('1281639991.987654321')) apfs_time_object = apfs_time.APFSTime() normalized_timestamp = apfs_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) apfs_time_object = apfs_time.APFSTime(timestamp=9223372036854775810) date_time_string = apfs_time_object._GetNormalizedTimestamp() self.assertIsNone(date_time_string) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" apfs_time_object = apfs_time.APFSTime() with self.assertRaises(ValueError): apfs_time_object.CopyFromDateTimeString('2554-07-21 23:34:34.000000') def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" apfs_time_object = apfs_time.APFSTime(timestamp=9223372036854775810) date_time_string = apfs_time_object.CopyToDateTimeString() self.assertIsNone(date_time_string) apfs_time_object = apfs_time.APFSTime() date_time_string = apfs_time_object.CopyToDateTimeString() self.assertIsNone(date_time_string) if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/cocoa_time.py000066400000000000000000000136451461534226200177160ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the Cocoa time implementation.""" import decimal import unittest from dfdatetime import cocoa_time class CocoaTimeEpochTest(unittest.TestCase): """Tests for the Cocoa time epoch.""" def testInitialize(self): """Tests the __init__ function.""" cocoa_time_epoch = cocoa_time.CocoaTimeEpoch() self.assertIsNotNone(cocoa_time_epoch) class CocoaTimeTest(unittest.TestCase): """Tests for the Cocoa timestamp.""" # pylint: disable=protected-access def testProperties(self): """Tests the properties.""" cocoa_time_object = cocoa_time.CocoaTime(timestamp=395011845.0) self.assertEqual(cocoa_time_object.timestamp, 395011845.0) cocoa_time_object = cocoa_time.CocoaTime() self.assertIsNone(cocoa_time_object.timestamp) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" cocoa_time_object = cocoa_time.CocoaTime(timestamp=395011845.0) normalized_timestamp = cocoa_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1373319045.0')) cocoa_time_object = cocoa_time.CocoaTime( time_zone_offset=60, timestamp=395011845.0) normalized_timestamp = cocoa_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1373315445.0')) cocoa_time_object = cocoa_time.CocoaTime(timestamp=395011845.0) cocoa_time_object.time_zone_offset = 60 normalized_timestamp = cocoa_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1373315445.0')) cocoa_time_object = cocoa_time.CocoaTime() normalized_timestamp = cocoa_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" cocoa_time_object = cocoa_time.CocoaTime() cocoa_time_object.CopyFromDateTimeString('2013-07-08') self.assertEqual(cocoa_time_object._timestamp, 394934400.0) self.assertEqual(cocoa_time_object._time_zone_offset, None) cocoa_time_object.CopyFromDateTimeString('2013-07-08 21:30:45') self.assertEqual(cocoa_time_object._timestamp, 395011845.0) self.assertEqual(cocoa_time_object._time_zone_offset, None) cocoa_time_object.CopyFromDateTimeString('2013-07-08 21:30:45.546875') self.assertEqual(cocoa_time_object._timestamp, 395011845.546875) self.assertEqual(cocoa_time_object._time_zone_offset, None) cocoa_time_object.CopyFromDateTimeString('2013-07-08 21:30:45.546875-01:00') self.assertEqual(cocoa_time_object._timestamp, 395011845.546875) self.assertEqual(cocoa_time_object._time_zone_offset, -60) cocoa_time_object.CopyFromDateTimeString('2013-07-08 21:30:45.546875+01:00') self.assertEqual(cocoa_time_object._timestamp, 395011845.546875) self.assertEqual(cocoa_time_object._time_zone_offset, 60) cocoa_time_object.CopyFromDateTimeString('2001-01-02 00:00:00') self.assertEqual(cocoa_time_object._timestamp, 86400.0) self.assertEqual(cocoa_time_object._time_zone_offset, None) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" cocoa_time_object = cocoa_time.CocoaTime(timestamp=395011845.546875) date_time_string = cocoa_time_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2013-07-08 21:30:45.546875') epoch_year = cocoa_time_object._EPOCH.year cocoa_time_object._EPOCH.year = -1 with self.assertRaises(ValueError): cocoa_time_object.CopyToDateTimeString() cocoa_time_object._EPOCH.year = epoch_year cocoa_time_object = cocoa_time.CocoaTime() date_time_string = cocoa_time_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" cocoa_time_object = cocoa_time.CocoaTime(timestamp=395011845.546875) date_time_string = cocoa_time_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2013-07-08T21:30:45.546875+00:00') def testGetDate(self): """Tests the GetDate function.""" cocoa_time_object = cocoa_time.CocoaTime(timestamp=395011845.546875) date_tuple = cocoa_time_object.GetDate() self.assertEqual(date_tuple, (2013, 7, 8)) cocoa_time_object = cocoa_time.CocoaTime() date_tuple = cocoa_time_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" cocoa_time_object = cocoa_time.CocoaTime(timestamp=395011845.546875) date_with_time_of_day_tuple = cocoa_time_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2013, 7, 8, 21, 30, 45)) cocoa_time_object = cocoa_time.CocoaTime() date_with_time_of_day_tuple = cocoa_time_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) # TODO: remove this method when there is no more need for it in Plaso. def testGetPlasoTimestamp(self): """Tests the GetPlasoTimestamp function.""" cocoa_time_object = cocoa_time.CocoaTime(timestamp=395011845.0) micro_posix_timestamp = cocoa_time_object.GetPlasoTimestamp() self.assertEqual(micro_posix_timestamp, 1373319045000000) cocoa_time_object = cocoa_time.CocoaTime() micro_posix_timestamp = cocoa_time_object.GetPlasoTimestamp() self.assertIsNone(micro_posix_timestamp) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" cocoa_time_object = cocoa_time.CocoaTime(timestamp=395011845.546875) time_of_day_tuple = cocoa_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (21, 30, 45)) cocoa_time_object = cocoa_time.CocoaTime() time_of_day_tuple = cocoa_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/delphi_date_time.py000066400000000000000000000201741461534226200210670ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the Delphi TDateTime implementation.""" import decimal import unittest from dfdatetime import delphi_date_time class DelphiDateTimeEpochTest(unittest.TestCase): """Tests for the Delphi TDateTime epoch.""" def testInitialize(self): """Tests the __init__ function.""" delphi_date_time_epoch = delphi_date_time.DelphiDateTimeEpoch() self.assertIsNotNone(delphi_date_time_epoch) class DelphiDateTimeInvalidYear(delphi_date_time.DelphiDateTime): """Delphi TDateTime timestamp for testing invalid year.""" def _CopyDateTimeFromString(self, time_string): """Copies a date and time from a string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3 or 6 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Returns: dict[str, int]: date and time values, such as year, month, day of month, hours, minutes, seconds, microseconds. Raises: ValueError: if the time string is invalid or not supported. """ return { 'year': 10000, 'month': 1, 'day_of_month': 2, 'hours': 0, 'minutes': 0, 'seconds': 0} class DelphiDateTimeTest(unittest.TestCase): """Tests for the Delphi TDateTime timestamp.""" # pylint: disable=protected-access def testProperties(self): """Tests the properties.""" delphi_date_time_object = delphi_date_time.DelphiDateTime( timestamp=41443.8263953) self.assertEqual(delphi_date_time_object.timestamp, 41443.8263953) delphi_date_time_object = delphi_date_time.DelphiDateTime() self.assertIsNone(delphi_date_time_object.timestamp) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" delphi_date_time_object = delphi_date_time.DelphiDateTime( timestamp=41443.8263953) expected_normalized_timestamp = decimal.Decimal( '1371585000.553919887170195579') normalized_timestamp = delphi_date_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, expected_normalized_timestamp) delphi_date_time_object = delphi_date_time.DelphiDateTime( time_zone_offset=60, timestamp=41443.8263953) expected_normalized_timestamp = decimal.Decimal( '1371581400.553919887170195579') normalized_timestamp = delphi_date_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, expected_normalized_timestamp) delphi_date_time_object = delphi_date_time.DelphiDateTime( timestamp=41443.8263953) delphi_date_time_object.time_zone_offset = 60 expected_normalized_timestamp = decimal.Decimal( '1371581400.553919887170195579') normalized_timestamp = delphi_date_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, expected_normalized_timestamp) delphi_date_time_object = delphi_date_time.DelphiDateTime() normalized_timestamp = delphi_date_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" delphi_date_time_object = delphi_date_time.DelphiDateTime() delphi_date_time_object.CopyFromDateTimeString('2013-06-18') self.assertEqual(delphi_date_time_object._timestamp, 41443.0) self.assertEqual(delphi_date_time_object._time_zone_offset, None) delphi_date_time_object.CopyFromDateTimeString('2013-06-18 19:50:00') self.assertEqual(delphi_date_time_object._timestamp, 41443.82638888889) self.assertEqual(delphi_date_time_object._time_zone_offset, None) delphi_date_time_object.CopyFromDateTimeString('2013-06-18 19:50:00.546875') self.assertEqual(delphi_date_time_object._timestamp, 41443.826395218464) self.assertEqual(delphi_date_time_object._time_zone_offset, None) delphi_date_time_object.CopyFromDateTimeString( '2013-06-18 19:50:00.546875-01:00') self.assertEqual(delphi_date_time_object._timestamp, 41443.826395218464) self.assertEqual(delphi_date_time_object._time_zone_offset, -60) delphi_date_time_object.CopyFromDateTimeString( '2013-06-18 19:50:00.546875+01:00') self.assertEqual(delphi_date_time_object._timestamp, 41443.826395218464) self.assertEqual(delphi_date_time_object._time_zone_offset, 60) delphi_date_time_object.CopyFromDateTimeString('1899-12-31 00:00:00') self.assertEqual(delphi_date_time_object._timestamp, 1.0) self.assertEqual(delphi_date_time_object._time_zone_offset, None) delphi_date_time_object = DelphiDateTimeInvalidYear() with self.assertRaises(ValueError): delphi_date_time_object.CopyFromDateTimeString('9999-01-02 00:00:00') def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" delphi_date_time_object = delphi_date_time.DelphiDateTime( timestamp=41443.8263953) date_time_string = delphi_date_time_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2013-06-18 19:50:00.553919') delphi_date_time_object = delphi_date_time.DelphiDateTime() date_time_string = delphi_date_time_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" delphi_date_time_object = delphi_date_time.DelphiDateTime( timestamp=41443.8263953) date_time_string = delphi_date_time_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2013-06-18T19:50:00.553919+00:00') delphi_date_time_object = delphi_date_time.DelphiDateTime( timestamp=8.0e+174) date_time_string = delphi_date_time_object.CopyToDateTimeStringISO8601() self.assertIsNone(date_time_string) def testGetDate(self): """Tests the GetDate function.""" delphi_date_time_object = delphi_date_time.DelphiDateTime( timestamp=41443.8263953) date_tuple = delphi_date_time_object.GetDate() self.assertEqual(date_tuple, (2013, 6, 18)) delphi_date_time_object = delphi_date_time.DelphiDateTime() date_tuple = delphi_date_time_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" delphi_date_time_object = delphi_date_time.DelphiDateTime( timestamp=41443.8263953) date_with_time_of_day_tuple = delphi_date_time_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2013, 6, 18, 19, 50, 0)) delphi_date_time_object = delphi_date_time.DelphiDateTime() date_with_time_of_day_tuple = delphi_date_time_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) # TODO: remove this method when there is no more need for it in Plaso. def testGetPlasoTimestamp(self): """Tests the GetPlasoTimestamp function.""" delphi_date_time_object = delphi_date_time.DelphiDateTime( timestamp=41443.8263953) micro_posix_timestamp = delphi_date_time_object.GetPlasoTimestamp() self.assertEqual(micro_posix_timestamp, 1371585000553920) delphi_date_time_object = delphi_date_time.DelphiDateTime() micro_posix_timestamp = delphi_date_time_object.GetPlasoTimestamp() self.assertIsNone(micro_posix_timestamp) delphi_date_time_object = delphi_date_time.DelphiDateTime( timestamp=8.0e+174) with self.assertRaises(ValueError): delphi_date_time_object.GetPlasoTimestamp() def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" delphi_date_time_object = delphi_date_time.DelphiDateTime( timestamp=41443.8263953) time_of_day_tuple = delphi_date_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (19, 50, 0)) delphi_date_time_object = delphi_date_time.DelphiDateTime() time_of_day_tuple = delphi_date_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/dotnet_datetime.py000066400000000000000000000070071461534226200207600ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the .NET DateTime implementation.""" import decimal import unittest from dfdatetime import dotnet_datetime class DotNetDateTimeEpochTest(unittest.TestCase): """Tests for the .NET DateTime epoch.""" def testInitialize(self): """Tests the __init__ function.""" dotnet_date_time_epoch = dotnet_datetime.DotNetDateTimeEpoch() self.assertIsNotNone(dotnet_date_time_epoch) class DotNetDateTimeTest(unittest.TestCase): """Tests for the ,NET DateTime timestamp.""" # pylint: disable=protected-access def testProperties(self): """Tests the properties.""" dotnet_date_time = dotnet_datetime.DotNetDateTime() self.assertEqual(dotnet_date_time.timestamp, 0) dotnet_date_time = dotnet_datetime.DotNetDateTime( timestamp=637751130027210000) self.assertEqual(dotnet_date_time.timestamp, 637751130027210000) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" dotnet_date_time = dotnet_datetime.DotNetDateTime( timestamp=637433719321230000) expected_normalized_timestamp = decimal.Decimal(1607775132123) / 1000 normalized_timestamp = dotnet_date_time._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, expected_normalized_timestamp) dotnet_date_time = dotnet_datetime.DotNetDateTime( time_zone_offset=60, timestamp=637433719321230000) expected_normalized_timestamp = decimal.Decimal(1607771532123) / 1000 normalized_timestamp = dotnet_date_time._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, expected_normalized_timestamp) dotnet_date_time = dotnet_datetime.DotNetDateTime( timestamp=637433719321230000) dotnet_date_time.time_zone_offset = 60 expected_normalized_timestamp = decimal.Decimal(1607771532123) / 1000 normalized_timestamp = dotnet_date_time._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, expected_normalized_timestamp) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" dotnet_date_time = dotnet_datetime.DotNetDateTime() dotnet_date_time.CopyFromDateTimeString('2020-12-12') self.assertEqual(dotnet_date_time._timestamp, 637433280000000000) self.assertEqual(dotnet_date_time._time_zone_offset, None) dotnet_date_time.CopyFromDateTimeString('2020-12-12 12:12:12') self.assertEqual(dotnet_date_time._timestamp, 637433719320000000) self.assertEqual(dotnet_date_time._time_zone_offset, None) dotnet_date_time.CopyFromDateTimeString('2020-12-12 12:12:12.123') self.assertEqual(dotnet_date_time._timestamp, 637433719321230000) self.assertEqual(dotnet_date_time._time_zone_offset, None) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" dotnet_date_time = dotnet_datetime.DotNetDateTime( timestamp=637433280000000000) dotnet_date_string = dotnet_date_time.CopyToDateTimeString() self.assertEqual(dotnet_date_string, '2020-12-12 00:00:00.0000000') dotnet_date_time = dotnet_datetime.DotNetDateTime( timestamp=637433719320000000) dotnet_date_string = dotnet_date_time.CopyToDateTimeString() self.assertEqual(dotnet_date_string, '2020-12-12 12:12:12.0000000') dotnet_date_time = dotnet_datetime.DotNetDateTime( timestamp=637433719321230000) dotnet_date_string = dotnet_date_time.CopyToDateTimeString() self.assertEqual(dotnet_date_string, '2020-12-12 12:12:12.1230000') if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/factory.py000066400000000000000000000051261461534226200172560ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the date and time values factory.""" import unittest from dfdatetime import interface from dfdatetime import factory class TestDateTimeValues(interface.DateTimeValues): """Date and time values for testing.""" # pylint: disable=redundant-returns-doc def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: decimal.Decimal: normalized timestamp, which contains the number of seconds since January 1, 1970 00:00:00 and a fraction of second used for increased precision, or None if the normalized timestamp cannot be determined. """ return None def CopyFromDateTimeString(self, time_string): """Copies a date time value from a date and time string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3 or 6 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Raises: ValueError: if the time string is invalid or not supported. """ return def CopyToDateTimeString(self): """Copies the date time value to a date and time string. Returns: str: date and time value formatted as: "YYYY-MM-DD hh:mm:ss.######" or None if the timestamp cannot be copied to a date and time string. """ return None class FactoryTest(unittest.TestCase): """Tests the date and time values factory.""" def testDateTimeValuesRegistration(self): """Tests the Register and DeregisterDateTimeValues functions.""" # pylint: disable=protected-access number_of_date_time_values_types = len( factory.Factory._date_time_values_types) factory.Factory.RegisterDateTimeValues(TestDateTimeValues) self.assertEqual( len(factory.Factory._date_time_values_types), number_of_date_time_values_types + 1) with self.assertRaises(KeyError): factory.Factory.RegisterDateTimeValues(TestDateTimeValues) factory.Factory.DeregisterDateTimeValues(TestDateTimeValues) self.assertEqual( len(factory.Factory._date_time_values_types), number_of_date_time_values_types) def testNewDateTimeValues(self): """Tests the NewDateTimeValues function.""" test_date_time_values = factory.Factory.NewDateTimeValues( 'Filetime', timestamp=0x01cb3a623d0a17ce) self.assertIsNotNone(test_date_time_values) if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/fake_time.py000066400000000000000000000136171461534226200175370ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the fake time implementation.""" import decimal import unittest from dfdatetime import fake_time class FakeTimeTest(unittest.TestCase): """Tests for the fake time.""" # pylint: disable=protected-access def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" fake_time_object = fake_time.FakeTime() fake_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') normalized_timestamp = fake_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281647191.546875')) fake_time_object = fake_time.FakeTime() fake_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875+01:00') normalized_timestamp = fake_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643591.546875')) fake_time_object = fake_time.FakeTime() fake_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') fake_time_object.time_zone_offset = 60 normalized_timestamp = fake_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643591.546875')) fake_time_object = fake_time.FakeTime() fake_time_object._number_of_seconds = None normalized_timestamp = fake_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) def testGetPlasoTimestamp(self): """Tests the GetPlasoTimestamp function.""" fake_time_object = fake_time.FakeTime() fake_time_object._normalized_timestamp = decimal.Decimal( '1333794697.6252465') plaso_timestamp = fake_time_object.GetPlasoTimestamp() self.assertEqual(plaso_timestamp, 1333794697625247) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" fake_time_object = fake_time.FakeTime() fake_time_object.CopyFromDateTimeString('2010-08-12') self.assertEqual(fake_time_object._number_of_seconds, 1281571200) self.assertIsNone(fake_time_object._microseconds) self.assertEqual(fake_time_object._time_zone_offset, None) fake_time_object.CopyFromDateTimeString('2010-08-12 21:06:31') self.assertEqual(fake_time_object._number_of_seconds, 1281647191) self.assertIsNone(fake_time_object._microseconds) self.assertEqual(fake_time_object._time_zone_offset, None) fake_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') self.assertEqual(fake_time_object._number_of_seconds, 1281647191) self.assertEqual(fake_time_object._microseconds, 546875) self.assertEqual(fake_time_object._time_zone_offset, None) fake_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875-01:00') self.assertEqual(fake_time_object._number_of_seconds, 1281647191) self.assertEqual(fake_time_object._microseconds, 546875) self.assertEqual(fake_time_object._time_zone_offset, -60) fake_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875+01:00') self.assertEqual(fake_time_object._number_of_seconds, 1281647191) self.assertEqual(fake_time_object._microseconds, 546875) self.assertEqual(fake_time_object._time_zone_offset, 60) fake_time_object.CopyFromDateTimeString('1601-01-02 00:00:00') self.assertEqual(fake_time_object._number_of_seconds, -11644387200) self.assertIsNone(fake_time_object._microseconds) self.assertEqual(fake_time_object._time_zone_offset, None) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" fake_time_object = fake_time.FakeTime() fake_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') date_time_string = fake_time_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2010-08-12 21:06:31.546875') fake_time_object = fake_time.FakeTime() fake_time_object._number_of_seconds = None date_time_string = fake_time_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" fake_time_object = fake_time.FakeTime() fake_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') date_time_string = fake_time_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2010-08-12T21:06:31.546875+00:00') def testGetDate(self): """Tests the GetDate function.""" fake_time_object = fake_time.FakeTime() fake_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') date_tuple = fake_time_object.GetDate() self.assertEqual(date_tuple, (2010, 8, 12)) fake_time_object = fake_time.FakeTime() fake_time_object._number_of_seconds = None date_tuple = fake_time_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" fake_time_object = fake_time.FakeTime() fake_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') date_with_time_of_day_tuple = fake_time_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2010, 8, 12, 21, 6, 31)) fake_time_object = fake_time.FakeTime() fake_time_object._number_of_seconds = None date_with_time_of_day_tuple = fake_time_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" fake_time_object = fake_time.FakeTime() fake_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') time_of_day_tuple = fake_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (21, 6, 31)) fake_time_object = fake_time.FakeTime() fake_time_object._number_of_seconds = None time_of_day_tuple = fake_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/fat_date_time.py000066400000000000000000000277221461534226200204020ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the FAT date time implementation.""" import decimal import unittest from dfdatetime import fat_date_time class FATDateTimeEpochTest(unittest.TestCase): """Tests for the FAT date time epoch.""" def testInitialize(self): """Tests the __init__ function.""" fat_date_time_epoch = fat_date_time.FATDateTimeEpoch() self.assertIsNotNone(fat_date_time_epoch) class FATDateTime(unittest.TestCase): """Tests for the FAT date time.""" # pylint: disable=protected-access def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" fat_date_time_object = fat_date_time.FATDateTime(fat_date_time=0xa8d03d0c) normalized_timestamp = fat_date_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281647192.0')) fat_date_time_object = fat_date_time.FATDateTime( fat_date_time=0xa8d03d0c, time_zone_offset=60) normalized_timestamp = fat_date_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643592.0')) fat_date_time_object = fat_date_time.FATDateTime(fat_date_time=0xa8d03d0c) fat_date_time_object.time_zone_offset = 60 normalized_timestamp = fat_date_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643592.0')) fat_date_time_object = fat_date_time.FATDateTime() normalized_timestamp = fat_date_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" fat_date_time_object = fat_date_time.FATDateTime() fat_date_time_object.CopyFromDateTimeString('2010-08-12') self.assertEqual(fat_date_time_object._number_of_seconds, 966038400) self.assertEqual(fat_date_time_object._time_zone_offset, None) fat_date_time_object.CopyFromDateTimeString('2010-08-12 21:06:31') self.assertEqual(fat_date_time_object._number_of_seconds, 966114391) self.assertEqual(fat_date_time_object._time_zone_offset, None) fat_date_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') self.assertEqual(fat_date_time_object._number_of_seconds, 966114391) self.assertEqual(fat_date_time_object._time_zone_offset, None) fat_date_time_object.CopyFromDateTimeString( '2010-08-12 21:06:31.546875-01:00') self.assertEqual(fat_date_time_object._number_of_seconds, 966114391) self.assertEqual(fat_date_time_object._time_zone_offset, -60) fat_date_time_object.CopyFromDateTimeString( '2010-08-12 21:06:31.546875+01:00') self.assertEqual(fat_date_time_object._number_of_seconds, 966114391) self.assertEqual(fat_date_time_object._time_zone_offset, 60) fat_date_time_object.CopyFromDateTimeString('1980-01-02 00:00:00') self.assertEqual(fat_date_time_object._number_of_seconds, 86400) self.assertEqual(fat_date_time_object._time_zone_offset, None) with self.assertRaises(ValueError): fat_date_time_object.CopyFromDateTimeString('2200-01-02 00:00:00') def testGetNumberOfSeconds(self): """Tests the _GetNumberOfSeconds function.""" fat_date_time_object = fat_date_time.FATDateTime() fat_date_time_object._GetNumberOfSeconds(0xa8d03d0c) # Invalid number of seconds. test_fat_date_time = (0xa8d03d0c & ~(0x1f << 16)) | ((30 & 0x1f) << 16) with self.assertRaises(ValueError): fat_date_time_object._GetNumberOfSeconds(test_fat_date_time) # Invalid number of minutes. test_fat_date_time = (0xa8d03d0c & ~(0x3f << 21)) | ((60 & 0x3f) << 21) with self.assertRaises(ValueError): fat_date_time_object._GetNumberOfSeconds(test_fat_date_time) # Invalid number of hours. test_fat_date_time = (0xa8d03d0c & ~(0x1f << 27)) | ((24 & 0x1f) << 27) with self.assertRaises(ValueError): fat_date_time_object._GetNumberOfSeconds(test_fat_date_time) # Invalid day of month. test_fat_date_time = (0xa8d03d0c & ~0x1f) | (32 & 0x1f) with self.assertRaises(ValueError): fat_date_time_object._GetNumberOfSeconds(test_fat_date_time) # Invalid month. test_fat_date_time = (0xa8d03d0c & ~(0x0f << 5)) | ((13 & 0x0f) << 5) with self.assertRaises(ValueError): fat_date_time_object._GetNumberOfSeconds(test_fat_date_time) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" fat_date_time_object = fat_date_time.FATDateTime(fat_date_time=0xa8d03d0c) date_time_string = fat_date_time_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2010-08-12 21:06:32') fat_date_time_object = fat_date_time.FATDateTime() date_time_string = fat_date_time_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" fat_date_time_object = fat_date_time.FATDateTime(fat_date_time=0xa8d03d0c) date_time_string = fat_date_time_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2010-08-12T21:06:32+00:00') def testGetDate(self): """Tests the GetDate function.""" fat_date_time_object = fat_date_time.FATDateTime(fat_date_time=0xa8d03d0c) date_tuple = fat_date_time_object.GetDate() self.assertEqual(date_tuple, (2010, 8, 12)) fat_date_time_object = fat_date_time.FATDateTime() date_tuple = fat_date_time_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" fat_date_time_object = fat_date_time.FATDateTime(fat_date_time=0xa8d03d0c) date_with_time_of_day_tuple = fat_date_time_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2010, 8, 12, 21, 6, 32)) fat_date_time_object = fat_date_time.FATDateTime() date_with_time_of_day_tuple = fat_date_time_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" fat_date_time_object = fat_date_time.FATDateTime(fat_date_time=0xa8d03d0c) time_of_day_tuple = fat_date_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (21, 6, 32)) fat_date_time_object = fat_date_time.FATDateTime() time_of_day_tuple = fat_date_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) class FATTimestampTest(unittest.TestCase): """Tests for the POSIX timestamp.""" # pylint: disable=protected-access def testProperties(self): """Tests the properties.""" fat_timestamp_object = fat_date_time.FATTimestamp(timestamp=131033589024) self.assertEqual(fat_timestamp_object.timestamp, 131033589024) fat_timestamp_object = fat_date_time.FATTimestamp() self.assertIsNone(fat_timestamp_object.timestamp) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" fat_timestamp_object = fat_date_time.FATTimestamp(timestamp=131033589024) normalized_timestamp = fat_timestamp_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1625868690.24')) fat_timestamp_object = fat_date_time.FATTimestamp( time_zone_offset=60, timestamp=131033589024) normalized_timestamp = fat_timestamp_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1625865090.24')) fat_timestamp_object = fat_date_time.FATTimestamp() normalized_timestamp = fat_timestamp_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" fat_timestamp_object = fat_date_time.FATTimestamp() fat_timestamp_object.CopyFromDateTimeString('2021-07-09') self.assertEqual(fat_timestamp_object._timestamp, 131025600000) self.assertEqual(fat_timestamp_object._time_zone_offset, None) fat_timestamp_object.CopyFromDateTimeString('2021-07-09 22:11:30') self.assertEqual(fat_timestamp_object._timestamp, 131033589000) self.assertEqual(fat_timestamp_object._time_zone_offset, None) fat_timestamp_object.CopyFromDateTimeString('2021-07-09 22:11:30.246875') self.assertEqual(fat_timestamp_object._timestamp, 131033589024) self.assertEqual(fat_timestamp_object._time_zone_offset, None) fat_timestamp_object.CopyFromDateTimeString( '2021-07-09 22:11:30.246875-01:00') self.assertEqual(fat_timestamp_object._timestamp, 131033589024) self.assertEqual(fat_timestamp_object._time_zone_offset, -60) fat_timestamp_object.CopyFromDateTimeString( '2021-07-09 22:11:30.246875+01:00') self.assertEqual(fat_timestamp_object._timestamp, 131033589024) self.assertEqual(fat_timestamp_object._time_zone_offset, 60) fat_timestamp_object.CopyFromDateTimeString('1980-01-02 00:00:00') self.assertEqual(fat_timestamp_object._timestamp, 8640000) self.assertEqual(fat_timestamp_object._time_zone_offset, None) with self.assertRaises(ValueError): fat_timestamp_object.CopyFromDateTimeString('2200-01-02 00:00:00') def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" fat_timestamp_object = fat_date_time.FATTimestamp(timestamp=131033589024) date_time_string = fat_timestamp_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2021-07-09 22:11:30.24') fat_timestamp_object = fat_date_time.FATTimestamp() date_time_string = fat_timestamp_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" fat_timestamp_object = fat_date_time.FATTimestamp(timestamp=131033589024) date_time_string = fat_timestamp_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2021-07-09T22:11:30.24+00:00') def testCopyToFATTimestampstampWithFractionOfSecond(self): """Tests the CopyToPosixTimestampWithFractionOfSecond function.""" fat_timestamp_object = fat_date_time.FATTimestamp(timestamp=131033589024) fat_timestamp, fraction_of_second = ( fat_timestamp_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertEqual(fat_timestamp, 1625868690) self.assertEqual(fraction_of_second, 24) fat_timestamp_object = fat_date_time.FATTimestamp() fat_timestamp, fraction_of_second = ( fat_timestamp_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertIsNone(fat_timestamp) self.assertIsNone(fraction_of_second) def testGetDate(self): """Tests the GetDate function.""" fat_timestamp_object = fat_date_time.FATTimestamp(timestamp=131033589024) date_tuple = fat_timestamp_object.GetDate() self.assertEqual(date_tuple, (2021, 7, 9)) fat_timestamp_object = fat_date_time.FATTimestamp() date_tuple = fat_timestamp_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" fat_timestamp_object = fat_date_time.FATTimestamp(timestamp=131033589024) date_with_time_of_day_tuple = fat_timestamp_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2021, 7, 9, 22, 11, 30)) fat_timestamp_object = fat_date_time.FATTimestamp() date_with_time_of_day_tuple = fat_timestamp_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" fat_timestamp_object = fat_date_time.FATTimestamp(timestamp=131033589024) time_of_day_tuple = fat_timestamp_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (22, 11, 30)) fat_timestamp_object = fat_date_time.FATTimestamp() time_of_day_tuple = fat_timestamp_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/filetime.py000066400000000000000000000126561461534226200174130ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the FILETIME timestamp implementation.""" import decimal import unittest from dfdatetime import filetime class FiletimeEpochTest(unittest.TestCase): """Tests for the FILETIME epoch.""" def testInitialize(self): """Tests the __init__ function.""" filetime_epoch = filetime.FiletimeEpoch() self.assertIsNotNone(filetime_epoch) class FiletimeTest(unittest.TestCase): """Tests for the FILETIME timestamp.""" # pylint: disable=protected-access def testProperties(self): """Tests the properties.""" filetime_object = filetime.Filetime(timestamp=0x01cb3a623d0a17ce) self.assertEqual(filetime_object.timestamp, 0x01cb3a623d0a17ce) filetime_object = filetime.Filetime() self.assertIsNone(filetime_object.timestamp) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" filetime_object = filetime.Filetime(timestamp=0x01cb3a623d0a17ce) normalized_timestamp = filetime_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281647191.546875')) filetime_object = filetime.Filetime( time_zone_offset=60, timestamp=0x01cb3a623d0a17ce) normalized_timestamp = filetime_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643591.546875')) filetime_object = filetime.Filetime(timestamp=0x01cb3a623d0a17ce) filetime_object.time_zone_offset = 60 normalized_timestamp = filetime_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643591.546875')) filetime_object = filetime.Filetime(timestamp=0x1ffffffffffffffff) normalized_timestamp = filetime_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) filetime_object = filetime.Filetime() normalized_timestamp = filetime_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" filetime_object = filetime.Filetime() filetime_object.CopyFromDateTimeString('2010-08-12') self.assertEqual(filetime_object._timestamp, 0x1cb39b14e8c4000) self.assertEqual(filetime_object._time_zone_offset, None) filetime_object.CopyFromDateTimeString('2010-08-12 21:06:31') self.assertEqual(filetime_object._timestamp, 0x1cb3a623cb6a580) self.assertEqual(filetime_object._time_zone_offset, None) filetime_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') self.assertEqual(filetime_object._timestamp, 0x01cb3a623d0a17ce) self.assertEqual(filetime_object._time_zone_offset, None) filetime_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875-01:00') self.assertEqual(filetime_object._timestamp, 0x01cb3a623d0a17ce) self.assertEqual(filetime_object._time_zone_offset, -60) filetime_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875+01:00') self.assertEqual(filetime_object._timestamp, 0x01cb3a623d0a17ce) self.assertEqual(filetime_object._time_zone_offset, 60) filetime_object.CopyFromDateTimeString('1601-01-02 00:00:00') self.assertEqual(filetime_object._timestamp, 86400 * 10000000) self.assertEqual(filetime_object._time_zone_offset, None) with self.assertRaises(ValueError): filetime_object.CopyFromDateTimeString('1500-01-02 00:00:00') def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" filetime_object = filetime.Filetime(timestamp=0x01cb3a623d0a17ce) date_time_string = filetime_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2010-08-12 21:06:31.5468750') filetime_object = filetime.Filetime() date_time_string = filetime_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" filetime_object = filetime.Filetime(timestamp=0x01cb3a623d0a17ce) date_time_string = filetime_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2010-08-12T21:06:31.5468750+00:00') def testGetDate(self): """Tests the GetDate function.""" filetime_object = filetime.Filetime(timestamp=0x01cb3a623d0a17ce) date_tuple = filetime_object.GetDate() self.assertEqual(date_tuple, (2010, 8, 12)) filetime_object = filetime.Filetime() date_tuple = filetime_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" filetime_object = filetime.Filetime(timestamp=0x01cb3a623d0a17ce) date_with_time_of_day_tuple = filetime_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2010, 8, 12, 21, 6, 31)) filetime_object = filetime.Filetime() date_with_time_of_day_tuple = filetime_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" filetime_object = filetime.Filetime(timestamp=0x01cb3a623d0a17ce) time_of_day_tuple = filetime_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (21, 6, 31)) filetime_object = filetime.Filetime() time_of_day_tuple = filetime_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/golang_time.py000066400000000000000000000203571461534226200200770ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Tests for the Golang time.Time timestamp implementation.""" import decimal import struct import unittest from dfdatetime import golang_time class GolangEpochTest(unittest.TestCase): """Test for the Golang time.Time epoch.""" def testInitialize(self): """Tests the __init__ function.""" golang_epoch = golang_time.GolangTimeEpoch() self.assertIsNotNone(golang_epoch) def testEpochDate(self): """Tests the Golang time.Time epoch properties.""" golang_epoch = golang_time.GolangTimeEpoch() self.assertEqual(golang_epoch.year, 1) self.assertEqual(golang_epoch.month, 1) self.assertEqual(golang_epoch.day_of_month, 1) class GolangTest(unittest.TestCase): """Tests for the Golang time.Time timestamp.""" # pylint: disable=protected-access def testProperties(self): """Tests the Golang time.Time timestamp properties.""" golang_timestamp = struct.pack('>Bqih', 1, 0, 0, -1) golang_time_object = golang_time.GolangTime( golang_timestamp=golang_timestamp) self.assertEqual(golang_time_object._number_of_seconds, 0) self.assertEqual(golang_time_object._nanoseconds, 0) self.assertEqual(golang_time_object.is_local_time, False) self.assertEqual(golang_time_object._time_zone_offset, 0) golang_timestamp = struct.pack( '>Bqih', 1, golang_time.GolangTime._GOLANG_TO_POSIX_BASE, 0, 60) golang_time_object = golang_time.GolangTime( golang_timestamp=golang_timestamp) self.assertEqual(golang_time_object._number_of_seconds, golang_time.GolangTime._GOLANG_TO_POSIX_BASE) self.assertEqual(golang_time_object._nanoseconds, 0) self.assertEqual(golang_time_object.is_local_time, False) self.assertEqual(golang_time_object._time_zone_offset, 60) golang_timestamp = bytes.fromhex('010000000e7791f70000000000ffff') golang_time_object = golang_time.GolangTime( golang_timestamp=golang_timestamp) self.assertEqual(golang_time_object._number_of_seconds, golang_time.GolangTime._GOLANG_TO_POSIX_BASE) self.assertEqual(golang_time_object._nanoseconds, 0) self.assertEqual(golang_time_object.is_local_time, False) self.assertEqual(golang_time_object._time_zone_offset, 0) def testGetNormalizedTimestamp(self): """Test the _GetNormalizedTimestamp function.""" golang_timestamp = bytes.fromhex('010000000000000000000000000000') golang_time_object = golang_time.GolangTime( golang_timestamp=golang_timestamp) normalized_timestamp = golang_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) golang_timestamp = struct.pack('>Bqih', 1, 63772480949, 711098348, 0) golang_time_object = golang_time.GolangTime( golang_timestamp=golang_timestamp) normalized_timestamp = golang_time_object._GetNormalizedTimestamp() self.assertEqual( normalized_timestamp, decimal.Decimal('1636884149.711098348')) golang_timestamp = struct.pack('>Bqih', 1, 63772480949, 711098348, 60) golang_time_object = golang_time.GolangTime( golang_timestamp=golang_timestamp) normalized_timestamp = golang_time_object._GetNormalizedTimestamp() self.assertEqual( normalized_timestamp, decimal.Decimal('1636880549.711098348')) golang_timestamp = struct.pack('>Bqih', 1, 63772480949, 711098348, 0) golang_time_object = golang_time.GolangTime( golang_timestamp=golang_timestamp) golang_time_object.time_zone_offset = 60 normalized_timestamp = golang_time_object._GetNormalizedTimestamp() self.assertEqual( normalized_timestamp, decimal.Decimal('1636880549.711098348')) golang_timestamp = bytes.fromhex('010000000e7791f70000000000ffff') golang_time_object = golang_time.GolangTime( golang_timestamp=golang_timestamp) normalized_timestamp = golang_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('0')) golang_timestamp = bytes.fromhex('010000000e7791f60000000000ffff') golang_time_object = golang_time.GolangTime( golang_timestamp=golang_timestamp) normalized_timestamp = golang_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) def testGetNumberOfSeconds(self): """Test the _GetNumberOfSeconds function.""" golang_time_object = golang_time.GolangTime() golang_timestamp = bytes.fromhex('010000000000000002000000030004') number_of_seconds, nanoseconds, time_zone_offset = ( golang_time_object._GetNumberOfSeconds(golang_timestamp)) self.assertEqual(number_of_seconds, 2) self.assertEqual(nanoseconds, 3) self.assertEqual(time_zone_offset, 4) golang_timestamp = bytes.fromhex('02000000000000000500000006ffff08') number_of_seconds, nanoseconds, time_zone_offset = ( golang_time_object._GetNumberOfSeconds(golang_timestamp)) self.assertEqual(number_of_seconds, 5) self.assertEqual(nanoseconds, 6) self.assertEqual(time_zone_offset, 0) with self.assertRaises(ValueError): golang_timestamp = bytes.fromhex('0100') golang_time_object._GetNumberOfSeconds(golang_timestamp) with self.assertRaises(ValueError): golang_timestamp = bytes.fromhex('020000000000000000000000000000') golang_time_object._GetNumberOfSeconds(golang_timestamp) with self.assertRaises(ValueError): golang_timestamp = bytes.fromhex('ff0000000000000000000000000000') golang_time_object._GetNumberOfSeconds(golang_timestamp) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" golang_time_object = golang_time.GolangTime() golang_time_object.CopyFromDateTimeString('0001-01-01') self.assertEqual(golang_time_object._number_of_seconds, 0) self.assertEqual(golang_time_object._nanoseconds, 0) self.assertEqual(golang_time_object._time_zone_offset, None) golang_time_object.CopyFromDateTimeString('0001-01-01 00:01:00') self.assertEqual(golang_time_object._number_of_seconds, 60) self.assertEqual(golang_time_object._nanoseconds, 0) self.assertEqual(golang_time_object._time_zone_offset, None) golang_time_object.CopyFromDateTimeString('0001-01-01 00:00:00.000001') self.assertEqual(golang_time_object._number_of_seconds, 0) self.assertEqual(golang_time_object._nanoseconds, 1000) self.assertEqual(golang_time_object._time_zone_offset, None) golang_time_object.CopyFromDateTimeString('2000-01-01') self.assertEqual(golang_time_object._number_of_seconds, 63082281600) self.assertEqual(golang_time_object._nanoseconds, 0) self.assertEqual(golang_time_object._time_zone_offset, None) golang_time_object.CopyFromDateTimeString('2000-01-01 12:23:45.567890') self.assertEqual(golang_time_object._number_of_seconds, 63082326225) self.assertEqual(golang_time_object._nanoseconds, 567890000) self.assertEqual(golang_time_object._time_zone_offset, None) golang_time_object.CopyFromDateTimeString( '2000-01-01 12:23:45.567890+01:00') self.assertEqual(golang_time_object._number_of_seconds, 63082326225) self.assertEqual(golang_time_object._nanoseconds, 567890000) self.assertEqual(golang_time_object._time_zone_offset, 60) def testCopyToDateTimeString(self): """Test the CopyToDateTimeString function.""" golang_timestamp = bytes.fromhex('010000000eafffe8d121d95050ffff') golang_time_object = golang_time.GolangTime( golang_timestamp=golang_timestamp) self.assertEqual(golang_time_object._number_of_seconds, 63082326225) self.assertEqual(golang_time_object._nanoseconds, 567890000) self.assertEqual(golang_time_object._time_zone_offset, 0) date_time_string = golang_time_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2000-01-01 12:23:45.567890000') golang_timestamp = bytes.fromhex('010000000eafffe8d10000ddd5ffff') golang_time_object = golang_time.GolangTime( golang_timestamp=golang_timestamp) self.assertEqual(golang_time_object._number_of_seconds, 63082326225) self.assertEqual(golang_time_object._nanoseconds, 56789) self.assertEqual(golang_time_object._time_zone_offset, 0) date_time_string = golang_time_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2000-01-01 12:23:45.000056789') if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/hfs_time.py000066400000000000000000000126251461534226200174070ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the HFS time implementation.""" import decimal import unittest from dfdatetime import hfs_time class HFSTimeEpochTest(unittest.TestCase): """Tests for the HFS time epoch.""" def testInitialize(self): """Tests the __init__ function.""" hfs_time_epoch = hfs_time.HFSTimeEpoch() self.assertIsNotNone(hfs_time_epoch) class HFSTimeTest(unittest.TestCase): """Tests for the HFS timestamp.""" # pylint: disable=protected-access def testProperties(self): """Tests the properties.""" hfs_time_object = hfs_time.HFSTime(timestamp=3458215528) self.assertEqual(hfs_time_object.timestamp, 3458215528) hfs_time_object = hfs_time.HFSTime() self.assertIsNone(hfs_time_object.timestamp) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" hfs_time_object = hfs_time.HFSTime(timestamp=3458215528) normalized_timestamp = hfs_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1375370728.0')) hfs_time_object = hfs_time.HFSTime( time_zone_offset=60, timestamp=3458215528) normalized_timestamp = hfs_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1375367128.0')) hfs_time_object = hfs_time.HFSTime(timestamp=3458215528) hfs_time_object.time_zone_offset = 60 normalized_timestamp = hfs_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1375367128.0')) hfs_time_object = hfs_time.HFSTime(timestamp=0x1ffffffff) normalized_timestamp = hfs_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) hfs_time_object = hfs_time.HFSTime(timestamp=-0x1ffffffff) normalized_timestamp = hfs_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) hfs_time_object = hfs_time.HFSTime() normalized_timestamp = hfs_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" hfs_time_object = hfs_time.HFSTime() hfs_time_object.CopyFromDateTimeString('2013-08-01') self.assertEqual(hfs_time_object._timestamp, 3458160000) self.assertEqual(hfs_time_object._time_zone_offset, None) hfs_time_object.CopyFromDateTimeString('2013-08-01 15:25:28') self.assertEqual(hfs_time_object._timestamp, 3458215528) self.assertEqual(hfs_time_object._time_zone_offset, None) hfs_time_object.CopyFromDateTimeString('2013-08-01 15:25:28.546875') self.assertEqual(hfs_time_object._timestamp, 3458215528) self.assertEqual(hfs_time_object._time_zone_offset, None) hfs_time_object.CopyFromDateTimeString('2013-08-01 15:25:28.546875-01:00') self.assertEqual(hfs_time_object._timestamp, 3458215528) self.assertEqual(hfs_time_object._time_zone_offset, -60) hfs_time_object.CopyFromDateTimeString('2013-08-01 15:25:28.546875+01:00') self.assertEqual(hfs_time_object._timestamp, 3458215528) self.assertEqual(hfs_time_object._time_zone_offset, 60) hfs_time_object.CopyFromDateTimeString('1904-01-02 00:00:00') self.assertEqual(hfs_time_object._timestamp, 86400) self.assertEqual(hfs_time_object._time_zone_offset, None) with self.assertRaises(ValueError): hfs_time_object.CopyFromDateTimeString('1600-01-02 00:00:00') def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" hfs_time_object = hfs_time.HFSTime(timestamp=3458215528) date_time_string = hfs_time_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2013-08-01 15:25:28') hfs_time_object = hfs_time.HFSTime() date_time_string = hfs_time_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" hfs_time_object = hfs_time.HFSTime(timestamp=3458215528) date_time_string = hfs_time_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2013-08-01T15:25:28+00:00') def testGetDate(self): """Tests the GetDate function.""" hfs_time_object = hfs_time.HFSTime(timestamp=3458215528) date_tuple = hfs_time_object.GetDate() self.assertEqual(date_tuple, (2013, 8, 1)) hfs_time_object = hfs_time.HFSTime() date_tuple = hfs_time_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" hfs_time_object = hfs_time.HFSTime(timestamp=3458215528) date_with_time_of_day_tuple = hfs_time_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2013, 8, 1, 15, 25, 28)) hfs_time_object = hfs_time.HFSTime() date_with_time_of_day_tuple = hfs_time_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" hfs_time_object = hfs_time.HFSTime(timestamp=3458215528) time_of_day_tuple = hfs_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (15, 25, 28)) hfs_time_object = hfs_time.HFSTime() time_of_day_tuple = hfs_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/interface.py000066400000000000000000000545311461534226200175530ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the date and time interfaces.""" import unittest from dfdatetime import definitions from dfdatetime import interface class EmptyDateTimeValues(interface.DateTimeValues): """Empty date time values for testing.""" # pylint: disable=abstract-method,redundant-returns-doc def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: float: normalized timestamp, which is None for testing purposes. """ return None class TestDateTimeValues(interface.DateTimeValues): """Date time values for testing.""" # pylint: disable=abstract-method def _GetNormalizedTimestamp(self): """Retrieves the normalized timestamp. Returns: float: normalized timestamp, which is 0.0 for testing purposes. """ return 0.0 class DateTimeEpochTest(unittest.TestCase): """Tests for the date and time epoch interface.""" def testInitialize(self): """Tests the __init__ function.""" date_time_epoch = interface.DateTimeEpoch(1970, 1, 1) self.assertIsNotNone(date_time_epoch) class DateTimeValuesTest(unittest.TestCase): """Tests for the date and time values interface.""" # pylint: disable=protected-access def testComparison(self): """Tests the comparison functions.""" date_time_values1 = EmptyDateTimeValues() date_time_values2 = EmptyDateTimeValues() self.assertTrue(date_time_values1 == date_time_values2) self.assertTrue(date_time_values1 >= date_time_values2) self.assertFalse(date_time_values1 > date_time_values2) self.assertTrue(date_time_values1 <= date_time_values2) self.assertFalse(date_time_values1 < date_time_values2) self.assertFalse(date_time_values1 != date_time_values2) date_time_values1 = EmptyDateTimeValues() date_time_values2 = TestDateTimeValues() self.assertFalse(date_time_values1 == date_time_values2) self.assertFalse(date_time_values1 >= date_time_values2) self.assertFalse(date_time_values1 > date_time_values2) self.assertTrue(date_time_values1 <= date_time_values2) self.assertTrue(date_time_values1 < date_time_values2) self.assertTrue(date_time_values1 != date_time_values2) date_time_values1 = TestDateTimeValues() date_time_values2 = EmptyDateTimeValues() self.assertFalse(date_time_values1 == date_time_values2) self.assertTrue(date_time_values1 >= date_time_values2) self.assertTrue(date_time_values1 > date_time_values2) self.assertFalse(date_time_values1 <= date_time_values2) self.assertFalse(date_time_values1 < date_time_values2) self.assertTrue(date_time_values1 != date_time_values2) date_time_values1 = TestDateTimeValues() date_time_values2 = TestDateTimeValues() self.assertTrue(date_time_values1 == date_time_values2) self.assertTrue(date_time_values1 >= date_time_values2) self.assertFalse(date_time_values1 > date_time_values2) self.assertTrue(date_time_values1 <= date_time_values2) self.assertFalse(date_time_values1 < date_time_values2) self.assertFalse(date_time_values1 != date_time_values2) self.assertFalse(date_time_values1 == 0.0) with self.assertRaises(ValueError): date_time_values1 >= 0.0 # pylint: disable=pointless-statement with self.assertRaises(ValueError): date_time_values1 > 0.0 # pylint: disable=pointless-statement with self.assertRaises(ValueError): date_time_values1 <= 0.0 # pylint: disable=pointless-statement with self.assertRaises(ValueError): date_time_values1 < 0.0 # pylint: disable=pointless-statement self.assertTrue(date_time_values1 != 0.0) def testCopyDateFromString(self): """Tests the _CopyDateFromString function.""" date_time_values = interface.DateTimeValues() expected_date_tuple = (2010, 1, 1) date_tuple = date_time_values._CopyDateFromString('2010-01-01') self.assertEqual(date_tuple, expected_date_tuple) expected_date_tuple = (2010, 8, 1) date_tuple = date_time_values._CopyDateFromString('2010-08-01') self.assertEqual(date_tuple, expected_date_tuple) expected_date_tuple = (2010, 8, 12) date_tuple = date_time_values._CopyDateFromString('2010-08-12') self.assertEqual(date_tuple, expected_date_tuple) expected_date_tuple = (2010, 8, 31) date_tuple = date_time_values._CopyDateFromString('2010-08-31') self.assertEqual(date_tuple, expected_date_tuple) expected_date_tuple = (2010, 12, 31) date_tuple = date_time_values._CopyDateFromString('2010-12-31') self.assertEqual(date_tuple, expected_date_tuple) expected_date_tuple = (1601, 1, 2) date_tuple = date_time_values._CopyDateFromString('1601-01-02') self.assertEqual(date_tuple, expected_date_tuple) with self.assertRaises(ValueError): date_time_values._CopyDateFromString('') with self.assertRaises(ValueError): date_time_values._CopyDateFromString('195a-01-02') with self.assertRaises(ValueError): date_time_values._CopyDateFromString('10000-01-02') with self.assertRaises(ValueError): date_time_values._CopyDateFromString('2010-09-00') with self.assertRaises(ValueError): date_time_values._CopyDateFromString('2010-09-31') with self.assertRaises(ValueError): date_time_values._CopyDateFromString('1601-01-32') with self.assertRaises(ValueError): date_time_values._CopyDateFromString('1601-01-b2') with self.assertRaises(ValueError): date_time_values._CopyDateFromString('1601-13-02') with self.assertRaises(ValueError): date_time_values._CopyDateFromString('1601-a1-02') with self.assertRaises(ValueError): date_time_values._CopyDateFromString('2010-02-29') with self.assertRaises(ValueError): date_time_values._CopyDateFromString('2010-04-31') def testCopyDateTimeFromString(self): """Tests the _CopyDateTimeFromString function.""" date_time_values = interface.DateTimeValues() expected_date_dict = { 'year': 2010, 'month': 8, 'day_of_month': 12} date_dict = date_time_values._CopyDateTimeFromString('2010-08-12') self.assertEqual(date_dict, expected_date_dict) expected_date_dict = { 'year': 2010, 'month': 8, 'day_of_month': 12, 'hours': 21, 'minutes': 6, 'seconds': 31} date_dict = date_time_values._CopyDateTimeFromString( '2010-08-12 21:06:31') self.assertEqual(date_dict, expected_date_dict) expected_date_dict = { 'year': 2010, 'month': 8, 'day_of_month': 12, 'hours': 21, 'minutes': 6, 'seconds': 31, 'nanoseconds': 546875000} date_dict = date_time_values._CopyDateTimeFromString( '2010-08-12 21:06:31.546875') self.assertEqual(date_dict, expected_date_dict) expected_date_dict = { 'year': 2010, 'month': 8, 'day_of_month': 12, 'hours': 21, 'minutes': 6, 'seconds': 31, 'nanoseconds': 546875000, 'time_zone_offset': -60} date_dict = date_time_values._CopyDateTimeFromString( '2010-08-12 21:06:31.546875-01:00') self.assertEqual(date_dict, expected_date_dict) expected_date_dict = { 'year': 2010, 'month': 8, 'day_of_month': 12, 'hours': 21, 'minutes': 6, 'seconds': 31, 'nanoseconds': 546875000, 'time_zone_offset': 60} date_dict = date_time_values._CopyDateTimeFromString( '2010-08-12 21:06:31.546875+01:00') self.assertEqual(date_dict, expected_date_dict) expected_date_dict = { 'year': 2010, 'month': 8, 'day_of_month': 12, 'hours': 21, 'minutes': 6, 'seconds': 31, 'nanoseconds': 546875333, 'time_zone_offset': 60} date_dict = date_time_values._CopyDateTimeFromString( '2010-08-12 21:06:31.546875333+01:00') self.assertEqual(date_dict, expected_date_dict) with self.assertRaises(ValueError): date_time_values._CopyDateTimeFromString('') with self.assertRaises(ValueError): date_time_values._CopyDateTimeFromString( '2010-08-12T21:06:31.546875+01:00') def testCopyTimeFromString(self): """Tests the _CopyTimeFromString function.""" date_time_values = interface.DateTimeValues() expected_time_tuple = (8, 4, 32, None, None) time_tuple = date_time_values._CopyTimeFromString('08:04:32') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 23, 56, None, None) time_tuple = date_time_values._CopyTimeFromString('20:23:56') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 23, 56, None, 330) time_tuple = date_time_values._CopyTimeFromString('20:23:56+05:30') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 23, 56, 327000000, None) time_tuple = date_time_values._CopyTimeFromString('20:23:56.327') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 23, 56, 327000000, 60) time_tuple = date_time_values._CopyTimeFromString('20:23:56.327+01:00') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 23, 56, 327124000, None) time_tuple = date_time_values._CopyTimeFromString('20:23:56.327124') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 23, 56, 327124000, -300) time_tuple = date_time_values._CopyTimeFromString('20:23:56.327124-05:00') self.assertEqual(time_tuple, expected_time_tuple) with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('') with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('14') with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('14:00') with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('24:00:00') with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('12b00:00') with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('12:00b00') with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('1s:00:00') with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('00:60:00') with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('00:e0:00') with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('00:00:60') with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('00:00:w0') with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('12:00:00.00') with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('12:00:00.0000') with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('12:00:00.00w') with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('12:00:00+01b00') with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('12:00:00+0w:00') with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('12:00:00+20:00') with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('12:00:00+01:0w') with self.assertRaises(ValueError): date_time_values._CopyTimeFromString('12:00:00+01:60') def testGetDateValues(self): """Tests the _GetDateValues function.""" date_time_values = interface.DateTimeValues() year, month, day_of_month = date_time_values._GetDateValues(0, 2000, 1, 1) self.assertEqual(year, 2000) self.assertEqual(month, 1) self.assertEqual(day_of_month, 1) year, month, day_of_month = date_time_values._GetDateValues(10, 2000, 1, 1) self.assertEqual(year, 2000) self.assertEqual(month, 1) self.assertEqual(day_of_month, 11) year, month, day_of_month = date_time_values._GetDateValues(100, 2000, 1, 1) self.assertEqual(year, 2000) self.assertEqual(month, 4) self.assertEqual(day_of_month, 10) year, month, day_of_month = date_time_values._GetDateValues(100, 1999, 1, 1) self.assertEqual(year, 1999) self.assertEqual(month, 4) self.assertEqual(day_of_month, 11) year, month, day_of_month = date_time_values._GetDateValues(0, 1999, 12, 30) self.assertEqual(year, 1999) self.assertEqual(month, 12) self.assertEqual(day_of_month, 30) year, month, day_of_month = date_time_values._GetDateValues(5, 1999, 12, 30) self.assertEqual(year, 2000) self.assertEqual(month, 1) self.assertEqual(day_of_month, 4) year, month, day_of_month = date_time_values._GetDateValues(-10, 2000, 1, 1) self.assertEqual(year, 1999) self.assertEqual(month, 12) self.assertEqual(day_of_month, 22) year, month, day_of_month = date_time_values._GetDateValues( -100, 2000, 1, 1) self.assertEqual(year, 1999) self.assertEqual(month, 9) self.assertEqual(day_of_month, 23) year, month, day_of_month = date_time_values._GetDateValues(-10, 2000, 1, 9) self.assertEqual(year, 1999) self.assertEqual(month, 12) self.assertEqual(day_of_month, 30) with self.assertRaises(ValueError): date_time_values._GetDateValues(10, -1, 1, 1) with self.assertRaises(ValueError): date_time_values._GetDateValues(10, 2000, 0, 1) with self.assertRaises(ValueError): date_time_values._GetDateValues(10, 2000, 13, 1) with self.assertRaises(ValueError): date_time_values._GetDateValues(10, 2000, 1, 0) with self.assertRaises(ValueError): date_time_values._GetDateValues(10, 2000, 1, 32) year, month, day_of_month = date_time_values._GetDateValues( 0, 1899, 12, 30) self.assertEqual(year, 1899) self.assertEqual(month, 12) self.assertEqual(day_of_month, 30) year, month, day_of_month = date_time_values._GetDateValues( 25569, 1899, 12, 30) self.assertEqual(year, 1970) self.assertEqual(month, 1) self.assertEqual(day_of_month, 1) year, month, day_of_month = date_time_values._GetDateValues( 36526, 1899, 12, 30) self.assertEqual(year, 2000) self.assertEqual(month, 1) self.assertEqual(day_of_month, 1) year, month, day_of_month = date_time_values._GetDateValues( 41275, 1899, 12, 30) self.assertEqual(year, 2013) self.assertEqual(month, 1) self.assertEqual(day_of_month, 1) year, month, day_of_month = date_time_values._GetDateValues( 41443, 1899, 12, 30) self.assertEqual(year, 2013) self.assertEqual(month, 6) self.assertEqual(day_of_month, 18) year, month, day_of_month = date_time_values._GetDateValues( -25569, 1899, 12, 30) self.assertEqual(year, 1829) self.assertEqual(month, 12) self.assertEqual(day_of_month, 28) year, month, day_of_month = date_time_values._GetDateValues(0, 1970, 1, 1) self.assertEqual(year, 1970) self.assertEqual(month, 1) self.assertEqual(day_of_month, 1) year, month, day_of_month = date_time_values._GetDateValues(-1, 1970, 1, 1) self.assertEqual(year, 1969) self.assertEqual(month, 12) self.assertEqual(day_of_month, 31) year, month, day_of_month = date_time_values._GetDateValues(364, 1970, 1, 1) self.assertEqual(year, 1970) self.assertEqual(month, 12) self.assertEqual(day_of_month, 31) year, month, day_of_month = date_time_values._GetDateValues( 1460, 1970, 1, 1) self.assertEqual(year, 1973) self.assertEqual(month, 12) self.assertEqual(day_of_month, 31) def testGetDateValuesWithEpoch(self): """Tests the _GetDateValuesWithEpoch function.""" date_time_epoch = interface.DateTimeEpoch(2000, 1, 1) date_time_values = interface.DateTimeValues() year, month, day_of_month = date_time_values._GetDateValuesWithEpoch( 0, date_time_epoch) self.assertEqual(year, 2000) self.assertEqual(month, 1) self.assertEqual(day_of_month, 1) def testGetDayOfYear(self): """Tests the _GetDayOfYear function.""" date_time_values = interface.DateTimeValues() day_of_year = date_time_values._GetDayOfYear(1999, 1, 1) self.assertEqual(day_of_year, 1) day_of_year = date_time_values._GetDayOfYear(1999, 4, 21) self.assertEqual(day_of_year, 111) day_of_year = date_time_values._GetDayOfYear(1999, 12, 31) self.assertEqual(day_of_year, 365) day_of_year = date_time_values._GetDayOfYear(2000, 1, 1) self.assertEqual(day_of_year, 1) day_of_year = date_time_values._GetDayOfYear(2000, 4, 21) self.assertEqual(day_of_year, 112) day_of_year = date_time_values._GetDayOfYear(2000, 12, 31) self.assertEqual(day_of_year, 366) with self.assertRaises(ValueError): date_time_values._GetDayOfYear(1999, 0, 1) with self.assertRaises(ValueError): date_time_values._GetDayOfYear(1999, 13, 1) with self.assertRaises(ValueError): date_time_values._GetDayOfYear(1999, 1, 0) with self.assertRaises(ValueError): date_time_values._GetDayOfYear(1999, 1, 32) def testGetDaysPerMonth(self): """Tests the _GetDaysPerMonth function.""" date_time_values = interface.DateTimeValues() expected_days_per_month = list(definitions.DAYS_PER_MONTH) days_per_month = [] for month in range(1, 13): days_per_month.append(date_time_values._GetDaysPerMonth(1999, month)) self.assertEqual(days_per_month, expected_days_per_month) expected_days_per_month[1] += 1 days_per_month = [] for month in range(1, 13): days_per_month.append(date_time_values._GetDaysPerMonth(2000, month)) self.assertEqual(days_per_month, expected_days_per_month) with self.assertRaises(ValueError): date_time_values._GetDaysPerMonth(1999, 0) with self.assertRaises(ValueError): date_time_values._GetDaysPerMonth(1999, 13) def testGetNumberOfDaysInCentury(self): """Tests the _GetNumberOfDaysInCentury function.""" date_time_values = interface.DateTimeValues() self.assertEqual(date_time_values._GetNumberOfDaysInCentury(1700), 36524) self.assertEqual(date_time_values._GetNumberOfDaysInCentury(2000), 36525) with self.assertRaises(ValueError): date_time_values._GetNumberOfDaysInCentury(-1) def testGetNumberOfDaysInYear(self): """Tests the _GetNumberOfDaysInYear function.""" date_time_values = interface.DateTimeValues() self.assertEqual(date_time_values._GetNumberOfDaysInYear(1999), 365) self.assertEqual(date_time_values._GetNumberOfDaysInYear(2000), 366) self.assertEqual(date_time_values._GetNumberOfDaysInYear(1996), 366) def testGetNumberOfSecondsFromElements(self): """Tests the _GetNumberOfSecondsFromElements function.""" date_time_values = interface.DateTimeValues() number_of_seconds = date_time_values._GetNumberOfSecondsFromElements( 1970, 1, 1, 0, 1, 0) self.assertEqual(number_of_seconds, 60) number_of_seconds = date_time_values._GetNumberOfSecondsFromElements( 2010, 8, 12, 0, 0, 0) self.assertEqual(number_of_seconds, 1281571200) number_of_seconds = date_time_values._GetNumberOfSecondsFromElements( 2010, 8, 12, None, None, None) self.assertEqual(number_of_seconds, 1281571200) number_of_seconds = date_time_values._GetNumberOfSecondsFromElements( 2010, 8, 12, 21, 6, 31) self.assertEqual(number_of_seconds, 1281647191) number_of_seconds = date_time_values._GetNumberOfSecondsFromElements( 2012, 3, 5, 20, 40, 0) self.assertEqual(number_of_seconds, 1330980000) number_of_seconds = date_time_values._GetNumberOfSecondsFromElements( 1601, 1, 2, 0, 0, 0) self.assertEqual(number_of_seconds, -11644387200) number_of_seconds = date_time_values._GetNumberOfSecondsFromElements( 0, 1, 2, 0, 0, 0) self.assertEqual(number_of_seconds, -62167132800) number_of_seconds = date_time_values._GetNumberOfSecondsFromElements( 2010, 8, 0, 24, 6, 31) self.assertIsNone(number_of_seconds) with self.assertRaises(ValueError): date_time_values._GetNumberOfSecondsFromElements( 2010, 13, 12, 21, 6, 31) with self.assertRaises(ValueError): date_time_values._GetNumberOfSecondsFromElements( 2010, 8, 32, 24, 6, 31) with self.assertRaises(ValueError): date_time_values._GetNumberOfSecondsFromElements( 2010, 8, 12, 24, 6, 31) with self.assertRaises(ValueError): date_time_values._GetNumberOfSecondsFromElements( 2010, 8, 12, 21, 99, 31) with self.assertRaises(ValueError): date_time_values._GetNumberOfSecondsFromElements( 2010, 8, 12, 21, 6, 65) with self.assertRaises(ValueError): date_time_values._GetNumberOfSecondsFromElements( 2013, 2, 29, 1, 4, 25) def testGetTimeValues(self): """Tests the _GetTimeValues function.""" date_time_values = interface.DateTimeValues() days, hours, minutes, seconds = date_time_values._GetTimeValues(10) self.assertEqual(days, 0) self.assertEqual(hours, 0) self.assertEqual(minutes, 0) self.assertEqual(seconds, 10) days, hours, minutes, seconds = date_time_values._GetTimeValues(190) self.assertEqual(days, 0) self.assertEqual(hours, 0) self.assertEqual(minutes, 3) self.assertEqual(seconds, 10) days, hours, minutes, seconds = date_time_values._GetTimeValues(18190) self.assertEqual(days, 0) self.assertEqual(hours, 5) self.assertEqual(minutes, 3) self.assertEqual(seconds, 10) days, hours, minutes, seconds = date_time_values._GetTimeValues(190990) self.assertEqual(days, 2) self.assertEqual(hours, 5) self.assertEqual(minutes, 3) self.assertEqual(seconds, 10) days, hours, minutes, seconds = date_time_values._GetTimeValues(-10) self.assertEqual(days, -1) self.assertEqual(hours, 23) self.assertEqual(minutes, 59) self.assertEqual(seconds, 50) days, hours, minutes, seconds = date_time_values._GetTimeValues(-190) self.assertEqual(days, -1) self.assertEqual(hours, 23) self.assertEqual(minutes, 56) self.assertEqual(seconds, 50) days, hours, minutes, seconds = date_time_values._GetTimeValues(-18190) self.assertEqual(days, -1) self.assertEqual(hours, 18) self.assertEqual(minutes, 56) self.assertEqual(seconds, 50) days, hours, minutes, seconds = date_time_values._GetTimeValues(-190990) self.assertEqual(days, -3) self.assertEqual(hours, 18) self.assertEqual(minutes, 56) self.assertEqual(seconds, 50) def testIsLeapYear(self): """Tests the _IsLeapYear function.""" date_time_values = interface.DateTimeValues() self.assertFalse(date_time_values._IsLeapYear(1999)) self.assertTrue(date_time_values._IsLeapYear(2000)) self.assertTrue(date_time_values._IsLeapYear(1996)) if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/java_time.py000066400000000000000000000116261461534226200175500ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the Java java.util.Date timestamp implementation.""" import decimal import unittest from dfdatetime import java_time class JavaTimeTest(unittest.TestCase): """Tests for the Java java.util.Date timestamp.""" # pylint: disable=protected-access def testProperties(self): """Tests the properties.""" java_time_object = java_time.JavaTime(timestamp=1281643591546) self.assertEqual(java_time_object.timestamp, 1281643591546) java_time_object = java_time.JavaTime() self.assertIsNone(java_time_object.timestamp) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" java_time_object = java_time.JavaTime(timestamp=1281643591546) normalized_timestamp = java_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643591.546')) java_time_object = java_time.JavaTime( time_zone_offset=60, timestamp=1281643591546) normalized_timestamp = java_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991.546')) java_time_object = java_time.JavaTime(timestamp=1281643591546) java_time_object.time_zone_offset = 60 normalized_timestamp = java_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991.546')) java_time_object = java_time.JavaTime() normalized_timestamp = java_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" java_time_object = java_time.JavaTime() java_time_object.CopyFromDateTimeString('2010-08-12') self.assertEqual(java_time_object._timestamp, 1281571200000) self.assertEqual(java_time_object._time_zone_offset, None) java_time_object.CopyFromDateTimeString('2010-08-12 21:06:31') self.assertEqual(java_time_object._timestamp, 1281647191000) self.assertEqual(java_time_object._time_zone_offset, None) java_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') self.assertEqual(java_time_object._timestamp, 1281647191546) self.assertEqual(java_time_object._time_zone_offset, None) java_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875-01:00') self.assertEqual(java_time_object._timestamp, 1281647191546) self.assertEqual(java_time_object._time_zone_offset, -60) java_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875+01:00') self.assertEqual(java_time_object._timestamp, 1281647191546) self.assertEqual(java_time_object._time_zone_offset, 60) java_time_object.CopyFromDateTimeString('1601-01-02 00:00:00') self.assertEqual(java_time_object._timestamp, -11644387200000) self.assertEqual(java_time_object._time_zone_offset, None) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" java_time_object = java_time.JavaTime(timestamp=1281643591546) date_time_string = java_time_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2010-08-12 20:06:31.546') java_time_object = java_time.JavaTime() date_time_string = java_time_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" java_time_object = java_time.JavaTime(timestamp=1281643591546) date_time_string = java_time_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2010-08-12T20:06:31.546+00:00') def testGetDate(self): """Tests the GetDate function.""" java_time_object = java_time.JavaTime(timestamp=1281643591546) date_tuple = java_time_object.GetDate() self.assertEqual(date_tuple, (2010, 8, 12)) java_time_object = java_time.JavaTime() date_tuple = java_time_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" java_time_object = java_time.JavaTime(timestamp=1281643591546) date_with_time_of_day_tuple = java_time_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2010, 8, 12, 20, 6, 31)) java_time_object = java_time.JavaTime() date_with_time_of_day_tuple = java_time_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" java_time_object = java_time.JavaTime(timestamp=1281643591546) time_of_day_tuple = java_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (20, 6, 31)) java_time_object = java_time.JavaTime() time_of_day_tuple = java_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/ole_automation_date.py000066400000000000000000000145521461534226200216260ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the OLE Automation date implementation.""" import decimal import unittest from dfdatetime import ole_automation_date class OLEAutomationDateEpochTest(unittest.TestCase): """Tests for the OLE Automation date epoch.""" def testInitialize(self): """Tests the __init__ function.""" ole_automation_date_epoch = ole_automation_date.OLEAutomationDateEpoch() self.assertIsNotNone(ole_automation_date_epoch) class OLEAutomationDateTest(unittest.TestCase): """Tests for the OLE Automation date.""" # pylint: disable=protected-access def testProperties(self): """Tests the properties.""" ole_automation_date_object = ole_automation_date.OLEAutomationDate( timestamp=43044.480556) self.assertEqual(ole_automation_date_object.timestamp, 43044.480556) ole_automation_date_object = ole_automation_date.OLEAutomationDate() self.assertIsNone(ole_automation_date_object.timestamp) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" ole_automation_date_object = ole_automation_date.OLEAutomationDate( timestamp=43044.480556) expected_normalized_timestamp = decimal.Decimal( '1509881520.038400194607675076') normalized_timestamp = ole_automation_date_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, expected_normalized_timestamp) ole_automation_date_object = ole_automation_date.OLEAutomationDate( time_zone_offset=60, timestamp=43044.480556) expected_normalized_timestamp = decimal.Decimal( '1509877920.038400194607675076') normalized_timestamp = ole_automation_date_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, expected_normalized_timestamp) ole_automation_date_object = ole_automation_date.OLEAutomationDate( timestamp=43044.480556) ole_automation_date_object.time_zone_offset = 60 expected_normalized_timestamp = decimal.Decimal( '1509877920.038400194607675076') normalized_timestamp = ole_automation_date_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, expected_normalized_timestamp) ole_automation_date_object = ole_automation_date.OLEAutomationDate() normalized_timestamp = ole_automation_date_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" ole_automation_date_object = ole_automation_date.OLEAutomationDate() ole_automation_date_object.CopyFromDateTimeString('2017-11-05') self.assertEqual(ole_automation_date_object._timestamp, 43044.0) self.assertEqual(ole_automation_date_object._time_zone_offset, None) ole_automation_date_object.CopyFromDateTimeString('2017-11-05 11:32:00') self.assertEqual(ole_automation_date_object._timestamp, 43044.48055555555) self.assertEqual(ole_automation_date_object._time_zone_offset, None) ole_automation_date_object.CopyFromDateTimeString( '2017-11-05 11:32:00.546875') self.assertEqual(ole_automation_date_object._timestamp, 43044.480561885124) self.assertEqual(ole_automation_date_object._time_zone_offset, None) ole_automation_date_object.CopyFromDateTimeString( '2017-11-05 11:32:00.546875-01:00') self.assertEqual(ole_automation_date_object._timestamp, 43044.480561885124) self.assertEqual(ole_automation_date_object._time_zone_offset, -60) ole_automation_date_object.CopyFromDateTimeString( '2017-11-05 11:32:00.546875+01:00') self.assertEqual(ole_automation_date_object._timestamp, 43044.480561885124) self.assertEqual(ole_automation_date_object._time_zone_offset, 60) ole_automation_date_object.CopyFromDateTimeString('1900-01-01 00:00:00') self.assertEqual(ole_automation_date_object._timestamp, 2.0) self.assertEqual(ole_automation_date_object._time_zone_offset, None) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" ole_automation_date_object = ole_automation_date.OLEAutomationDate( timestamp=43044.480556) date_time_string = ole_automation_date_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2017-11-05 11:32:00.038400') ole_automation_date_object = ole_automation_date.OLEAutomationDate() date_time_string = ole_automation_date_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" ole_automation_date_object = ole_automation_date.OLEAutomationDate( timestamp=43044.480556) date_time_string = ole_automation_date_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2017-11-05T11:32:00.038400+00:00') def testGetDate(self): """Tests the GetDate function.""" ole_automation_date_object = ole_automation_date.OLEAutomationDate( timestamp=43044.480556) date_tuple = ole_automation_date_object.GetDate() self.assertEqual(date_tuple, (2017, 11, 5)) ole_automation_date_object = ole_automation_date.OLEAutomationDate() date_tuple = ole_automation_date_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" ole_automation_date_object = ole_automation_date.OLEAutomationDate( timestamp=43044.480556) date_with_time_of_day_tuple = ( ole_automation_date_object.GetDateWithTimeOfDay()) self.assertEqual(date_with_time_of_day_tuple, (2017, 11, 5, 11, 32, 0)) ole_automation_date_object = ole_automation_date.OLEAutomationDate() date_with_time_of_day_tuple = ( ole_automation_date_object.GetDateWithTimeOfDay()) self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" ole_automation_date_object = ole_automation_date.OLEAutomationDate( timestamp=43044.480556) time_of_day_tuple = ole_automation_date_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (11, 32, 0)) ole_automation_date_object = ole_automation_date.OLEAutomationDate() time_of_day_tuple = ole_automation_date_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/posix_time.py000066400000000000000000000601521461534226200177670ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the POSIX time implementation.""" import decimal import unittest from dfdatetime import posix_time class PosixTimeEpochTest(unittest.TestCase): """Tests for the POSIX time epoch.""" def testInitialize(self): """Tests the __init__ function.""" posix_epoch = posix_time.PosixTimeEpoch() self.assertIsNotNone(posix_epoch) class PosixTimeTest(unittest.TestCase): """Tests for the POSIX timestamp.""" # pylint: disable=protected-access def testProperties(self): """Tests the properties.""" posix_time_object = posix_time.PosixTime(timestamp=1281643591) self.assertEqual(posix_time_object.timestamp, 1281643591) posix_time_object = posix_time.PosixTime() self.assertIsNone(posix_time_object.timestamp) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" posix_time_object = posix_time.PosixTime(timestamp=1281643591) normalized_timestamp = posix_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643591.0')) posix_time_object = posix_time.PosixTime( time_zone_offset=60, timestamp=1281643591) normalized_timestamp = posix_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991.0')) posix_time_object = posix_time.PosixTime(timestamp=1281643591) posix_time_object.time_zone_offset = 60 normalized_timestamp = posix_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991.0')) posix_time_object = posix_time.PosixTime() normalized_timestamp = posix_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" posix_time_object = posix_time.PosixTime() posix_time_object.CopyFromDateTimeString('2010-08-12') self.assertEqual(posix_time_object._timestamp, 1281571200) self.assertEqual(posix_time_object._time_zone_offset, None) posix_time_object.CopyFromDateTimeString('2010-08-12 21:06:31') self.assertEqual(posix_time_object._timestamp, 1281647191) self.assertEqual(posix_time_object._time_zone_offset, None) posix_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') self.assertEqual(posix_time_object._timestamp, 1281647191) self.assertEqual(posix_time_object._time_zone_offset, None) posix_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875-01:00') self.assertEqual(posix_time_object._timestamp, 1281647191) self.assertEqual(posix_time_object._time_zone_offset, -60) posix_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875+01:00') self.assertEqual(posix_time_object._timestamp, 1281647191) self.assertEqual(posix_time_object._time_zone_offset, 60) posix_time_object.CopyFromDateTimeString('1601-01-02 00:00:00') self.assertEqual(posix_time_object._timestamp, -11644387200) self.assertEqual(posix_time_object._time_zone_offset, None) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" posix_time_object = posix_time.PosixTime(timestamp=1281643591) date_time_string = posix_time_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2010-08-12 20:06:31') posix_time_object = posix_time.PosixTime() date_time_string = posix_time_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" posix_time_object = posix_time.PosixTime(timestamp=1281643591) date_time_string = posix_time_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2010-08-12T20:06:31+00:00') posix_time_object = posix_time.PosixTime(timestamp=-11644468446) date_time_string = posix_time_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '1601-01-01T01:25:54+00:00') def testCopyToPosixTimestampWithFractionOfSecond(self): """Tests the CopyToPosixTimestampWithFractionOfSecond function.""" posix_time_object = posix_time.PosixTime(timestamp=1281643591) posix_timestamp, fraction_of_second = ( posix_time_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertEqual(posix_timestamp, 1281643591) self.assertIsNone(fraction_of_second) posix_time_object = posix_time.PosixTime(timestamp=-11644468446) posix_timestamp, fraction_of_second = ( posix_time_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertEqual(posix_timestamp, -11644468446) self.assertIsNone(fraction_of_second) posix_time_object = posix_time.PosixTime() posix_timestamp, fraction_of_second = ( posix_time_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertIsNone(posix_timestamp) self.assertIsNone(fraction_of_second) def testGetDate(self): """Tests the GetDate function.""" posix_time_object = posix_time.PosixTime(timestamp=1281643591) date_tuple = posix_time_object.GetDate() self.assertEqual(date_tuple, (2010, 8, 12)) posix_time_object = posix_time.PosixTime() date_tuple = posix_time_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" posix_time_object = posix_time.PosixTime(timestamp=1281643591) date_with_time_of_day_tuple = posix_time_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2010, 8, 12, 20, 6, 31)) posix_time_object = posix_time.PosixTime() date_with_time_of_day_tuple = posix_time_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" posix_time_object = posix_time.PosixTime(timestamp=1281643591) time_of_day_tuple = posix_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (20, 6, 31)) posix_time_object = posix_time.PosixTime() time_of_day_tuple = posix_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) class PosixTimeInMillisecondsTest(unittest.TestCase): """Tests for the POSIX timestamp in milliseconds.""" # pylint: disable=protected-access def testProperties(self): """Tests the properties.""" posix_time_object = posix_time.PosixTimeInMilliseconds( timestamp=1281643591546) self.assertEqual(posix_time_object.timestamp, 1281643591546) posix_time_object = posix_time.PosixTimeInMilliseconds() self.assertIsNone(posix_time_object.timestamp) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" posix_time_object = posix_time.PosixTimeInMilliseconds( timestamp=1281643591546) normalized_timestamp = posix_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643591.546')) posix_time_object = posix_time.PosixTimeInMilliseconds( time_zone_offset=60, timestamp=1281643591546) normalized_timestamp = posix_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991.546')) posix_time_object = posix_time.PosixTimeInMilliseconds( timestamp=1281643591546) posix_time_object.time_zone_offset = 60 normalized_timestamp = posix_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991.546')) posix_time_object = posix_time.PosixTimeInMilliseconds() normalized_timestamp = posix_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) # pylint: disable=protected-access def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" posix_time_object = posix_time.PosixTimeInMilliseconds() posix_time_object.CopyFromDateTimeString('2010-08-12') self.assertEqual(posix_time_object._timestamp, 1281571200000) self.assertEqual(posix_time_object._time_zone_offset, None) posix_time_object.CopyFromDateTimeString('2010-08-12 21:06:31') self.assertEqual(posix_time_object._timestamp, 1281647191000) self.assertEqual(posix_time_object._time_zone_offset, None) posix_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546') self.assertEqual(posix_time_object._timestamp, 1281647191546) self.assertEqual(posix_time_object._time_zone_offset, None) posix_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546-01:00') self.assertEqual(posix_time_object._timestamp, 1281647191546) self.assertEqual(posix_time_object._time_zone_offset, -60) posix_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546+01:00') self.assertEqual(posix_time_object._timestamp, 1281647191546) self.assertEqual(posix_time_object._time_zone_offset, 60) posix_time_object.CopyFromDateTimeString('1601-01-02 00:00:00') self.assertEqual(posix_time_object._timestamp, -11644387200000) self.assertEqual(posix_time_object._time_zone_offset, None) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" posix_time_object = posix_time.PosixTimeInMilliseconds( timestamp=1281643591546) date_time_string = posix_time_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2010-08-12 20:06:31.546') posix_time_object = posix_time.PosixTimeInMilliseconds() date_time_string = posix_time_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" posix_time_object = posix_time.PosixTimeInMilliseconds( timestamp=1281643591546) date_time_string = posix_time_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2010-08-12T20:06:31.546+00:00') posix_time_object = posix_time.PosixTimeInMilliseconds( timestamp=-11644468446327) date_time_string = posix_time_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '1601-01-01T01:25:53.673+00:00') def testCopyToPosixTimestampWithFractionOfSecond(self): """Tests the CopyToPosixTimestampWithFractionOfSecond function.""" posix_time_object = posix_time.PosixTimeInMilliseconds( timestamp=1281643591546) posix_timestamp, fraction_of_second = ( posix_time_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertEqual(posix_timestamp, 1281643591) self.assertEqual(fraction_of_second, 546) posix_time_object = posix_time.PosixTimeInMilliseconds( timestamp=-11644468446327) posix_timestamp, fraction_of_second = ( posix_time_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertEqual(posix_timestamp, -11644468446) self.assertEqual(fraction_of_second, -327) posix_time_object = posix_time.PosixTime() posix_timestamp, fraction_of_second = ( posix_time_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertIsNone(posix_timestamp) self.assertIsNone(fraction_of_second) def testGetDate(self): """Tests the GetDate function.""" posix_time_object = posix_time.PosixTimeInMilliseconds( timestamp=1281643591546) date_tuple = posix_time_object.GetDate() self.assertEqual(date_tuple, (2010, 8, 12)) posix_time_object = posix_time.PosixTimeInMilliseconds() date_tuple = posix_time_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" posix_time_object = posix_time.PosixTimeInMilliseconds( timestamp=1281643591546) date_with_time_of_day_tuple = posix_time_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2010, 8, 12, 20, 6, 31)) posix_time_object = posix_time.PosixTimeInMilliseconds() date_with_time_of_day_tuple = posix_time_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" posix_time_object = posix_time.PosixTimeInMilliseconds( timestamp=1281643591546) time_of_day_tuple = posix_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (20, 6, 31)) posix_time_object = posix_time.PosixTimeInMilliseconds() time_of_day_tuple = posix_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) class PosixTimeInMicrosecondsTest(unittest.TestCase): """Tests for the POSIX timestamp in microseconds.""" # pylint: disable=protected-access def testProperties(self): """Tests the properties.""" posix_time_object = posix_time.PosixTimeInMicroseconds( timestamp=1281643591546875) self.assertEqual(posix_time_object.timestamp, 1281643591546875) posix_time_object = posix_time.PosixTimeInMicroseconds() self.assertIsNone(posix_time_object.timestamp) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" posix_time_object = posix_time.PosixTimeInMicroseconds( timestamp=1281643591546875) normalized_timestamp = posix_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643591.546875')) posix_time_object = posix_time.PosixTimeInMicroseconds( time_zone_offset=60, timestamp=1281643591546875) normalized_timestamp = posix_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991.546875')) posix_time_object = posix_time.PosixTimeInMicroseconds( timestamp=1281643591546875) posix_time_object.time_zone_offset = 60 normalized_timestamp = posix_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991.546875')) posix_time_object = posix_time.PosixTimeInMicroseconds() normalized_timestamp = posix_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) # pylint: disable=protected-access def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" posix_time_object = posix_time.PosixTimeInMicroseconds() posix_time_object.CopyFromDateTimeString('2010-08-12') self.assertEqual(posix_time_object._timestamp, 1281571200000000) self.assertEqual(posix_time_object._time_zone_offset, None) posix_time_object.CopyFromDateTimeString('2010-08-12 21:06:31') self.assertEqual(posix_time_object._timestamp, 1281647191000000) self.assertEqual(posix_time_object._time_zone_offset, None) posix_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') self.assertEqual(posix_time_object._timestamp, 1281647191546875) self.assertEqual(posix_time_object._time_zone_offset, None) posix_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875-01:00') self.assertEqual(posix_time_object._timestamp, 1281647191546875) self.assertEqual(posix_time_object._time_zone_offset, -60) posix_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875+01:00') self.assertEqual(posix_time_object._timestamp, 1281647191546875) self.assertEqual(posix_time_object._time_zone_offset, 60) posix_time_object.CopyFromDateTimeString('1601-01-02 00:00:00') self.assertEqual(posix_time_object._timestamp, -11644387200000000) self.assertEqual(posix_time_object._time_zone_offset, None) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" posix_time_object = posix_time.PosixTimeInMicroseconds( timestamp=1281643591546875) date_time_string = posix_time_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2010-08-12 20:06:31.546875') posix_time_object = posix_time.PosixTimeInMicroseconds() date_time_string = posix_time_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" posix_time_object = posix_time.PosixTimeInMicroseconds( timestamp=1281643591546875) date_time_string = posix_time_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2010-08-12T20:06:31.546875+00:00') posix_time_object = posix_time.PosixTimeInMicroseconds( timestamp=-11644468446327447) date_time_string = posix_time_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '1601-01-01T01:25:53.672553+00:00') def testCopyToPosixTimestampWithFractionOfSecond(self): """Tests the CopyToPosixTimestampWithFractionOfSecond function.""" posix_time_object = posix_time.PosixTimeInMicroseconds( timestamp=1281643591546875) posix_timestamp, fraction_of_second = ( posix_time_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertEqual(posix_timestamp, 1281643591) self.assertEqual(fraction_of_second, 546875) posix_time_object = posix_time.PosixTimeInMicroseconds( timestamp=-11644468446327447) posix_timestamp, fraction_of_second = ( posix_time_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertEqual(posix_timestamp, -11644468446) self.assertEqual(fraction_of_second, -327447) posix_time_object = posix_time.PosixTime() posix_timestamp, fraction_of_second = ( posix_time_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertIsNone(posix_timestamp) self.assertIsNone(fraction_of_second) def testGetDate(self): """Tests the GetDate function.""" posix_time_object = posix_time.PosixTimeInMicroseconds( timestamp=1281643591546875) date_tuple = posix_time_object.GetDate() self.assertEqual(date_tuple, (2010, 8, 12)) posix_time_object = posix_time.PosixTimeInMicroseconds() date_tuple = posix_time_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" posix_time_object = posix_time.PosixTimeInMicroseconds( timestamp=1281643591546875) date_with_time_of_day_tuple = posix_time_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2010, 8, 12, 20, 6, 31)) posix_time_object = posix_time.PosixTimeInMicroseconds() date_with_time_of_day_tuple = posix_time_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" posix_time_object = posix_time.PosixTimeInMicroseconds( timestamp=1281643591546875) time_of_day_tuple = posix_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (20, 6, 31)) posix_time_object = posix_time.PosixTimeInMicroseconds() time_of_day_tuple = posix_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) class PosixTimeInNanoSecondsTest(unittest.TestCase): """Tests for the POSIX timestamp in nanoseconds.""" # pylint: disable=protected-access def testProperties(self): """Tests the properties.""" posix_time_object = posix_time.PosixTimeInNanoseconds( timestamp=1281643591987654321) self.assertEqual(posix_time_object.timestamp, 1281643591987654321) posix_time_object = posix_time.PosixTimeInNanoseconds() self.assertIsNone(posix_time_object.timestamp) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" posix_time_object = posix_time.PosixTimeInNanoseconds( timestamp=1281643591987654321) normalized_timestamp = posix_time_object._GetNormalizedTimestamp() self.assertEqual( normalized_timestamp, decimal.Decimal('1281643591.987654321')) posix_time_object = posix_time.PosixTimeInNanoseconds( time_zone_offset=60, timestamp=1281643591987654321) normalized_timestamp = posix_time_object._GetNormalizedTimestamp() self.assertEqual( normalized_timestamp, decimal.Decimal('1281639991.987654321')) posix_time_object = posix_time.PosixTimeInNanoseconds( timestamp=1281643591987654321) posix_time_object.time_zone_offset = 60 normalized_timestamp = posix_time_object._GetNormalizedTimestamp() self.assertEqual( normalized_timestamp, decimal.Decimal('1281639991.987654321')) posix_time_object = posix_time.PosixTimeInNanoseconds() normalized_timestamp = posix_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" posix_time_object = posix_time.PosixTimeInNanoseconds() posix_time_object.CopyFromDateTimeString('2010-08-12') self.assertEqual(posix_time_object.timestamp, 1281571200000000000) self.assertEqual(posix_time_object._time_zone_offset, None) posix_time_object.CopyFromDateTimeString('2010-08-12 21:06:31') self.assertEqual(posix_time_object.timestamp, 1281647191000000000) self.assertEqual(posix_time_object._time_zone_offset, None) posix_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.654321') self.assertEqual(posix_time_object.timestamp, 1281647191654321000) self.assertEqual(posix_time_object._time_zone_offset, None) posix_time_object.CopyFromDateTimeString( '2010-08-12 21:06:31.654321-01:00') self.assertEqual(posix_time_object.timestamp, 1281647191654321000) self.assertEqual(posix_time_object._time_zone_offset, -60) posix_time_object.CopyFromDateTimeString( '2010-08-12 21:06:31.654321+01:00') self.assertEqual(posix_time_object.timestamp, 1281647191654321000) self.assertEqual(posix_time_object._time_zone_offset, 60) posix_time_object.CopyFromDateTimeString('1601-01-02 00:00:00') self.assertEqual(posix_time_object.timestamp, -11644387200000000000) self.assertEqual(posix_time_object._time_zone_offset, None) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" posix_time_object = posix_time.PosixTimeInNanoseconds( timestamp=1281643591987654321) date_time_string = posix_time_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2010-08-12 20:06:31.987654321') posix_time_object = posix_time.PosixTimeInNanoseconds() date_time_string = posix_time_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" posix_time_object = posix_time.PosixTimeInNanoseconds( timestamp=1281643591987654321) date_time_string = posix_time_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2010-08-12T20:06:31.987654321+00:00') def testGetDate(self): """Tests the GetDate function.""" posix_time_object = posix_time.PosixTimeInNanoseconds( timestamp=1281643591987654321) date_tuple = posix_time_object.GetDate() self.assertEqual(date_tuple, (2010, 8, 12)) posix_time_object = posix_time.PosixTimeInNanoseconds() date_tuple = posix_time_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" posix_time_object = posix_time.PosixTimeInNanoseconds( timestamp=1281643591987654321) date_with_time_of_day_tuple = posix_time_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2010, 8, 12, 20, 6, 31)) posix_time_object = posix_time.PosixTimeInNanoseconds() date_with_time_of_day_tuple = posix_time_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" posix_time_object = posix_time.PosixTimeInNanoseconds( timestamp=1281643591987654321) time_of_day_tuple = posix_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (20, 6, 31)) posix_time_object = posix_time.PosixTimeInNanoseconds() time_of_day_tuple = posix_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/precisions.py000066400000000000000000000176171461534226200177750ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for date and time precision helpers.""" import decimal import unittest from dfdatetime import definitions from dfdatetime import precisions class DateTimePrecisionHelperTest(unittest.TestCase): """Tests for the date time precision helper interface.""" def testCopyNanosecondsToFractionOfSecond(self): """Tests the CopyNanosecondsToFractionOfSecond function.""" precision_helper = precisions.DateTimePrecisionHelper with self.assertRaises(NotImplementedError): precision_helper.CopyNanosecondsToFractionOfSecond(0) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" precision_helper = precisions.DateTimePrecisionHelper with self.assertRaises(NotImplementedError): precision_helper.CopyToDateTimeString((2018, 1, 2, 19, 45, 12), 0.5) class SecondsPrecisionHelperTest(unittest.TestCase): """Tests for the seconds precision helper.""" def testCopyNanosecondsToFractionOfSecond(self): """Tests the CopyNanosecondsToFractionOfSecond function.""" precision_helper = precisions.SecondsPrecisionHelper fraction_of_second = precision_helper.CopyNanosecondsToFractionOfSecond( 123456) self.assertEqual(fraction_of_second, 0.0) with self.assertRaises(ValueError): precision_helper.CopyNanosecondsToFractionOfSecond(-1) with self.assertRaises(ValueError): precision_helper.CopyNanosecondsToFractionOfSecond(1000000000) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" precision_helper = precisions.SecondsPrecisionHelper date_time_string = precision_helper.CopyToDateTimeString( (2018, 1, 2, 19, 45, 12), 0.123456) self.assertEqual(date_time_string, '2018-01-02 19:45:12') with self.assertRaises(ValueError): precision_helper.CopyToDateTimeString((2018, 1, 2, 19, 45, 12), 4.123456) class CentisecondsPrevisionHelperTest(unittest.TestCase): """Tests for the centiseconds prevision helper.""" def testCopyNanosecondsToFractionOfSecond(self): """Tests the CopyNanosecondsToFractionOfSecond function.""" precision_helper = precisions.CentisecondsPrecisionHelper fraction_of_second = precision_helper.CopyNanosecondsToFractionOfSecond( 123456789) self.assertEqual(fraction_of_second, decimal.Decimal('0.12')) with self.assertRaises(ValueError): precision_helper.CopyNanosecondsToFractionOfSecond(-1) with self.assertRaises(ValueError): precision_helper.CopyNanosecondsToFractionOfSecond(1000000000) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" precision_helper = precisions.CentisecondsPrecisionHelper date_time_string = precision_helper.CopyToDateTimeString( (2018, 1, 2, 19, 45, 12), 0.123456) self.assertEqual(date_time_string, '2018-01-02 19:45:12.12') with self.assertRaises(ValueError): precision_helper.CopyToDateTimeString((2018, 1, 2, 19, 45, 12), 4.123456) class MillisecondsPrecisionHelperTest(unittest.TestCase): """Tests for the milliseconds precision helper.""" def testCopyNanosecondsToFractionOfSecond(self): """Tests the CopyNanosecondsToFractionOfSecond function.""" precision_helper = precisions.MillisecondsPrecisionHelper fraction_of_second = precision_helper.CopyNanosecondsToFractionOfSecond( 123456789) self.assertEqual(fraction_of_second, decimal.Decimal('0.123')) with self.assertRaises(ValueError): precision_helper.CopyNanosecondsToFractionOfSecond(-1) with self.assertRaises(ValueError): precision_helper.CopyNanosecondsToFractionOfSecond(1000000000) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" precision_helper = precisions.MillisecondsPrecisionHelper date_time_string = precision_helper.CopyToDateTimeString( (2018, 1, 2, 19, 45, 12), 0.123456) self.assertEqual(date_time_string, '2018-01-02 19:45:12.123') with self.assertRaises(ValueError): precision_helper.CopyToDateTimeString((2018, 1, 2, 19, 45, 12), 4.123456) class DeciMillisecondsPrevisionHelperTest(unittest.TestCase): """Tests for the decimilliseconds precision helper.""" def testCopyNanosecondsToFractionOfSecond(self): """Tests the CopyNanosecondsToFractionOfSecond function.""" precision_helper = precisions.DecimillisecondsPrecisionHelper fraction_of_second = precision_helper.CopyNanosecondsToFractionOfSecond( 123456789) self.assertEqual(fraction_of_second, decimal.Decimal('0.1234')) with self.assertRaises(ValueError): precision_helper.CopyNanosecondsToFractionOfSecond(-1) with self.assertRaises(ValueError): precision_helper.CopyNanosecondsToFractionOfSecond(1000000000) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" precision_helper = precisions.DecimillisecondsPrecisionHelper date_time_string = precision_helper.CopyToDateTimeString( (2018, 1, 2, 19, 45, 12), 0.123456) self.assertEqual(date_time_string, '2018-01-02 19:45:12.1234') with self.assertRaises(ValueError): precision_helper.CopyToDateTimeString((2018, 1, 2, 19, 45, 12), 4.123456) class MicrosecondsPrecisionHelperTest(unittest.TestCase): """Tests for the microseconds precision helper.""" def testCopyNanosecondsToFractionOfSecond(self): """Tests the CopyNanosecondsToFractionOfSecond function.""" precision_helper = precisions.MicrosecondsPrecisionHelper fraction_of_second = precision_helper.CopyNanosecondsToFractionOfSecond( 123456789) self.assertEqual(fraction_of_second, decimal.Decimal('0.123456')) with self.assertRaises(ValueError): precision_helper.CopyNanosecondsToFractionOfSecond(-1) with self.assertRaises(ValueError): precision_helper.CopyNanosecondsToFractionOfSecond(1000000000) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" precision_helper = precisions.MicrosecondsPrecisionHelper date_time_string = precision_helper.CopyToDateTimeString( (2018, 1, 2, 19, 45, 12), 0.123456) self.assertEqual(date_time_string, '2018-01-02 19:45:12.123456') with self.assertRaises(ValueError): precision_helper.CopyToDateTimeString((2018, 1, 2, 19, 45, 12), 4.123456) class NanosecondsPrecisionHelperTest(unittest.TestCase): """Tests for the nanoseconds precision helper.""" def testCopyNanosecondsToFractionOfSecond(self): """Tests the CopyNanosecondsToFractionOfSecond function.""" precision_helper = precisions.NanosecondsPrecisionHelper fraction_of_second = precision_helper.CopyNanosecondsToFractionOfSecond( 123456789) self.assertEqual(fraction_of_second, decimal.Decimal('0.123456789')) with self.assertRaises(ValueError): precision_helper.CopyNanosecondsToFractionOfSecond(-1) with self.assertRaises(ValueError): precision_helper.CopyNanosecondsToFractionOfSecond(1000000000) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" precision_helper = precisions.NanosecondsPrecisionHelper date_time_string = precision_helper.CopyToDateTimeString( (2018, 1, 2, 19, 45, 12), 0.123456789) self.assertEqual(date_time_string, '2018-01-02 19:45:12.123456789') with self.assertRaises(ValueError): precision_helper.CopyToDateTimeString( (2018, 1, 2, 19, 45, 12), 4.123456789) class PrecisionHelperFactoryTest(unittest.TestCase): """Tests for the date time precision helper factory.""" def testCreatePrecisionHelper(self): """Tests the CreatePrecisionHelper function.""" precision_helper = precisions.PrecisionHelperFactory.CreatePrecisionHelper( definitions.PRECISION_1_MICROSECOND) self.assertIsNotNone(precision_helper) with self.assertRaises(ValueError): precisions.PrecisionHelperFactory.CreatePrecisionHelper('bogus') if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/rfc2579_date_time.py000066400000000000000000000274071461534226200207310ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the RFC2579 date-time implementation.""" import decimal import unittest from dfdatetime import rfc2579_date_time class RFC2579DateTimeInvalidYear(rfc2579_date_time.RFC2579DateTime): """RFC2579 date-time for testing invalid year.""" def _CopyDateTimeFromString(self, time_string): """Copies a date and time from a string. Args: time_string (str): date and time value formatted as: YYYY-MM-DD hh:mm:ss.######[+-]##:## Where # are numeric digits ranging from 0 to 9 and the seconds fraction can be either 3 or 6 digits. The time of day, seconds fraction and time zone offset are optional. The default time zone is UTC. Returns: dict[str, int]: date and time values, such as year, month, day of month, hours, minutes, seconds, microseconds. Raises: ValueError: if the time string is invalid or not supported. """ return { 'year': 70000, 'month': 1, 'day_of_month': 2, 'hours': 0, 'minutes': 0, 'seconds': 0} class RFC2579DateTimeTest(unittest.TestCase): """Tests for the RFC2579 date-time.""" # pylint: disable=protected-access def testInitialize(self): """Tests the initialization function.""" rfc2579_date_time_object = rfc2579_date_time.RFC2579DateTime() self.assertIsNotNone(rfc2579_date_time_object) rfc2579_date_time_object = rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 20, 6, 31, 6, '+', 2, 0)) self.assertIsNotNone(rfc2579_date_time_object) self.assertEqual(rfc2579_date_time_object.year, 2010) self.assertEqual(rfc2579_date_time_object.month, 8) self.assertEqual(rfc2579_date_time_object.day_of_month, 12) self.assertEqual(rfc2579_date_time_object.hours, 20) self.assertEqual(rfc2579_date_time_object.minutes, 6) self.assertEqual(rfc2579_date_time_object.seconds, 31) self.assertEqual(rfc2579_date_time_object.deciseconds, 6) self.assertEqual(rfc2579_date_time_object.time_zone_offset, 120) with self.assertRaises(ValueError): rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 20, 6, 31)) with self.assertRaises(ValueError): rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(65537, 8, 12, 20, 6, 31, 6, '+', 2, 0)) with self.assertRaises(ValueError): rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 13, 12, 20, 6, 31, 6, '+', 2, 0)) with self.assertRaises(ValueError): rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 32, 20, 6, 31, 6, '+', 2, 0)) with self.assertRaises(ValueError): rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 24, 6, 31, 6, '+', 2, 0)) with self.assertRaises(ValueError): rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 20, 61, 31, 6, '+', 2, 0)) with self.assertRaises(ValueError): rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 20, 6, 61, 6, '+', 2, 0)) with self.assertRaises(ValueError): rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 20, 6, 31, 11, '+', 2, 0)) with self.assertRaises(ValueError): rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 20, 6, 31, 6, '#', 2, 0)) with self.assertRaises(ValueError): rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 20, 6, 31, 6, '+', 14, 0)) with self.assertRaises(ValueError): rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 20, 6, 31, 6, '+', 2, 60)) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" rfc2579_date_time_object = rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 20, 6, 31, 6, '+', 0, 0)) normalized_timestamp = rfc2579_date_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643591.6')) rfc2579_date_time_object = rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 20, 6, 31, 6, '+', 1, 0)) normalized_timestamp = rfc2579_date_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991.6')) rfc2579_date_time_object = rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 20, 6, 31, 6, '+', 0, 0)) rfc2579_date_time_object.time_zone_offset = 60 normalized_timestamp = rfc2579_date_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991.6')) rfc2579_date_time_object = rfc2579_date_time.RFC2579DateTime() normalized_timestamp = rfc2579_date_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" rfc2579_date_time_object = rfc2579_date_time.RFC2579DateTime() rfc2579_date_time_object.CopyFromDateTimeString('2010-08-12') self.assertEqual(rfc2579_date_time_object._number_of_seconds, 1281571200) self.assertEqual(rfc2579_date_time_object._time_zone_offset, None) self.assertEqual(rfc2579_date_time_object.year, 2010) self.assertEqual(rfc2579_date_time_object.month, 8) self.assertEqual(rfc2579_date_time_object.day_of_month, 12) self.assertEqual(rfc2579_date_time_object.hours, 0) self.assertEqual(rfc2579_date_time_object.minutes, 0) self.assertEqual(rfc2579_date_time_object.seconds, 0) self.assertEqual(rfc2579_date_time_object.deciseconds, 0) rfc2579_date_time_object.CopyFromDateTimeString('2010-08-12 21:06:31') self.assertEqual(rfc2579_date_time_object._number_of_seconds, 1281647191) self.assertEqual(rfc2579_date_time_object._time_zone_offset, None) self.assertEqual(rfc2579_date_time_object.year, 2010) self.assertEqual(rfc2579_date_time_object.month, 8) self.assertEqual(rfc2579_date_time_object.day_of_month, 12) self.assertEqual(rfc2579_date_time_object.hours, 21) self.assertEqual(rfc2579_date_time_object.minutes, 6) self.assertEqual(rfc2579_date_time_object.seconds, 31) self.assertEqual(rfc2579_date_time_object.deciseconds, 0) rfc2579_date_time_object.CopyFromDateTimeString( '2010-08-12 21:06:31.546875') self.assertEqual(rfc2579_date_time_object._number_of_seconds, 1281647191) self.assertEqual(rfc2579_date_time_object._time_zone_offset, None) self.assertEqual(rfc2579_date_time_object.year, 2010) self.assertEqual(rfc2579_date_time_object.month, 8) self.assertEqual(rfc2579_date_time_object.day_of_month, 12) self.assertEqual(rfc2579_date_time_object.hours, 21) self.assertEqual(rfc2579_date_time_object.minutes, 6) self.assertEqual(rfc2579_date_time_object.seconds, 31) self.assertEqual(rfc2579_date_time_object.deciseconds, 5) rfc2579_date_time_object.CopyFromDateTimeString( '2010-08-12 21:06:31.546875-01:00') self.assertEqual(rfc2579_date_time_object._number_of_seconds, 1281647191) self.assertEqual(rfc2579_date_time_object._time_zone_offset, -60) self.assertEqual(rfc2579_date_time_object.year, 2010) self.assertEqual(rfc2579_date_time_object.month, 8) self.assertEqual(rfc2579_date_time_object.day_of_month, 12) self.assertEqual(rfc2579_date_time_object.hours, 21) self.assertEqual(rfc2579_date_time_object.minutes, 6) self.assertEqual(rfc2579_date_time_object.seconds, 31) self.assertEqual(rfc2579_date_time_object.deciseconds, 5) self.assertEqual(rfc2579_date_time_object.time_zone_offset, -60) rfc2579_date_time_object.CopyFromDateTimeString( '2010-08-12 21:06:31.546875+01:00') self.assertEqual(rfc2579_date_time_object._number_of_seconds, 1281647191) self.assertEqual(rfc2579_date_time_object._time_zone_offset, 60) self.assertEqual(rfc2579_date_time_object.year, 2010) self.assertEqual(rfc2579_date_time_object.month, 8) self.assertEqual(rfc2579_date_time_object.day_of_month, 12) self.assertEqual(rfc2579_date_time_object.hours, 21) self.assertEqual(rfc2579_date_time_object.minutes, 6) self.assertEqual(rfc2579_date_time_object.seconds, 31) self.assertEqual(rfc2579_date_time_object.deciseconds, 5) self.assertEqual(rfc2579_date_time_object.time_zone_offset, 60) rfc2579_date_time_object.CopyFromDateTimeString('1601-01-02 00:00:00') self.assertEqual(rfc2579_date_time_object._number_of_seconds, -11644387200) self.assertEqual(rfc2579_date_time_object._time_zone_offset, None) self.assertEqual(rfc2579_date_time_object.year, 1601) self.assertEqual(rfc2579_date_time_object.month, 1) self.assertEqual(rfc2579_date_time_object.day_of_month, 2) self.assertEqual(rfc2579_date_time_object.hours, 0) self.assertEqual(rfc2579_date_time_object.minutes, 0) self.assertEqual(rfc2579_date_time_object.seconds, 0) self.assertEqual(rfc2579_date_time_object.deciseconds, 0) rfc2579_date_time_object = RFC2579DateTimeInvalidYear() with self.assertRaises(ValueError): rfc2579_date_time_object.CopyFromDateTimeString('9999-01-02 00:00:00') def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" rfc2579_date_time_object = rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 20, 6, 31, 6, '+', 0, 0)) date_time_string = rfc2579_date_time_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2010-08-12 20:06:31.6') rfc2579_date_time_object = rfc2579_date_time.RFC2579DateTime() date_time_string = rfc2579_date_time_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" rfc2579_date_time_object = rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 20, 6, 31, 6, '+', 0, 0)) date_time_string = rfc2579_date_time_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2010-08-12T20:06:31.6+00:00') def testGetDate(self): """Tests the GetDate function.""" rfc2579_date_time_object = rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 20, 6, 31, 6, '+', 0, 0)) date_tuple = rfc2579_date_time_object.GetDate() self.assertEqual(date_tuple, (2010, 8, 12)) rfc2579_date_time_object = rfc2579_date_time.RFC2579DateTime() date_tuple = rfc2579_date_time_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" rfc2579_date_time_object = rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 20, 6, 31, 6, '+', 0, 0)) date_with_time_of_day_tuple = ( rfc2579_date_time_object.GetDateWithTimeOfDay()) self.assertEqual(date_with_time_of_day_tuple, (2010, 8, 12, 20, 6, 31)) rfc2579_date_time_object = rfc2579_date_time.RFC2579DateTime() date_with_time_of_day_tuple = ( rfc2579_date_time_object.GetDateWithTimeOfDay()) self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" rfc2579_date_time_object = rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 20, 6, 31, 6, '+', 0, 0)) time_of_day_tuple = rfc2579_date_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (20, 6, 31)) rfc2579_date_time_object = rfc2579_date_time.RFC2579DateTime() time_of_day_tuple = rfc2579_date_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/semantic_time.py000066400000000000000000000174531461534226200204360ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the semantic time implementation.""" import unittest from dfdatetime import semantic_time from tests import interface class SemanticTimeTest(unittest.TestCase): """Tests for semantic time.""" # pylint: disable=assignment-from-none,protected-access def testComparison(self): """Tests the comparison functions.""" semantic_time_object1 = semantic_time.SemanticTime() setattr(semantic_time_object1, '_SORT_ORDER', 1) semantic_time_object2 = semantic_time.SemanticTime() setattr(semantic_time_object2, '_SORT_ORDER', 1) self.assertTrue(semantic_time_object1 == semantic_time_object2) self.assertTrue(semantic_time_object1 >= semantic_time_object2) self.assertFalse(semantic_time_object1 > semantic_time_object2) self.assertTrue(semantic_time_object1 <= semantic_time_object2) self.assertFalse(semantic_time_object1 < semantic_time_object2) self.assertFalse(semantic_time_object1 != semantic_time_object2) semantic_time_object2 = semantic_time.SemanticTime() setattr(semantic_time_object2, '_SORT_ORDER', 2) self.assertFalse(semantic_time_object1 == semantic_time_object2) self.assertFalse(semantic_time_object1 >= semantic_time_object2) self.assertFalse(semantic_time_object1 > semantic_time_object2) self.assertTrue(semantic_time_object1 <= semantic_time_object2) self.assertTrue(semantic_time_object1 < semantic_time_object2) self.assertTrue(semantic_time_object1 != semantic_time_object2) date_time_values1 = interface.TestDateTimeValues() self.assertFalse(semantic_time_object1 == date_time_values1) self.assertFalse(semantic_time_object1 >= date_time_values1) self.assertFalse(semantic_time_object1 > date_time_values1) self.assertTrue(semantic_time_object1 <= date_time_values1) self.assertTrue(semantic_time_object1 < date_time_values1) self.assertTrue(semantic_time_object1 != date_time_values1) self.assertFalse(semantic_time_object1 == 0.0) with self.assertRaises(ValueError): semantic_time_object1 >= 0.0 # pylint: disable=pointless-statement with self.assertRaises(ValueError): semantic_time_object1 > 0.0 # pylint: disable=pointless-statement with self.assertRaises(ValueError): semantic_time_object1 <= 0.0 # pylint: disable=pointless-statement with self.assertRaises(ValueError): semantic_time_object1 < 0.0 # pylint: disable=pointless-statement self.assertTrue(semantic_time_object1 != 0.0) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" semantic_time_object = semantic_time.SemanticTime() semantic_time_object.CopyFromDateTimeString('Never') self.assertEqual(semantic_time_object.string, 'Never') def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" semantic_time_object = semantic_time.SemanticTime(string='Never') date_time_string = semantic_time_object.CopyToDateTimeString() self.assertEqual(date_time_string, 'Never') def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" semantic_time_object = semantic_time.SemanticTime(string='Never') date_time_string = semantic_time_object.CopyToDateTimeStringISO8601() self.assertIsNone(date_time_string) def testCopyToPosixTimestamp(self): """Tests the CopyToPosixTimestamp function.""" semantic_time_object = semantic_time.SemanticTime() posix_timestamp = semantic_time_object.CopyToPosixTimestamp() self.assertIsNone(posix_timestamp) def testCopyToPosixTimestampWithFractionOfSecond(self): """Tests the CopyToPosixTimestampWithFractionOfSecond function.""" semantic_time_object = semantic_time.SemanticTime() posix_timestamp, fraction_of_second = ( semantic_time_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertIsNone(posix_timestamp) self.assertIsNone(fraction_of_second) def testGetDate(self): """Tests the GetDate function.""" semantic_time_object = semantic_time.SemanticTime() date_tuple = semantic_time_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" semantic_time_object = semantic_time.SemanticTime() date_with_time_of_day_tuple = semantic_time_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" semantic_time_object = semantic_time.SemanticTime() time_of_day_tuple = semantic_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) def testGetPlasoTimestamp(self): """Tests the GetPlasoTimestamp function.""" semantic_time_object = semantic_time.SemanticTime() micro_posix_timestamp = semantic_time_object.GetPlasoTimestamp() self.assertEqual(micro_posix_timestamp, 0) class InvalidTimeTest(unittest.TestCase): """Tests for semantic time that represents invalid..""" def testInitialize(self): """Tests the __init__ function.""" invalid_time_object = semantic_time.InvalidTime() self.assertEqual(invalid_time_object.string, 'Invalid') class NeverTest(unittest.TestCase): """Tests for semantic time that represents never.""" # pylint: disable=protected-access def testInitialize(self): """Tests the __init__ function.""" never_time_object = semantic_time.Never() self.assertEqual(never_time_object.string, 'Never') def testComparison(self): """Tests the comparison functions.""" never_time_object1 = semantic_time.Never() never_time_object2 = semantic_time.Never() self.assertTrue(never_time_object1 == never_time_object2) self.assertTrue(never_time_object1 >= never_time_object2) self.assertFalse(never_time_object1 > never_time_object2) self.assertTrue(never_time_object1 <= never_time_object2) self.assertFalse(never_time_object1 < never_time_object2) self.assertFalse(never_time_object1 != never_time_object2) semantic_time_object2 = semantic_time.SemanticTime() setattr(semantic_time_object2, '_SORT_ORDER', 1) self.assertFalse(never_time_object1 == semantic_time_object2) self.assertTrue(never_time_object1 >= semantic_time_object2) self.assertTrue(never_time_object1 > semantic_time_object2) self.assertFalse(never_time_object1 <= semantic_time_object2) self.assertFalse(never_time_object1 < semantic_time_object2) self.assertTrue(never_time_object1 != semantic_time_object2) date_time_values1 = interface.TestDateTimeValues() self.assertFalse(never_time_object1 == date_time_values1) self.assertTrue(never_time_object1 >= date_time_values1) self.assertTrue(never_time_object1 > date_time_values1) self.assertFalse(never_time_object1 <= date_time_values1) self.assertFalse(never_time_object1 < date_time_values1) self.assertTrue(never_time_object1 != date_time_values1) self.assertFalse(never_time_object1 == 0.0) with self.assertRaises(ValueError): never_time_object1 >= 0.0 # pylint: disable=pointless-statement with self.assertRaises(ValueError): never_time_object1 > 0.0 # pylint: disable=pointless-statement with self.assertRaises(ValueError): never_time_object1 <= 0.0 # pylint: disable=pointless-statement with self.assertRaises(ValueError): never_time_object1 < 0.0 # pylint: disable=pointless-statement self.assertTrue(never_time_object1 != 0.0) class NotSetTest(unittest.TestCase): """Tests for semantic time that represents not set.""" def testInitialize(self): """Tests the __init__ function.""" not_set_time_object = semantic_time.NotSet() self.assertEqual(not_set_time_object.string, 'Not set') if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/serializer.py000066400000000000000000000257751461534226200177740ustar00rootroot00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Tests for the date and time values serializer.""" import unittest from dfdatetime import dotnet_datetime from dfdatetime import fat_date_time from dfdatetime import golang_time from dfdatetime import posix_time from dfdatetime import rfc2579_date_time from dfdatetime import semantic_time from dfdatetime import serializer from dfdatetime import systemtime from dfdatetime import time_elements class SerializerTest(unittest.TestCase): """Tests for the date and time values serializer.""" def testConvertDateTimeValuesToJSON(self): """Test ConvertDateTimeValuesToJSON function.""" posix_time_object = posix_time.PosixTime(timestamp=1281643591) expected_json_dict = { '__class_name__': 'PosixTime', '__type__': 'DateTimeValues', 'timestamp': 1281643591} json_dict = serializer.Serializer.ConvertDateTimeValuesToJSON( posix_time_object) self.assertEqual(json_dict, expected_json_dict) posix_time_object.is_local_time = True posix_time_object.time_zone_hint = 'Europe/Amsterdam' expected_json_dict = { '__class_name__': 'PosixTime', '__type__': 'DateTimeValues', 'is_local_time': True, 'time_zone_hint': 'Europe/Amsterdam', 'timestamp': 1281643591} json_dict = serializer.Serializer.ConvertDateTimeValuesToJSON( posix_time_object) self.assertEqual(json_dict, expected_json_dict) posix_time_object = posix_time.PosixTime( timestamp=1281643591, time_zone_offset=60) expected_json_dict = { '__class_name__': 'PosixTime', '__type__': 'DateTimeValues', 'timestamp': 1281643591, 'time_zone_offset': 60} json_dict = serializer.Serializer.ConvertDateTimeValuesToJSON( posix_time_object) self.assertEqual(json_dict, expected_json_dict) never_time_object = semantic_time.Never() expected_json_dict = { '__class_name__': 'Never', '__type__': 'DateTimeValues', 'string': 'Never'} json_dict = serializer.Serializer.ConvertDateTimeValuesToJSON( never_time_object) self.assertEqual(json_dict, expected_json_dict) dotnet_datetime_object = dotnet_datetime.DotNetDateTime( timestamp=637433719321230000) expected_json_dict = { '__class_name__': 'DotNetDateTime', '__type__': 'DateTimeValues', 'timestamp': 637433719321230000} json_dict = serializer.Serializer.ConvertDateTimeValuesToJSON( dotnet_datetime_object) self.assertEqual(json_dict, expected_json_dict) fat_date_time_object = fat_date_time.FATDateTime(fat_date_time=0xa8d03d0c) expected_json_dict = { '__class_name__': 'FATDateTime', '__type__': 'DateTimeValues', 'fat_date_time': 2832219404} json_dict = serializer.Serializer.ConvertDateTimeValuesToJSON( fat_date_time_object) self.assertEqual(json_dict, expected_json_dict) golang_timestamp = bytes.fromhex('01000000000000000200000003ffff') golang_time_object = golang_time.GolangTime( golang_timestamp=golang_timestamp) expected_json_dict = { '__class_name__': 'GolangTime', '__type__': 'DateTimeValues', 'golang_timestamp': ( b'\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff')} json_dict = serializer.Serializer.ConvertDateTimeValuesToJSON( golang_time_object) self.assertEqual(json_dict, expected_json_dict) rfc2579_date_time_object = rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 20, 6, 31, 6, '+', 2, 0)) expected_json_dict = { '__class_name__': 'RFC2579DateTime', '__type__': 'DateTimeValues', 'rfc2579_date_time_tuple': (2010, 8, 12, 20, 6, 31, 6, '+', 2, 0)} json_dict = serializer.Serializer.ConvertDateTimeValuesToJSON( rfc2579_date_time_object) self.assertEqual(json_dict, expected_json_dict) systemtime_object = systemtime.Systemtime( system_time_tuple=(2010, 8, 4, 12, 20, 6, 31, 142)) expected_json_dict = { '__class_name__': 'Systemtime', '__type__': 'DateTimeValues', 'system_time_tuple': (2010, 8, 4, 12, 20, 6, 31, 142)} json_dict = serializer.Serializer.ConvertDateTimeValuesToJSON( systemtime_object) self.assertEqual(json_dict, expected_json_dict) time_elements_object = time_elements.TimeElements( is_delta=True, time_elements_tuple=(2010, 8, 12, 20, 6, 31)) expected_json_dict = { '__class_name__': 'TimeElements', '__type__': 'DateTimeValues', 'is_delta': True, 'time_elements_tuple': (2010, 8, 12, 20, 6, 31)} json_dict = serializer.Serializer.ConvertDateTimeValuesToJSON( time_elements_object) self.assertEqual(json_dict, expected_json_dict) time_elements_object = time_elements.TimeElementsInMilliseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 546)) expected_json_dict = { '__class_name__': 'TimeElementsInMilliseconds', '__type__': 'DateTimeValues', 'time_elements_tuple': (2010, 8, 12, 20, 6, 31, 546)} json_dict = serializer.Serializer.ConvertDateTimeValuesToJSON( time_elements_object) self.assertEqual(json_dict, expected_json_dict) time_elements_object = time_elements.TimeElementsInMicroseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876)) expected_json_dict = { '__class_name__': 'TimeElementsInMicroseconds', '__type__': 'DateTimeValues', 'time_elements_tuple': (2010, 8, 12, 20, 6, 31, 429876)} json_dict = serializer.Serializer.ConvertDateTimeValuesToJSON( time_elements_object) self.assertEqual(json_dict, expected_json_dict) def testConvertJSONToDateTimeValues(self): """Test ConvertJSONToDateTimeValues function.""" json_dict = { '__class_name__': 'PosixTime', '__type__': 'DateTimeValues', 'timestamp': 1281643591} expected_date_time_object = posix_time.PosixTime(timestamp=1281643591) date_time_object = serializer.Serializer.ConvertJSONToDateTimeValues( json_dict) self.assertEqual(date_time_object, expected_date_time_object) json_dict = { '__class_name__': 'PosixTime', '__type__': 'DateTimeValues', 'is_local_time': True, 'time_zone_hint': 'Europe/Amsterdam', 'timestamp': 1281643591} expected_date_time_object.is_local_time = True expected_date_time_object.time_zone_hint = 'Europe/Amsterdam' date_time_object = serializer.Serializer.ConvertJSONToDateTimeValues( json_dict) self.assertEqual(date_time_object, expected_date_time_object) json_dict = { '__class_name__': 'PosixTime', '__type__': 'DateTimeValues', 'timestamp': 1281643591, 'time_zone_offset': 60} expected_date_time_object = posix_time.PosixTime( timestamp=1281643591, time_zone_offset=60) date_time_object = serializer.Serializer.ConvertJSONToDateTimeValues( json_dict) self.assertEqual(date_time_object, expected_date_time_object) json_dict = { '__class_name__': 'Never', '__type__': 'DateTimeValues', 'string': 'Never'} expected_date_time_object = semantic_time.Never() date_time_object = serializer.Serializer.ConvertJSONToDateTimeValues( json_dict) self.assertEqual(date_time_object, expected_date_time_object) json_dict = { '__class_name__': 'DotNetDateTime', '__type__': 'DateTimeValues', 'timestamp': 637433719321230000} expected_date_time_object = dotnet_datetime.DotNetDateTime( timestamp=637433719321230000) date_time_object = serializer.Serializer.ConvertJSONToDateTimeValues( json_dict) self.assertEqual(date_time_object, expected_date_time_object) json_dict = { '__class_name__': 'FATDateTime', '__type__': 'DateTimeValues', 'fat_date_time': 2832219404} expected_date_time_object = fat_date_time.FATDateTime( fat_date_time=0xa8d03d0c) date_time_object = serializer.Serializer.ConvertJSONToDateTimeValues( json_dict) self.assertEqual(date_time_object, expected_date_time_object) json_dict = { '__class_name__': 'GolangTime', '__type__': 'DateTimeValues', 'golang_timestamp': ( b'\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\xff\xff')} golang_timestamp = bytes.fromhex('01000000000000000200000003ffff') expected_date_time_object = golang_time.GolangTime( golang_timestamp=golang_timestamp) date_time_object = serializer.Serializer.ConvertJSONToDateTimeValues( json_dict) self.assertEqual(date_time_object, expected_date_time_object) json_dict = { '__class_name__': 'RFC2579DateTime', '__type__': 'DateTimeValues', 'rfc2579_date_time_tuple': (2010, 8, 12, 20, 6, 31, 6, '+', 2, 0)} expected_date_time_object = rfc2579_date_time.RFC2579DateTime( rfc2579_date_time_tuple=(2010, 8, 12, 20, 6, 31, 6, '+', 2, 0)) date_time_object = serializer.Serializer.ConvertJSONToDateTimeValues( json_dict) self.assertEqual(date_time_object, expected_date_time_object) json_dict = { '__class_name__': 'Systemtime', '__type__': 'DateTimeValues', 'system_time_tuple': (2010, 8, 4, 12, 20, 6, 31, 142)} expected_date_time_object = systemtime.Systemtime( system_time_tuple=(2010, 8, 4, 12, 20, 6, 31, 142)) date_time_object = serializer.Serializer.ConvertJSONToDateTimeValues( json_dict) self.assertEqual(date_time_object, expected_date_time_object) json_dict = { '__class_name__': 'TimeElements', '__type__': 'DateTimeValues', 'is_delta': True, 'time_elements_tuple': (2010, 8, 12, 20, 6, 31)} expected_date_time_object = time_elements.TimeElements( is_delta=True, time_elements_tuple=(2010, 8, 12, 20, 6, 31)) date_time_object = serializer.Serializer.ConvertJSONToDateTimeValues( json_dict) self.assertEqual(date_time_object, expected_date_time_object) self.assertTrue(date_time_object.is_delta) json_dict = { '__class_name__': 'TimeElementsInMilliseconds', '__type__': 'DateTimeValues', 'time_elements_tuple': (2010, 8, 12, 20, 6, 31, 546)} expected_date_time_object = time_elements.TimeElementsInMilliseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 546)) date_time_object = serializer.Serializer.ConvertJSONToDateTimeValues( json_dict) self.assertEqual(date_time_object, expected_date_time_object) json_dict = { '__class_name__': 'TimeElementsInMicroseconds', '__type__': 'DateTimeValues', 'time_elements_tuple': (2010, 8, 12, 20, 6, 31, 429876)} expected_date_time_object = time_elements.TimeElementsInMicroseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876)) date_time_object = serializer.Serializer.ConvertJSONToDateTimeValues( json_dict) self.assertEqual(date_time_object, expected_date_time_object) if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/systemtime.py000066400000000000000000000223141461534226200200100ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the SYSTEMTIME structure implementation.""" import decimal import unittest from dfdatetime import systemtime class SystemtimeTest(unittest.TestCase): """Tests for the SYSTEMTIME structure.""" # pylint: disable=protected-access def testInitialize(self): """Tests the initialization function.""" systemtime_object = systemtime.Systemtime() self.assertIsNotNone(systemtime_object) systemtime_object = systemtime.Systemtime( system_time_tuple=(2010, 8, 4, 12, 20, 6, 31, 142)) self.assertIsNotNone(systemtime_object) self.assertEqual(systemtime_object.year, 2010) self.assertEqual(systemtime_object.month, 8) self.assertEqual(systemtime_object.day_of_month, 12) self.assertEqual(systemtime_object.hours, 20) self.assertEqual(systemtime_object.minutes, 6) self.assertEqual(systemtime_object.seconds, 31) self.assertEqual(systemtime_object.milliseconds, 142) with self.assertRaises(ValueError): systemtime.Systemtime( system_time_tuple=(2010, 8, 4, 12, 20, 6, 31)) with self.assertRaises(ValueError): systemtime.Systemtime( system_time_tuple=(1500, 8, 4, 12, 20, 6, 31, 142)) with self.assertRaises(ValueError): systemtime.Systemtime( system_time_tuple=(2010, 13, 4, 12, 20, 6, 31, 142)) with self.assertRaises(ValueError): systemtime.Systemtime( system_time_tuple=(2010, 8, 7, 12, 20, 6, 31, 142)) with self.assertRaises(ValueError): systemtime.Systemtime( system_time_tuple=(2010, 8, 4, 32, 20, 6, 31, 142)) with self.assertRaises(ValueError): systemtime.Systemtime( system_time_tuple=(2010, 8, 4, 12, 24, 6, 31, 142)) with self.assertRaises(ValueError): systemtime.Systemtime( system_time_tuple=(2010, 8, 4, 12, 20, 61, 31, 142)) with self.assertRaises(ValueError): systemtime.Systemtime( system_time_tuple=(2010, 8, 4, 12, 20, 6, 61, 142)) with self.assertRaises(ValueError): systemtime.Systemtime( system_time_tuple=(2010, 8, 4, 12, 20, 6, 31, 1001)) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" systemtime_object = systemtime.Systemtime( system_time_tuple=(2010, 8, 4, 12, 20, 6, 31, 142)) normalized_timestamp = systemtime_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643591.142')) systemtime_object = systemtime.Systemtime( system_time_tuple=(2010, 8, 4, 12, 20, 6, 31, 142), time_zone_offset=60) normalized_timestamp = systemtime_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991.142')) systemtime_object = systemtime.Systemtime( system_time_tuple=(2010, 8, 4, 12, 20, 6, 31, 142)) systemtime_object.time_zone_offset = 60 normalized_timestamp = systemtime_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991.142')) systemtime_object = systemtime.Systemtime() normalized_timestamp = systemtime_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" systemtime_object = systemtime.Systemtime() systemtime_object.CopyFromDateTimeString('2010-08-12') self.assertEqual(systemtime_object._number_of_seconds, 1281571200) self.assertEqual(systemtime_object._time_zone_offset, None) self.assertEqual(systemtime_object.year, 2010) self.assertEqual(systemtime_object.month, 8) self.assertEqual(systemtime_object.day_of_month, 12) self.assertEqual(systemtime_object.hours, 0) self.assertEqual(systemtime_object.minutes, 0) self.assertEqual(systemtime_object.seconds, 0) self.assertEqual(systemtime_object.milliseconds, 0) systemtime_object.CopyFromDateTimeString('2010-08-12 21:06:31') self.assertEqual(systemtime_object._number_of_seconds, 1281647191) self.assertEqual(systemtime_object._time_zone_offset, None) self.assertEqual(systemtime_object.year, 2010) self.assertEqual(systemtime_object.month, 8) self.assertEqual(systemtime_object.day_of_month, 12) self.assertEqual(systemtime_object.hours, 21) self.assertEqual(systemtime_object.minutes, 6) self.assertEqual(systemtime_object.seconds, 31) self.assertEqual(systemtime_object.milliseconds, 0) systemtime_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') self.assertEqual(systemtime_object._number_of_seconds, 1281647191) self.assertEqual(systemtime_object._time_zone_offset, None) self.assertEqual(systemtime_object.year, 2010) self.assertEqual(systemtime_object.month, 8) self.assertEqual(systemtime_object.day_of_month, 12) self.assertEqual(systemtime_object.hours, 21) self.assertEqual(systemtime_object.minutes, 6) self.assertEqual(systemtime_object.seconds, 31) self.assertEqual(systemtime_object.milliseconds, 546) systemtime_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875-01:00') self.assertEqual(systemtime_object._number_of_seconds, 1281647191) self.assertEqual(systemtime_object._time_zone_offset, -60) self.assertEqual(systemtime_object.year, 2010) self.assertEqual(systemtime_object.month, 8) self.assertEqual(systemtime_object.day_of_month, 12) self.assertEqual(systemtime_object.hours, 21) self.assertEqual(systemtime_object.minutes, 6) self.assertEqual(systemtime_object.seconds, 31) self.assertEqual(systemtime_object.milliseconds, 546) self.assertEqual(systemtime_object.time_zone_offset, -60) systemtime_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875+01:00') self.assertEqual(systemtime_object._number_of_seconds, 1281647191) self.assertEqual(systemtime_object._time_zone_offset, 60) self.assertEqual(systemtime_object.year, 2010) self.assertEqual(systemtime_object.month, 8) self.assertEqual(systemtime_object.day_of_month, 12) self.assertEqual(systemtime_object.hours, 21) self.assertEqual(systemtime_object.minutes, 6) self.assertEqual(systemtime_object.seconds, 31) self.assertEqual(systemtime_object.milliseconds, 546) self.assertEqual(systemtime_object.time_zone_offset, 60) systemtime_object.CopyFromDateTimeString('1601-01-02 00:00:00') self.assertEqual(systemtime_object._number_of_seconds, -11644387200) self.assertEqual(systemtime_object._time_zone_offset, None) self.assertEqual(systemtime_object.year, 1601) self.assertEqual(systemtime_object.month, 1) self.assertEqual(systemtime_object.day_of_month, 2) self.assertEqual(systemtime_object.hours, 0) self.assertEqual(systemtime_object.minutes, 0) self.assertEqual(systemtime_object.seconds, 0) self.assertEqual(systemtime_object.milliseconds, 0) with self.assertRaises(ValueError): systemtime_object.CopyFromDateTimeString('1600-01-02 00:00:00') def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" systemtime_object = systemtime.Systemtime( system_time_tuple=(2010, 8, 4, 12, 20, 6, 31, 142)) date_time_string = systemtime_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2010-08-12 20:06:31.142') systemtime_object = systemtime.Systemtime() date_time_string = systemtime_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" systemtime_object = systemtime.Systemtime( system_time_tuple=(2010, 8, 4, 12, 20, 6, 31, 142)) date_time_string = systemtime_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2010-08-12T20:06:31.142+00:00') def testGetDate(self): """Tests the GetDate function.""" systemtime_object = systemtime.Systemtime( system_time_tuple=(2010, 8, 4, 12, 20, 6, 31, 142)) date_tuple = systemtime_object.GetDate() self.assertEqual(date_tuple, (2010, 8, 12)) systemtime_object = systemtime.Systemtime() date_tuple = systemtime_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" systemtime_object = systemtime.Systemtime( system_time_tuple=(2010, 8, 4, 12, 20, 6, 31, 142)) date_with_time_of_day_tuple = systemtime_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2010, 8, 12, 20, 6, 31)) systemtime_object = systemtime.Systemtime() date_with_time_of_day_tuple = systemtime_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" systemtime_object = systemtime.Systemtime( system_time_tuple=(2010, 8, 4, 12, 20, 6, 31, 142)) time_of_day_tuple = systemtime_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (20, 6, 31)) systemtime_object = systemtime.Systemtime() time_of_day_tuple = systemtime_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/time_elements.py000066400000000000000000002777711461534226200204620ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the time elements implementation.""" import datetime import decimal import unittest from dfdatetime import definitions from dfdatetime import time_elements class TimeElementsTest(unittest.TestCase): """Tests for the time elements.""" # pylint: disable=protected-access def testInitialize(self): """Tests the initialization function.""" time_elements_object = time_elements.TimeElements() self.assertIsNotNone(time_elements_object) expected_time_elements_tuple = (2010, 8, 12, 20, 6, 31) time_elements_object = time_elements.TimeElements( time_elements_tuple=(2010, 8, 12, 20, 6, 31)) self.assertIsNotNone(time_elements_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object.year, 2010) self.assertEqual(time_elements_object.month, 8) self.assertEqual(time_elements_object.day_of_month, 12) self.assertEqual(time_elements_object.hours, 20) self.assertEqual(time_elements_object.minutes, 6) self.assertEqual(time_elements_object.seconds, 31) expected_time_elements_tuple = (2010, 2, 29, 20, 6, 31) time_elements_object = time_elements.TimeElements( is_delta=True, time_elements_tuple=(2010, 2, 29, 20, 6, 31)) self.assertIsNotNone(time_elements_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object.year, 2010) self.assertEqual(time_elements_object.month, 2) self.assertEqual(time_elements_object.day_of_month, 29) self.assertEqual(time_elements_object.hours, 20) self.assertEqual(time_elements_object.minutes, 6) self.assertEqual(time_elements_object.seconds, 31) self.assertTrue(time_elements_object.is_delta) with self.assertRaises(ValueError): time_elements.TimeElements( time_elements_tuple=(2010, 8, 12, 20, 6)) with self.assertRaises(ValueError): time_elements.TimeElements( time_elements_tuple=(2010, 2, 29, 20, 6)) with self.assertRaises(ValueError): time_elements.TimeElements( time_elements_tuple=(2010, 13, 12, 20, 6, 31)) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" time_elements_object = time_elements.TimeElements( time_elements_tuple=(2010, 8, 12, 20, 6, 31)) normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643591')) time_elements_object = time_elements.TimeElements( time_elements_tuple=(2010, 8, 12, 20, 6, 31), time_zone_offset=60) normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991')) time_elements_object = time_elements.TimeElements( time_elements_tuple=(2010, 8, 12, 20, 6, 31)) time_elements_object.time_zone_offset = 60 normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991')) time_elements_object = time_elements.TimeElements() normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) def testCopyDateTimeFromStringISO8601(self): """Tests the _CopyDateTimeFromStringISO8601 function.""" time_elements_object = time_elements.TimeElements() expected_date_dict = { 'year': 2010, 'month': 8, 'day_of_month': 12} date_dict = time_elements_object._CopyDateTimeFromStringISO8601( '2010-08-12') self.assertEqual(date_dict, expected_date_dict) expected_date_dict = { 'year': 2010, 'month': 8, 'day_of_month': 12, 'hours': 21, 'minutes': 6, 'seconds': 31} date_dict = time_elements_object._CopyDateTimeFromStringISO8601( '2010-08-12T21:06:31') self.assertEqual(date_dict, expected_date_dict) expected_date_dict = { 'year': 2010, 'month': 8, 'day_of_month': 12, 'hours': 21, 'minutes': 6, 'seconds': 31, 'nanoseconds': 546875000} date_dict = time_elements_object._CopyDateTimeFromStringISO8601( '2010-08-12T21:06:31.546875') self.assertEqual(date_dict, expected_date_dict) expected_date_dict = { 'year': 2010, 'month': 8, 'day_of_month': 12, 'hours': 21, 'minutes': 6, 'seconds': 31, 'nanoseconds': 546875000, 'time_zone_offset': -60} date_dict = time_elements_object._CopyDateTimeFromStringISO8601( '2010-08-12T21:06:31.546875-01:00') self.assertEqual(date_dict, expected_date_dict) expected_date_dict = { 'year': 2010, 'month': 8, 'day_of_month': 12, 'hours': 21, 'minutes': 6, 'seconds': 31, 'nanoseconds': 546875000, 'time_zone_offset': 60} date_dict = time_elements_object._CopyDateTimeFromStringISO8601( '2010-08-12T21:06:31.546875+01:00') self.assertEqual(date_dict, expected_date_dict) expected_date_dict = { 'year': 2010, 'month': 8, 'day_of_month': 12, 'hours': 21, 'minutes': 6, 'seconds': 31, 'nanoseconds': 546875333, 'time_zone_offset': 60} date_dict = time_elements_object._CopyDateTimeFromStringISO8601( '2010-08-12T21:06:31.546875333+01:00') self.assertEqual(date_dict, expected_date_dict) with self.assertRaises(ValueError): time_elements_object._CopyDateTimeFromStringISO8601('') with self.assertRaises(ValueError): time_elements_object._CopyDateTimeFromStringISO8601('2010-08-12T1') with self.assertRaises(ValueError): time_elements_object._CopyDateTimeFromStringISO8601( '2010-08-12 21:06:31.546875+01:00') def testCopyDateTimeFromStringRFC822(self): """Tests the _CopyDateTimeFromStringRFC822 function.""" time_elements_object = time_elements.TimeElements() expected_date_dict = { 'year': 1982, 'month': 6, 'day_of_month': 20, 'hours': 11, 'minutes': 57, 'seconds': 9, 'time_zone_offset': 0} date_dict = time_elements_object._CopyDateTimeFromStringRFC822( 'Sun, 20 Jun 82 11:57:09 GMT') self.assertEqual(date_dict, expected_date_dict) expected_date_dict = { 'year': 1982, 'month': 6, 'day_of_month': 20, 'hours': 11, 'minutes': 57, 'seconds': 9, 'time_zone_offset': 0} date_dict = time_elements_object._CopyDateTimeFromStringRFC822( '20 Jun 82 11:57:09 GMT') self.assertEqual(date_dict, expected_date_dict) expected_date_dict = { 'year': 1982, 'month': 6, 'day_of_month': 20, 'hours': 11, 'minutes': 57, 'time_zone_offset': 0} date_dict = time_elements_object._CopyDateTimeFromStringRFC822( '20 Jun 82 11:57 GMT') self.assertEqual(date_dict, expected_date_dict) expected_date_dict = { 'year': 1982, 'month': 6, 'day_of_month': 20, 'hours': 11, 'minutes': 57, 'seconds': 9, 'time_zone_offset': -300} date_dict = time_elements_object._CopyDateTimeFromStringRFC822( 'Sun, 20 Jun 82 11:57:09 EST') self.assertEqual(date_dict, expected_date_dict) expected_date_dict = { 'year': 1982, 'month': 6, 'day_of_month': 20, 'hours': 11, 'minutes': 57, 'seconds': 9, 'time_zone_offset': -300} date_dict = time_elements_object._CopyDateTimeFromStringRFC822( 'Sun, 20 Jun 82 11:57:09 -0500') self.assertEqual(date_dict, expected_date_dict) with self.assertRaises(ValueError): time_elements_object._CopyDateTimeFromStringRFC822( 'XXX, 20 Jun 82 11:57:09 GMT') with self.assertRaises(ValueError): time_elements_object._CopyDateTimeFromStringRFC822( 'Sun, XX Jun 82 11:57:09 GMT') with self.assertRaises(ValueError): time_elements_object._CopyDateTimeFromStringRFC822( 'Sun, 20 XXX 82 11:57:09 GMT') with self.assertRaises(ValueError): time_elements_object._CopyDateTimeFromStringRFC822( 'Sun, 20 Jun XX 11:57:09 GMT') with self.assertRaises(ValueError): time_elements_object._CopyDateTimeFromStringRFC822( 'Sun, 20 Jun 82 XX:XX:XX XXX') def testCopyDateTimeFromStringRFC1123(self): """Tests the _CopyDateTimeFromStringRFC1123 function.""" time_elements_object = time_elements.TimeElements() expected_date_dict = { 'year': 1982, 'month': 6, 'day_of_month': 20, 'hours': 11, 'minutes': 57, 'seconds': 9, 'time_zone_offset': 0} date_dict = time_elements_object._CopyDateTimeFromStringRFC1123( 'Sun, 20 Jun 1982 11:57:09 GMT') self.assertEqual(date_dict, expected_date_dict) expected_date_dict = { 'year': 1982, 'month': 6, 'day_of_month': 20, 'hours': 11, 'minutes': 57, 'seconds': 9, 'time_zone_offset': 0} date_dict = time_elements_object._CopyDateTimeFromStringRFC1123( '20 Jun 1982 11:57:09 GMT') self.assertEqual(date_dict, expected_date_dict) expected_date_dict = { 'year': 1982, 'month': 6, 'day_of_month': 20, 'hours': 11, 'minutes': 57, 'time_zone_offset': 0} date_dict = time_elements_object._CopyDateTimeFromStringRFC1123( '20 Jun 1982 11:57 GMT') self.assertEqual(date_dict, expected_date_dict) expected_date_dict = { 'year': 1982, 'month': 6, 'day_of_month': 20, 'hours': 11, 'minutes': 57, 'seconds': 9, 'time_zone_offset': -300} date_dict = time_elements_object._CopyDateTimeFromStringRFC1123( 'Sun, 20 Jun 1982 11:57:09 EST') self.assertEqual(date_dict, expected_date_dict) expected_date_dict = { 'year': 1982, 'month': 6, 'day_of_month': 20, 'hours': 11, 'minutes': 57, 'seconds': 9, 'time_zone_offset': -300} date_dict = time_elements_object._CopyDateTimeFromStringRFC1123( 'Sun, 20 Jun 1982 11:57:09 -0500') self.assertEqual(date_dict, expected_date_dict) with self.assertRaises(ValueError): time_elements_object._CopyDateTimeFromStringRFC1123( 'XXX, 20 Jun 1982 11:57:09 GMT') with self.assertRaises(ValueError): time_elements_object._CopyDateTimeFromStringRFC1123( 'Sun, XX Jun 1982 11:57:09 GMT') with self.assertRaises(ValueError): time_elements_object._CopyDateTimeFromStringRFC1123( 'Sun, 20 XXX 1982 11:57:09 GMT') with self.assertRaises(ValueError): time_elements_object._CopyDateTimeFromStringRFC1123( 'Sun, 20 Jun XXXX 11:57:09 GMT') with self.assertRaises(ValueError): time_elements_object._CopyDateTimeFromStringRFC1123( 'Sun, 20 Jun 1982 XX:XX:XX XXX') # TODO: add tests for _CopyFromDateTimeValues def testCopyTimeFromStringISO8601(self): """Tests the _CopyTimeFromStringISO8601 function.""" time_elements_object = time_elements.TimeElements() expected_time_tuple = (8, None, None, None, None) time_tuple = time_elements_object._CopyTimeFromStringISO8601('08') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (8, 4, None, None, None) time_tuple = time_elements_object._CopyTimeFromStringISO8601('08:04') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (8, 4, None, None, None) time_tuple = time_elements_object._CopyTimeFromStringISO8601('0804') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 30, 0, 0, None) time_tuple = time_elements_object._CopyTimeFromStringISO8601('20.5') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (8, 4, 32, None, None) time_tuple = time_elements_object._CopyTimeFromStringISO8601('08:04:32') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (8, 4, 32, None, None) time_tuple = time_elements_object._CopyTimeFromStringISO8601('080432') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 23, 30, 0, None) time_tuple = time_elements_object._CopyTimeFromStringISO8601('20:23.5') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 23, 30, 0, None) time_tuple = time_elements_object._CopyTimeFromStringISO8601('2023.5') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 23, 56, None, None) time_tuple = time_elements_object._CopyTimeFromStringISO8601('20:23:56') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 23, 56, None, 330) time_tuple = time_elements_object._CopyTimeFromStringISO8601( '20:23:56+05:30') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 23, 56, 327000000, None) time_tuple = time_elements_object._CopyTimeFromStringISO8601('20:23:56.327') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 23, 56, 327000000, 60) time_tuple = time_elements_object._CopyTimeFromStringISO8601( '20:23:56.327+01:00') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 23, 56, 327124000, None) time_tuple = time_elements_object._CopyTimeFromStringISO8601( '20:23:56.327124') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (8, 4, 32, None, 0) time_tuple = time_elements_object._CopyTimeFromStringISO8601('08:04:32Z') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (8, 4, 32, None, 0) time_tuple = time_elements_object._CopyTimeFromStringISO8601( '08:04:32+00:00') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 23, 56, 327124000, -300) time_tuple = time_elements_object._CopyTimeFromStringISO8601( '20:23:56.327124-05:00') self.assertEqual(time_tuple, expected_time_tuple) with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringISO8601('') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringISO8601('1') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringISO8601('14:1') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringISO8601('14:15:1') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringISO8601('24:00:00') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringISO8601('12b00:00') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringISO8601('12:00b00') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringISO8601('1s:00:00') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringISO8601('00:60:00') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringISO8601('00:e0:00') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringISO8601('00:00:60') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringISO8601('00:00:w0') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringISO8601('12:00:00.00w') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringISO8601('12:00:00+01b00') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringISO8601('12:00:00+0w:00') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringISO8601('12:00:00+20:00') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringISO8601('12:00:00+01:0w') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringISO8601('12:00:00+01:60') def testCopyTimeFromStringRFC(self): """Tests the _CopyTimeFromStringRFC function.""" time_elements_object = time_elements.TimeElements() expected_time_tuple = (11, 57, 9, 0) time_tuple = time_elements_object._CopyTimeFromStringRFC('11:57:09', 'GMT') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (11, 57, None, 0) time_tuple = time_elements_object._CopyTimeFromStringRFC('11:57', 'GMT') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (11, 57, None, -300) time_tuple = time_elements_object._CopyTimeFromStringRFC('11:57', 'EST') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (11, 57, None, -300) time_tuple = time_elements_object._CopyTimeFromStringRFC('11:57', '-0500') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (11, 57, None, 60) time_tuple = time_elements_object._CopyTimeFromStringRFC('11:57', '+0100') self.assertEqual(time_tuple, expected_time_tuple) with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringRFC('11:', 'GMT') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringRFC('11:57:', 'GMT') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringRFC('XX:57:09', 'GMT') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringRFC('24:57:09', 'GMT') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringRFC('11:XX:09', 'GMT') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringRFC('11:60:09', 'GMT') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringRFC('11:57:XX', 'GMT') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringRFC('11:57:60', 'GMT') with self.assertRaises(ValueError): time_elements_object._CopyTimeFromStringRFC('11:57:09', 'XXX') def testCopyFromDatetime(self): """Tests the CopyFromDatetime function.""" time_elements_object = time_elements.TimeElements() expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) expected_number_of_seconds = 1281647191 datetime_object = datetime.datetime(2010, 8, 12, 21, 6, 31, 546875) time_elements_object.CopyFromDatetime(datetime_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual( time_elements_object._number_of_seconds, expected_number_of_seconds) self.assertTrue(time_elements_object.is_local_time) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) expected_number_of_seconds = 1281647191 datetime_object = datetime.datetime( 2010, 8, 12, 21, 6, 31, 546875, tzinfo=datetime.timezone.utc) time_elements_object.CopyFromDatetime(datetime_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual( time_elements_object._number_of_seconds, expected_number_of_seconds) self.assertFalse(time_elements_object.is_local_time) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" time_elements_object = time_elements.TimeElements() expected_time_elements_tuple = (2010, 8, 12, 0, 0, 0) time_elements_object.CopyFromDateTimeString('2010-08-12') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281571200) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromDateTimeString('2010-08-12 21:06:31') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromDateTimeString( '2010-08-12 21:06:31.546875-01:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object._time_zone_offset, -60) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromDateTimeString( '2010-08-12 21:06:31.546875+01:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object._time_zone_offset, 60) expected_time_elements_tuple = (1601, 1, 2, 0, 0, 0) time_elements_object.CopyFromDateTimeString('1601-01-02 00:00:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, -11644387200) self.assertEqual(time_elements_object._time_zone_offset, None) def testCopyFromStringISO8601(self): """Tests the CopyFromStringISO8601 function.""" time_elements_object = time_elements.TimeElements() expected_time_elements_tuple = (2010, 8, 12, 0, 0, 0) time_elements_object.CopyFromStringISO8601('2010-08-12') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281571200) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31+00:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object._time_zone_offset, 0) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31.5') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31.546875') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31,546875') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601( '2010-08-12T21:06:31.546875-01:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object._time_zone_offset, -60) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601( '2010-08-12T21:06:31.546875+01:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object._time_zone_offset, 60) expected_time_elements_tuple = (2012, 3, 5, 20, 40, 0) time_elements_object.CopyFromStringISO8601( '2012-03-05T20:40:00.0000000+00:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1330980000) self.assertEqual(time_elements_object._time_zone_offset, 0) with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601(None) with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601( '2010-08-12 21:06:31.546875+01:00') # Valid ISO 8601 notations currently not supported. with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601('2016-W33') with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601('2016-W33-3') with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601('--08-17') with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601('2016-230') def testCopyFromStringTuple(self): """Tests the CopyFromStringTuple function.""" time_elements_object = time_elements.TimeElements() expected_time_elements_tuple = (2010, 8, 12, 20, 6, 31) time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '12', '20', '6', '31')) self.assertIsNotNone(time_elements_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) with self.assertRaises(ValueError): time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '12', '20', '6')) with self.assertRaises(ValueError): time_elements_object.CopyFromStringTuple( time_elements_tuple=('20A0', 'B', '12', '20', '6', '31')) with self.assertRaises(ValueError): time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', 'B', '12', '20', '6', '31')) with self.assertRaises(ValueError): time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '1C', '20', '6', '31')) with self.assertRaises(ValueError): time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '12', 'D0', '6', '31')) with self.assertRaises(ValueError): time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '12', '20', 'E', '31')) with self.assertRaises(ValueError): time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '12', '20', '6', 'F1')) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" time_elements_object = time_elements.TimeElements( time_elements_tuple=(2010, 8, 12, 20, 6, 31)) date_time_string = time_elements_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2010-08-12 20:06:31') time_elements_object = time_elements.TimeElements( time_elements_tuple=(0, 8, 12, 20, 6, 31)) date_time_string = time_elements_object.CopyToDateTimeString() self.assertEqual(date_time_string, '0000-08-12 20:06:31') time_elements_object = time_elements.TimeElements() date_time_string = time_elements_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" time_elements_object = time_elements.TimeElements( time_elements_tuple=(2010, 8, 12, 20, 6, 31)) date_time_string = time_elements_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2010-08-12T20:06:31+00:00') time_elements_object.is_local_time = True date_time_string = time_elements_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2010-08-12T20:06:31') time_elements_object = time_elements.TimeElements( time_elements_tuple=(2010, 8, 12, 20, 6, 31), time_zone_offset=120) date_time_string = time_elements_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2010-08-12T20:06:31+02:00') time_elements_object = time_elements.TimeElements( time_elements_tuple=(2010, 8, 12, 20, 6, 31), time_zone_offset=-300) date_time_string = time_elements_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2010-08-12T20:06:31-05:00') def testCopyToPosixTimestamp(self): """Tests the CopyToPosixTimestamp function.""" time_elements_object = time_elements.TimeElements( time_elements_tuple=(2010, 8, 12, 20, 6, 31)) posix_timestamp = time_elements_object.CopyToPosixTimestamp() self.assertEqual(posix_timestamp, 1281643591) time_elements_object = time_elements.TimeElements() posix_timestamp = time_elements_object.CopyToPosixTimestamp() self.assertIsNone(posix_timestamp) def testCopyToPosixTimestampWithFractionOfSecond(self): """Tests the CopyToPosixTimestampWithFractionOfSecond function.""" time_elements_object = time_elements.TimeElements( time_elements_tuple=(2010, 8, 12, 20, 6, 31)) posix_timestamp, fraction_of_second = ( time_elements_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertEqual(posix_timestamp, 1281643591) self.assertIsNone(fraction_of_second) time_elements_object = time_elements.TimeElements() posix_timestamp, fraction_of_second = ( time_elements_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertIsNone(posix_timestamp) self.assertIsNone(fraction_of_second) def testGetDate(self): """Tests the GetDate function.""" time_elements_object = time_elements.TimeElements( time_elements_tuple=(2010, 8, 12, 20, 6, 31)) date_tuple = time_elements_object.GetDate() self.assertEqual(date_tuple, (2010, 8, 12)) time_elements_object = time_elements.TimeElements() date_tuple = time_elements_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" time_elements_object = time_elements.TimeElements( time_elements_tuple=(2010, 8, 12, 20, 6, 31)) date_with_time_of_day_tuple = time_elements_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2010, 8, 12, 20, 6, 31)) time_elements_object = time_elements.TimeElements() date_with_time_of_day_tuple = time_elements_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" time_elements_object = time_elements.TimeElements( time_elements_tuple=(2010, 8, 12, 20, 6, 31)) time_of_day_tuple = time_elements_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (20, 6, 31)) time_elements_object = time_elements.TimeElements() time_of_day_tuple = time_elements_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) def testNewFromDeltaAndDate(self): """Tests the NewFromDeltaAndDate function.""" time_elements_object = time_elements.TimeElements( is_delta=True, time_elements_tuple=(1, 0, 0, 20, 6, 31)) new_time_elements_object = time_elements_object.NewFromDeltaAndDate( 2009, 1, 12) self.assertIsNotNone(new_time_elements_object) self.assertFalse(new_time_elements_object.is_delta) self.assertEqual(new_time_elements_object.year, 2010) self.assertEqual(new_time_elements_object.month, 1) self.assertEqual(new_time_elements_object.day_of_month, 12) time_elements_object = time_elements.TimeElements(is_delta=True) new_time_elements_object = time_elements_object.NewFromDeltaAndDate( 2009, 1, 12) self.assertIsNone(new_time_elements_object) time_elements_object = time_elements.TimeElements(is_delta=False) with self.assertRaises(ValueError): time_elements_object.NewFromDeltaAndDate(2009, 1, 12) def testNewFromDeltaAndYear(self): """Tests the NewFromDeltaAndYear function.""" time_elements_object = time_elements.TimeElements( is_delta=True, time_elements_tuple=(1, 8, 12, 20, 6, 31)) new_time_elements_object = time_elements_object.NewFromDeltaAndYear(2009) self.assertIsNotNone(new_time_elements_object) self.assertFalse(new_time_elements_object.is_delta) self.assertEqual(new_time_elements_object.year, 2010) time_elements_object = time_elements.TimeElements(is_delta=True) new_time_elements_object = time_elements_object.NewFromDeltaAndYear(2009) self.assertIsNone(new_time_elements_object) time_elements_object = time_elements.TimeElements(is_delta=False) with self.assertRaises(ValueError): time_elements_object.NewFromDeltaAndYear(2009) class TimeElementsWithFractionOfSeconds(unittest.TestCase): """Tests for the time elements with fractions of seconds.""" # pylint: disable=protected-access def testInitialize(self): """Tests the initialization function.""" time_elements_object = time_elements.TimeElementsWithFractionOfSecond() self.assertIsNotNone(time_elements_object) expected_time_elements_tuple = (2010, 8, 12, 20, 6, 31) time_elements_object = time_elements.TimeElementsWithFractionOfSecond( fraction_of_second=decimal.Decimal(0.87), precision=definitions.PRECISION_10_MILLISECONDS, time_elements_tuple=(2010, 8, 12, 20, 6, 31)) self.assertIsNotNone(time_elements_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object.fraction_of_second, 0.87) expected_time_elements_tuple = (2010, 8, 12, 20, 6, 31) time_elements_object = time_elements.TimeElementsWithFractionOfSecond( fraction_of_second=decimal.Decimal(0.8742), precision=definitions.PRECISION_100_MICROSECONDS, time_elements_tuple=(2010, 8, 12, 20, 6, 31)) self.assertIsNotNone(time_elements_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object.fraction_of_second, 0.8742) with self.assertRaises(ValueError): time_elements.TimeElementsWithFractionOfSecond( fraction_of_second=decimal.Decimal('1.87'), precision=definitions.PRECISION_10_MILLISECONDS, time_elements_tuple=(2010, 8, 12, 20, 6, 31)) with self.assertRaises(ValueError): time_elements.TimeElementsWithFractionOfSecond( fraction_of_second=decimal.Decimal('-1'), precision=definitions.PRECISION_10_MILLISECONDS, time_elements_tuple=(2010, 8, 12, 20, 6, 31)) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" time_elements_object = time_elements.TimeElementsWithFractionOfSecond( fraction_of_second=decimal.Decimal('0.87'), precision=definitions.PRECISION_10_MILLISECONDS, time_elements_tuple=(2010, 8, 12, 20, 6, 31)) normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643591.87')) time_elements_object = time_elements.TimeElementsWithFractionOfSecond( fraction_of_second=decimal.Decimal('0.87'), precision=definitions.PRECISION_10_MILLISECONDS, time_elements_tuple=(2010, 8, 12, 20, 6, 31), time_zone_offset=60) normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991.87')) time_elements_object = time_elements.TimeElementsWithFractionOfSecond( fraction_of_second=decimal.Decimal('0.87'), precision=definitions.PRECISION_10_MILLISECONDS, time_elements_tuple=(2010, 8, 12, 20, 6, 31)) time_elements_object.time_zone_offset = 60 normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991.87')) time_elements_object = time_elements.TimeElementsWithFractionOfSecond( fraction_of_second=decimal.Decimal('0.8724'), precision=definitions.PRECISION_100_MICROSECONDS, time_elements_tuple=(2010, 8, 12, 20, 6, 31)) normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643591.8724')) time_elements_object = time_elements.TimeElementsWithFractionOfSecond() normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) def testCopyFromDateTimeValues(self): """Tests the _CopyFromDateTimeValues function.""" date_time_values = { 'year': 2010, 'month': 8, 'day_of_month': 12, 'hours': 21, 'minutes': 6, 'seconds': 31, 'nanoseconds': 123456789, 'time_zone_offset': 60} time_elements_object = time_elements.TimeElementsWithFractionOfSecond( precision=definitions.PRECISION_10_MILLISECONDS) time_elements_object._CopyFromDateTimeValues(date_time_values) self.assertEqual( time_elements_object._time_elements_tuple, (2010, 8, 12, 21, 6, 31)) self.assertEqual( time_elements_object.fraction_of_second, decimal.Decimal('0.12')) time_elements_object = time_elements.TimeElementsWithFractionOfSecond( precision=definitions.PRECISION_1_MILLISECOND) time_elements_object._CopyFromDateTimeValues(date_time_values) self.assertEqual( time_elements_object.fraction_of_second, decimal.Decimal('0.123')) time_elements_object = time_elements.TimeElementsWithFractionOfSecond( precision=definitions.PRECISION_100_MICROSECONDS) time_elements_object._CopyFromDateTimeValues(date_time_values) self.assertEqual( time_elements_object.fraction_of_second, decimal.Decimal('0.1234')) def testCopyFromDatetime(self): """Tests the CopyFromDatetime function.""" datetime_object = datetime.datetime(2010, 8, 12, 21, 6, 31, 546875) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) expected_number_of_seconds = 1281647191 expected_fraction_of_second = decimal.Decimal('0.54') time_elements_object = time_elements.TimeElementsWithFractionOfSecond( precision=definitions.PRECISION_10_MILLISECONDS) time_elements_object.CopyFromDatetime(datetime_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual( time_elements_object._number_of_seconds, expected_number_of_seconds) self.assertEqual( time_elements_object.fraction_of_second, expected_fraction_of_second) self.assertTrue(time_elements_object.is_local_time) datetime_object = datetime.datetime( 2010, 8, 12, 21, 6, 31, 546875, tzinfo=datetime.timezone.utc) time_elements_object.CopyFromDatetime(datetime_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual( time_elements_object._number_of_seconds, expected_number_of_seconds) self.assertEqual( time_elements_object.fraction_of_second, expected_fraction_of_second) self.assertFalse(time_elements_object.is_local_time) def testCopyFromStringTuple(self): """Tests the CopyFromStringTuple function.""" time_elements_object = time_elements.TimeElementsWithFractionOfSecond( precision=definitions.PRECISION_10_MILLISECONDS) expected_time_elements_tuple = (2010, 8, 12, 20, 6, 31) expected_fraction_of_second = decimal.Decimal('0.46') time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '12', '20', '6', '31', '0.46')) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual( time_elements_object.fraction_of_second, expected_fraction_of_second) time_elements_object = time_elements.TimeElementsWithFractionOfSecond( precision=definitions.PRECISION_100_MICROSECONDS) time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '12', '20', '6', '31', '0.4671')) self.assertEqual( time_elements_object.fraction_of_second, decimal.Decimal('0.4671')) with self.assertRaises(ValueError): time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '12', '20', '6', '31')) with self.assertRaises(ValueError): time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '12', '20', '6', '31', '96')) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" time_elements_object = time_elements.TimeElementsWithFractionOfSecond( fraction_of_second=decimal.Decimal('0.87'), precision=definitions.PRECISION_10_MILLISECONDS, time_elements_tuple=(2010, 8, 12, 20, 6, 31)) date_time_string = time_elements_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2010-08-12 20:06:31.87') time_elements_object = time_elements.TimeElementsWithFractionOfSecond( fraction_of_second=decimal.Decimal('0.874'), precision=definitions.PRECISION_1_MILLISECOND, time_elements_tuple=(2010, 8, 12, 20, 6, 31)) date_time_string = time_elements_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2010-08-12 20:06:31.874') time_elements_object = time_elements.TimeElementsWithFractionOfSecond( fraction_of_second=decimal.Decimal('0.8741'), precision=definitions.PRECISION_100_MICROSECONDS, time_elements_tuple=(2010, 8, 12, 20, 6, 31)) date_time_string = time_elements_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2010-08-12 20:06:31.8741') def testNewFromDeltaAndDate(self): """Tests the NewFromDeltaAndDate function.""" time_elements_object = time_elements.TimeElementsWithFractionOfSecond( fraction_of_second=decimal.Decimal('0.87'), is_delta=True, precision=definitions.PRECISION_10_MILLISECONDS, time_elements_tuple=(1, 0, 0, 20, 6, 31)) new_time_elements_object = time_elements_object.NewFromDeltaAndDate( 2009, 1, 12) self.assertFalse(new_time_elements_object.is_delta) self.assertEqual(new_time_elements_object.year, 2010) self.assertEqual(new_time_elements_object.month, 1) self.assertEqual(new_time_elements_object.day_of_month, 12) self.assertEqual( new_time_elements_object.fraction_of_second, decimal.Decimal('0.87')) time_elements_object = time_elements.TimeElementsWithFractionOfSecond( time_elements_tuple=(1, 0, 0, 20, 6, 31)) with self.assertRaises(ValueError): time_elements_object.NewFromDeltaAndDate(2009, 1, 12) time_elements_object = time_elements.TimeElementsWithFractionOfSecond() with self.assertRaises(ValueError): time_elements_object.NewFromDeltaAndDate(2009, 1, 12) def testNewFromDeltaAndYear(self): """Tests the NewFromDeltaAndYear function.""" time_elements_object = time_elements.TimeElementsWithFractionOfSecond( fraction_of_second=decimal.Decimal('0.87'), is_delta=True, precision=definitions.PRECISION_10_MILLISECONDS, time_elements_tuple=(1, 8, 12, 20, 6, 31)) new_time_elements_object = time_elements_object.NewFromDeltaAndYear(2009) self.assertFalse(new_time_elements_object.is_delta) self.assertEqual(new_time_elements_object.year, 2010) class TimeElementsInMillisecondsTest(unittest.TestCase): """Tests for the time elements in milliseconds.""" # pylint: disable=protected-access def testInitialize(self): """Tests the initialization function.""" time_elements_object = time_elements.TimeElements() self.assertIsNotNone(time_elements_object) expected_time_elements_tuple = (2010, 8, 12, 20, 6, 31) time_elements_object = time_elements.TimeElementsInMilliseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 546)) self.assertIsNotNone(time_elements_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object.milliseconds, 546) with self.assertRaises(ValueError): time_elements.TimeElementsInMilliseconds( time_elements_tuple=(2010, 13, 12, 20, 6, 31)) with self.assertRaises(ValueError): time_elements.TimeElementsInMilliseconds( time_elements_tuple=(2010, 13, 12, 20, 6, 31, 1001)) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" time_elements_object = time_elements.TimeElementsInMilliseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429)) normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643591.429')) time_elements_object = time_elements.TimeElementsInMilliseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429), time_zone_offset=60) normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991.429')) time_elements_object = time_elements.TimeElementsInMilliseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429)) time_elements_object.time_zone_offset = 60 normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991.429')) time_elements_object = time_elements.TimeElementsInMilliseconds() normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) # TODO: add tests for _CopyFromDateTimeValues def testCopyFromDatetime(self): """Tests the CopyFromDatetime function.""" time_elements_object = time_elements.TimeElementsInMilliseconds() expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) expected_number_of_seconds = 1281647191 datetime_object = datetime.datetime(2010, 8, 12, 21, 6, 31, 546875) time_elements_object.CopyFromDatetime(datetime_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual( time_elements_object._number_of_seconds, expected_number_of_seconds) self.assertEqual(time_elements_object.milliseconds, 546) self.assertTrue(time_elements_object.is_local_time) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) expected_number_of_seconds = 1281647191 datetime_object = datetime.datetime( 2010, 8, 12, 21, 6, 31, 546875, tzinfo=datetime.timezone.utc) time_elements_object.CopyFromDatetime(datetime_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual( time_elements_object._number_of_seconds, expected_number_of_seconds) self.assertEqual(time_elements_object.milliseconds, 546) self.assertFalse(time_elements_object.is_local_time) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" time_elements_object = time_elements.TimeElementsInMilliseconds() expected_time_elements_tuple = (2010, 8, 12, 0, 0, 0) time_elements_object.CopyFromDateTimeString('2010-08-12') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281571200) self.assertEqual(time_elements_object.milliseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromDateTimeString('2010-08-12 21:06:31') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.milliseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.milliseconds, 546) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromDateTimeString( '2010-08-12 21:06:31.546875-01:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.milliseconds, 546) self.assertEqual(time_elements_object._time_zone_offset, -60) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromDateTimeString( '2010-08-12 21:06:31.546875+01:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.milliseconds, 546) self.assertEqual(time_elements_object._time_zone_offset, 60) expected_time_elements_tuple = (1601, 1, 2, 0, 0, 0) time_elements_object.CopyFromDateTimeString('1601-01-02 00:00:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, -11644387200) self.assertEqual(time_elements_object.milliseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, None) def testCopyFromStringISO8601(self): """Tests the CopyFromStringISO8601 function.""" time_elements_object = time_elements.TimeElementsInMilliseconds() expected_time_elements_tuple = (2010, 8, 12, 0, 0, 0) time_elements_object.CopyFromStringISO8601('2010-08-12') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281571200) self.assertEqual(time_elements_object.milliseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.milliseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31+00:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.milliseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, 0) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31.5') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.milliseconds, 500) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31.546875') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.milliseconds, 546) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31,546875') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.milliseconds, 546) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601( '2010-08-12T21:06:31.546875-01:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.milliseconds, 546) self.assertEqual(time_elements_object._time_zone_offset, -60) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601( '2010-08-12T21:06:31.546875+01:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.milliseconds, 546) self.assertEqual(time_elements_object._time_zone_offset, 60) expected_time_elements_tuple = (2012, 3, 5, 20, 40, 0) time_elements_object.CopyFromStringISO8601( '2012-03-05T20:40:00.0000000+00:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1330980000) self.assertEqual(time_elements_object.milliseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, 0) with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601(None) with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601( '2010-08-12 21:06:31.546875+01:00') # Valid ISO 8601 notations currently not supported. with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601('2016-W33') with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601('2016-W33-3') with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601('--08-17') with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601('2016-230') def testCopyFromStringTuple(self): """Tests the CopyFromStringTuple function.""" time_elements_object = time_elements.TimeElementsInMilliseconds() expected_time_elements_tuple = (2010, 8, 12, 20, 6, 31) time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '12', '20', '6', '31', '546')) self.assertIsNotNone(time_elements_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object.milliseconds, 546) with self.assertRaises(ValueError): time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '12', '20', '6', '31')) with self.assertRaises(ValueError): time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '12', '20', '6', '31', '9S')) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" time_elements_object = time_elements.TimeElementsInMilliseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429)) date_time_string = time_elements_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2010-08-12 20:06:31.429') time_elements_object = time_elements.TimeElementsInMilliseconds() date_time_string = time_elements_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" time_elements_object = time_elements.TimeElementsInMilliseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429)) date_time_string = time_elements_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2010-08-12T20:06:31.429+00:00') def testCopyToPosixTimestamp(self): """Tests the CopyToPosixTimestamp function.""" time_elements_object = time_elements.TimeElementsInMilliseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429)) posix_timestamp = time_elements_object.CopyToPosixTimestamp() self.assertEqual(posix_timestamp, 1281643591) time_elements_object = time_elements.TimeElements() posix_timestamp = time_elements_object.CopyToPosixTimestamp() self.assertIsNone(posix_timestamp) def testCopyToPosixTimestampWithFractionOfSecond(self): """Tests the CopyToPosixTimestampWithFractionOfSecond function.""" time_elements_object = time_elements.TimeElementsInMilliseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429)) posix_timestamp, fraction_of_second = ( time_elements_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertEqual(posix_timestamp, 1281643591) self.assertEqual(fraction_of_second, 429) time_elements_object = time_elements.TimeElements() posix_timestamp, fraction_of_second = ( time_elements_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertIsNone(posix_timestamp) self.assertIsNone(fraction_of_second) def testGetDate(self): """Tests the GetDate function.""" time_elements_object = time_elements.TimeElementsInMilliseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429)) date_tuple = time_elements_object.GetDate() self.assertEqual(date_tuple, (2010, 8, 12)) time_elements_object = time_elements.TimeElementsInMilliseconds() date_tuple = time_elements_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" time_elements_object = time_elements.TimeElementsInMilliseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429)) date_with_time_of_day_tuple = time_elements_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2010, 8, 12, 20, 6, 31)) time_elements_object = time_elements.TimeElementsInMilliseconds() date_with_time_of_day_tuple = time_elements_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" time_elements_object = time_elements.TimeElementsInMilliseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429)) time_of_day_tuple = time_elements_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (20, 6, 31)) time_elements_object = time_elements.TimeElementsInMilliseconds() time_of_day_tuple = time_elements_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) def testNewFromDeltaAndDate(self): """Tests the NewFromDeltaAndDate function.""" time_elements_object = time_elements.TimeElementsInMilliseconds( is_delta=True, time_elements_tuple=(1, 0, 0, 20, 6, 31, 429)) new_time_elements_object = time_elements_object.NewFromDeltaAndDate( 2009, 1, 12) self.assertIsNotNone(new_time_elements_object) self.assertFalse(new_time_elements_object.is_delta) self.assertEqual(new_time_elements_object.year, 2010) self.assertEqual(new_time_elements_object.month, 1) self.assertEqual(new_time_elements_object.day_of_month, 12) self.assertEqual(new_time_elements_object.milliseconds, 429) time_elements_object = time_elements.TimeElements(is_delta=True) new_time_elements_object = time_elements_object.NewFromDeltaAndDate( 2009, 1, 12) self.assertIsNone(new_time_elements_object) time_elements_object = time_elements.TimeElements(is_delta=False) with self.assertRaises(ValueError): time_elements_object.NewFromDeltaAndDate(2009, 1, 12) def testNewFromDeltaAndYear(self): """Tests the NewFromDeltaAndYear function.""" time_elements_object = time_elements.TimeElementsInMilliseconds( is_delta=True, time_elements_tuple=(1, 8, 12, 20, 6, 31, 429)) new_time_elements_object = time_elements_object.NewFromDeltaAndYear(2009) self.assertIsNotNone(new_time_elements_object) self.assertFalse(new_time_elements_object.is_delta) self.assertEqual(new_time_elements_object.year, 2010) self.assertEqual(new_time_elements_object.milliseconds, 429) time_elements_object = time_elements.TimeElements(is_delta=True) new_time_elements_object = time_elements_object.NewFromDeltaAndYear(2009) self.assertIsNone(new_time_elements_object) time_elements_object = time_elements.TimeElements(is_delta=False) with self.assertRaises(ValueError): time_elements_object.NewFromDeltaAndYear(2009) class TimeElementsInMicrosecondsTest(unittest.TestCase): """Tests for the time elements in microseconds.""" # pylint: disable=protected-access def testInitialize(self): """Tests the initialization function.""" time_elements_object = time_elements.TimeElements() self.assertIsNotNone(time_elements_object) expected_time_elements_tuple = (2010, 8, 12, 20, 6, 31) time_elements_object = time_elements.TimeElementsInMicroseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 546875)) self.assertIsNotNone(time_elements_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object.microseconds, 546875) with self.assertRaises(ValueError): time_elements.TimeElementsInMicroseconds( time_elements_tuple=(2010, 13, 12, 20, 6, 31)) with self.assertRaises(ValueError): time_elements.TimeElementsInMicroseconds( time_elements_tuple=(2010, 13, 12, 20, 6, 31, 1001)) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" time_elements_object = time_elements.TimeElementsInMicroseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876)) normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643591.429876')) time_elements_object = time_elements.TimeElementsInMicroseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876), time_zone_offset=60) normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991.429876')) time_elements_object = time_elements.TimeElementsInMicroseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876)) time_elements_object.time_zone_offset = 60 normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281639991.429876')) time_elements_object = time_elements.TimeElementsInMicroseconds() normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) # TODO: add tests for _CopyFromDateTimeValues def testCopyFromDatetime(self): """Tests the CopyFromDatetime function.""" time_elements_object = time_elements.TimeElementsInMicroseconds() expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) expected_number_of_seconds = 1281647191 datetime_object = datetime.datetime(2010, 8, 12, 21, 6, 31, 546875) time_elements_object.CopyFromDatetime(datetime_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual( time_elements_object._number_of_seconds, expected_number_of_seconds) self.assertEqual(time_elements_object.microseconds, 546875) self.assertTrue(time_elements_object.is_local_time) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) expected_number_of_seconds = 1281647191 datetime_object = datetime.datetime( 2010, 8, 12, 21, 6, 31, 546875, tzinfo=datetime.timezone.utc) time_elements_object.CopyFromDatetime(datetime_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual( time_elements_object._number_of_seconds, expected_number_of_seconds) self.assertEqual(time_elements_object.microseconds, 546875) self.assertFalse(time_elements_object.is_local_time) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" time_elements_object = time_elements.TimeElementsInMicroseconds() expected_time_elements_tuple = (2010, 8, 12, 0, 0, 0) time_elements_object.CopyFromDateTimeString('2010-08-12') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281571200) self.assertEqual(time_elements_object.microseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromDateTimeString('2010-08-12 21:06:31') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.microseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.microseconds, 546875) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromDateTimeString( '2010-08-12 21:06:31.546875-01:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.microseconds, 546875) self.assertEqual(time_elements_object._time_zone_offset, -60) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromDateTimeString( '2010-08-12 21:06:31.546875+01:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.microseconds, 546875) self.assertEqual(time_elements_object._time_zone_offset, 60) expected_time_elements_tuple = (1601, 1, 2, 0, 0, 0) time_elements_object.CopyFromDateTimeString('1601-01-02 00:00:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, -11644387200) self.assertEqual(time_elements_object.microseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, None) def testCopyFromStringISO8601(self): """Tests the CopyFromStringISO8601 function.""" time_elements_object = time_elements.TimeElementsInMicroseconds() expected_time_elements_tuple = (2010, 8, 12, 0, 0, 0) time_elements_object.CopyFromStringISO8601('2010-08-12') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281571200) self.assertEqual(time_elements_object.microseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.microseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31+00:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.microseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, 0) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31.5') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.microseconds, 500000) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31.546875') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.microseconds, 546875) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31,546875') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.microseconds, 546875) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601( '2010-08-12T21:06:31.546875-01:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.microseconds, 546875) self.assertEqual(time_elements_object._time_zone_offset, -60) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601( '2010-08-12T21:06:31.546875+01:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.microseconds, 546875) self.assertEqual(time_elements_object._time_zone_offset, 60) expected_time_elements_tuple = (2012, 3, 5, 20, 40, 0) time_elements_object.CopyFromStringISO8601( '2012-03-05T20:40:00.0000000+00:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1330980000) self.assertEqual(time_elements_object.microseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, 0) with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601(None) with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601( '2010-08-12 21:06:31.546875+01:00') # Valid ISO 8601 notations currently not supported. with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601('2016-W33') with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601('2016-W33-3') with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601('--08-17') with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601('2016-230') def testCopyFromStringTuple(self): """Tests the CopyFromStringTuple function.""" time_elements_object = time_elements.TimeElementsInMicroseconds() expected_time_elements_tuple = (2010, 8, 12, 20, 6, 31) time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '12', '20', '6', '31', '546')) self.assertIsNotNone(time_elements_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object.microseconds, 546) with self.assertRaises(ValueError): time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '12', '20', '6', '31')) with self.assertRaises(ValueError): time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '12', '20', '6', '31', '9S')) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" time_elements_object = time_elements.TimeElementsInMicroseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876)) date_time_string = time_elements_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2010-08-12 20:06:31.429876') time_elements_object = time_elements.TimeElementsInMicroseconds() date_time_string = time_elements_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" time_elements_object = time_elements.TimeElementsInMicroseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876)) date_time_string = time_elements_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2010-08-12T20:06:31.429876+00:00') def testCopyToPosixTimestamp(self): """Tests the CopyToPosixTimestamp function.""" time_elements_object = time_elements.TimeElementsInMicroseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876)) posix_timestamp = time_elements_object.CopyToPosixTimestamp() self.assertEqual(posix_timestamp, 1281643591) time_elements_object = time_elements.TimeElements() posix_timestamp = time_elements_object.CopyToPosixTimestamp() self.assertIsNone(posix_timestamp) def testCopyToPosixTimestampWithFractionOfSecond(self): """Tests the CopyToPosixTimestampWithFractionOfSecond function.""" time_elements_object = time_elements.TimeElementsInMicroseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876)) posix_timestamp, fraction_of_second = ( time_elements_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertEqual(posix_timestamp, 1281643591) self.assertEqual(fraction_of_second, 429876) time_elements_object = time_elements.TimeElements() posix_timestamp, fraction_of_second = ( time_elements_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertIsNone(posix_timestamp) self.assertIsNone(fraction_of_second) def testGetDate(self): """Tests the GetDate function.""" time_elements_object = time_elements.TimeElementsInMicroseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876)) date_tuple = time_elements_object.GetDate() self.assertEqual(date_tuple, (2010, 8, 12)) time_elements_object = time_elements.TimeElementsInMicroseconds() date_tuple = time_elements_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" time_elements_object = time_elements.TimeElementsInMicroseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876)) date_with_time_of_day_tuple = time_elements_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2010, 8, 12, 20, 6, 31)) time_elements_object = time_elements.TimeElementsInMicroseconds() date_with_time_of_day_tuple = time_elements_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" time_elements_object = time_elements.TimeElementsInMicroseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876)) time_of_day_tuple = time_elements_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (20, 6, 31)) time_elements_object = time_elements.TimeElementsInMicroseconds() time_of_day_tuple = time_elements_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) def testNewFromDeltaAndDate(self): """Tests the NewFromDeltaAndDate function.""" time_elements_object = time_elements.TimeElementsInMicroseconds( is_delta=True, time_elements_tuple=(1, 0, 0, 20, 6, 31, 429876)) new_time_elements_object = time_elements_object.NewFromDeltaAndDate( 2009, 1, 12) self.assertIsNotNone(new_time_elements_object) self.assertFalse(new_time_elements_object.is_delta) self.assertEqual(new_time_elements_object.year, 2010) self.assertEqual(new_time_elements_object.month, 1) self.assertEqual(new_time_elements_object.day_of_month, 12) self.assertEqual(new_time_elements_object.microseconds, 429876) time_elements_object = time_elements.TimeElements(is_delta=True) new_time_elements_object = time_elements_object.NewFromDeltaAndDate( 2009, 1, 12) self.assertIsNone(new_time_elements_object) time_elements_object = time_elements.TimeElements(is_delta=False) with self.assertRaises(ValueError): time_elements_object.NewFromDeltaAndDate(2009, 1, 12) def testNewFromDeltaAndYear(self): """Tests the NewFromDeltaAndYear function.""" time_elements_object = time_elements.TimeElementsInMicroseconds( is_delta=True, time_elements_tuple=(1, 8, 12, 20, 6, 31, 429876)) new_time_elements_object = time_elements_object.NewFromDeltaAndYear(2009) self.assertIsNotNone(new_time_elements_object) self.assertFalse(new_time_elements_object.is_delta) self.assertEqual(new_time_elements_object.year, 2010) self.assertEqual(new_time_elements_object.microseconds, 429876) time_elements_object = time_elements.TimeElements(is_delta=True) new_time_elements_object = time_elements_object.NewFromDeltaAndYear(2009) self.assertIsNone(new_time_elements_object) time_elements_object = time_elements.TimeElements(is_delta=False) with self.assertRaises(ValueError): time_elements_object.NewFromDeltaAndYear(2009) class TimeElementsInNanosecondsTest(unittest.TestCase): """Tests for the time elements in nanoseconds.""" # pylint: disable=protected-access def testInitialize(self): """Tests the initialization function.""" time_elements_object = time_elements.TimeElements() self.assertIsNotNone(time_elements_object) expected_time_elements_tuple = (2010, 8, 12, 20, 6, 31) time_elements_object = time_elements.TimeElementsInNanoseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 546875218)) self.assertIsNotNone(time_elements_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object.nanoseconds, 546875218) with self.assertRaises(ValueError): time_elements.TimeElementsInNanoseconds( time_elements_tuple=(2010, 13, 12, 20, 6, 31)) with self.assertRaises(ValueError): time_elements.TimeElementsInNanoseconds( time_elements_tuple=(2010, 13, 12, 20, 6, 31, 1001)) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" time_elements_object = time_elements.TimeElementsInNanoseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876301)) normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertEqual( normalized_timestamp, decimal.Decimal('1281643591.429876301')) time_elements_object = time_elements.TimeElementsInNanoseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876301), time_zone_offset=60) normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertEqual( normalized_timestamp, decimal.Decimal('1281639991.429876301')) time_elements_object = time_elements.TimeElementsInNanoseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876301)) time_elements_object.time_zone_offset = 60 normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertEqual( normalized_timestamp, decimal.Decimal('1281639991.429876301')) time_elements_object = time_elements.TimeElementsInNanoseconds() normalized_timestamp = time_elements_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) # TODO: add tests for _CopyFromDateTimeValues def testCopyFromDatetime(self): """Tests the CopyFromDatetime function.""" time_elements_object = time_elements.TimeElementsInNanoseconds() expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) expected_number_of_seconds = 1281647191 # Note that datetime has microsecond precision. datetime_object = datetime.datetime(2010, 8, 12, 21, 6, 31, 546875) time_elements_object.CopyFromDatetime(datetime_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual( time_elements_object._number_of_seconds, expected_number_of_seconds) self.assertEqual(time_elements_object.nanoseconds, 546875000) self.assertTrue(time_elements_object.is_local_time) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) expected_number_of_seconds = 1281647191 # Note that datetime has microsecond precision. datetime_object = datetime.datetime( 2010, 8, 12, 21, 6, 31, 546875, tzinfo=datetime.timezone.utc) time_elements_object.CopyFromDatetime(datetime_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual( time_elements_object._number_of_seconds, expected_number_of_seconds) self.assertEqual(time_elements_object.nanoseconds, 546875000) self.assertFalse(time_elements_object.is_local_time) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" time_elements_object = time_elements.TimeElementsInNanoseconds() expected_time_elements_tuple = (2010, 8, 12, 0, 0, 0) time_elements_object.CopyFromDateTimeString('2010-08-12') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281571200) self.assertEqual(time_elements_object.nanoseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromDateTimeString('2010-08-12 21:06:31') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.nanoseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875218') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.nanoseconds, 546875218) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromDateTimeString( '2010-08-12 21:06:31.546875218-01:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.nanoseconds, 546875218) self.assertEqual(time_elements_object._time_zone_offset, -60) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromDateTimeString( '2010-08-12 21:06:31.546875218+01:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.nanoseconds, 546875218) self.assertEqual(time_elements_object._time_zone_offset, 60) expected_time_elements_tuple = (1601, 1, 2, 0, 0, 0) time_elements_object.CopyFromDateTimeString('1601-01-02 00:00:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, -11644387200) self.assertEqual(time_elements_object.nanoseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, None) def testCopyFromStringISO8601(self): """Tests the CopyFromStringISO8601 function.""" time_elements_object = time_elements.TimeElementsInNanoseconds() expected_time_elements_tuple = (2010, 8, 12, 0, 0, 0) time_elements_object.CopyFromStringISO8601('2010-08-12') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281571200) self.assertEqual(time_elements_object.nanoseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.nanoseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31+00:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.nanoseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, 0) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31.5') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.nanoseconds, 500000000) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31.546875218') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.nanoseconds, 546875218) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31,546875218') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.nanoseconds, 546875218) self.assertEqual(time_elements_object._time_zone_offset, None) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601( '2010-08-12T21:06:31.546875218-01:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.nanoseconds, 546875218) self.assertEqual(time_elements_object._time_zone_offset, -60) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601( '2010-08-12T21:06:31.546875218+01:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1281647191) self.assertEqual(time_elements_object.nanoseconds, 546875218) self.assertEqual(time_elements_object._time_zone_offset, 60) expected_time_elements_tuple = (2012, 3, 5, 20, 40, 0) time_elements_object.CopyFromStringISO8601( '2012-03-05T20:40:00.0000000+00:00') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object._number_of_seconds, 1330980000) self.assertEqual(time_elements_object.nanoseconds, 0) self.assertEqual(time_elements_object._time_zone_offset, 0) with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601(None) with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601( '2010-08-12 21:06:31.546875218+01:00') # Valid ISO 8601 notations currently not supported. with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601('2016-W33') with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601('2016-W33-3') with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601('--08-17') with self.assertRaises(ValueError): time_elements_object.CopyFromStringISO8601('2016-230') def testCopyFromStringTuple(self): """Tests the CopyFromStringTuple function.""" time_elements_object = time_elements.TimeElementsInNanoseconds() expected_time_elements_tuple = (2010, 8, 12, 20, 6, 31) time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '12', '20', '6', '31', '546')) self.assertIsNotNone(time_elements_object) self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual(time_elements_object.nanoseconds, 546) with self.assertRaises(ValueError): time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '12', '20', '6', '31')) with self.assertRaises(ValueError): time_elements_object.CopyFromStringTuple( time_elements_tuple=('2010', '8', '12', '20', '6', '31', '9S')) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" time_elements_object = time_elements.TimeElementsInNanoseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876301)) date_time_string = time_elements_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2010-08-12 20:06:31.429876301') time_elements_object = time_elements.TimeElementsInNanoseconds() date_time_string = time_elements_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" time_elements_object = time_elements.TimeElementsInNanoseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876301)) date_time_string = time_elements_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2010-08-12T20:06:31.429876301+00:00') def testCopyToPosixTimestamp(self): """Tests the CopyToPosixTimestamp function.""" time_elements_object = time_elements.TimeElementsInNanoseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876301)) posix_timestamp = time_elements_object.CopyToPosixTimestamp() self.assertEqual(posix_timestamp, 1281643591) time_elements_object = time_elements.TimeElements() posix_timestamp = time_elements_object.CopyToPosixTimestamp() self.assertIsNone(posix_timestamp) def testCopyToPosixTimestampWithFractionOfSecond(self): """Tests the CopyToPosixTimestampWithFractionOfSecond function.""" time_elements_object = time_elements.TimeElementsInNanoseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876301)) posix_timestamp, fraction_of_second = ( time_elements_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertEqual(posix_timestamp, 1281643591) self.assertEqual(fraction_of_second, 429876301) time_elements_object = time_elements.TimeElements() posix_timestamp, fraction_of_second = ( time_elements_object.CopyToPosixTimestampWithFractionOfSecond()) self.assertIsNone(posix_timestamp) self.assertIsNone(fraction_of_second) def testGetDate(self): """Tests the GetDate function.""" time_elements_object = time_elements.TimeElementsInNanoseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876301)) date_tuple = time_elements_object.GetDate() self.assertEqual(date_tuple, (2010, 8, 12)) time_elements_object = time_elements.TimeElementsInNanoseconds() date_tuple = time_elements_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" time_elements_object = time_elements.TimeElementsInNanoseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876301)) date_with_time_of_day_tuple = time_elements_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2010, 8, 12, 20, 6, 31)) time_elements_object = time_elements.TimeElementsInNanoseconds() date_with_time_of_day_tuple = time_elements_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" time_elements_object = time_elements.TimeElementsInNanoseconds( time_elements_tuple=(2010, 8, 12, 20, 6, 31, 429876301)) time_of_day_tuple = time_elements_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (20, 6, 31)) time_elements_object = time_elements.TimeElementsInNanoseconds() time_of_day_tuple = time_elements_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) def testNewFromDeltaAndDate(self): """Tests the NewFromDeltaAndDate function.""" time_elements_object = time_elements.TimeElementsInNanoseconds( is_delta=True, time_elements_tuple=(1, 0, 0, 20, 6, 31, 429876301)) new_time_elements_object = time_elements_object.NewFromDeltaAndDate( 2009, 1, 12) self.assertIsNotNone(new_time_elements_object) self.assertFalse(new_time_elements_object.is_delta) self.assertEqual(new_time_elements_object.year, 2010) self.assertEqual(new_time_elements_object.month, 1) self.assertEqual(new_time_elements_object.day_of_month, 12) self.assertEqual(new_time_elements_object.nanoseconds, 429876301) time_elements_object = time_elements.TimeElements(is_delta=True) new_time_elements_object = time_elements_object.NewFromDeltaAndDate( 2009, 1, 12) self.assertIsNone(new_time_elements_object) time_elements_object = time_elements.TimeElements(is_delta=False) with self.assertRaises(ValueError): time_elements_object.NewFromDeltaAndDate(2009, 1, 12) def testNewFromDeltaAndYear(self): """Tests the NewFromDeltaAndYear function.""" time_elements_object = time_elements.TimeElementsInNanoseconds( is_delta=True, time_elements_tuple=(1, 8, 12, 20, 6, 31, 429876301)) new_time_elements_object = time_elements_object.NewFromDeltaAndYear(2009) self.assertIsNotNone(new_time_elements_object) self.assertFalse(new_time_elements_object.is_delta) self.assertEqual(new_time_elements_object.year, 2010) self.assertEqual(new_time_elements_object.nanoseconds, 429876301) time_elements_object = time_elements.TimeElements(is_delta=True) new_time_elements_object = time_elements_object.NewFromDeltaAndYear(2009) self.assertIsNone(new_time_elements_object) time_elements_object = time_elements.TimeElements(is_delta=False) with self.assertRaises(ValueError): time_elements_object.NewFromDeltaAndYear(2009) if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/uuid_time.py000066400000000000000000000157331461534226200176000ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the UUID version 1 time implementation.""" import decimal import uuid import unittest from dfdatetime import uuid_time class UUIDTimeEpochTEst(unittest.TestCase): """Tests for the UUID version 1 time time epoch.""" def testInitialize(self): """Tests the __init__ function.""" uuid_time_epoch = uuid_time.UUIDTimeEpoch() self.assertIsNotNone(uuid_time_epoch) class UUIDTimeTest(unittest.TestCase): """Tests for the UUID version 1 timestamp.""" # pylint: disable=protected-access def testInitialize(self): """Tests the initialization function.""" uuid_time_object = uuid_time.UUIDTime() self.assertIsNotNone(uuid_time_object) uuid_object = uuid.UUID('00911b54-9ef4-11e1-be53-525400123456') uuid_time_object = uuid_time.UUIDTime(timestamp=uuid_object.time) self.assertIsNotNone(uuid_time_object) expected_timestamp = 135564234616544084 self.assertEqual(uuid_time_object.timestamp, expected_timestamp) with self.assertRaises(ValueError): uuid_time.UUIDTime(timestamp=0x1fffffffffffffff) with self.assertRaises(ValueError): uuid_time.UUIDTime(timestamp=-1) def testProperties(self): """Tests the properties.""" uuid_object = uuid.UUID('00911b54-9ef4-11e1-be53-525400123456') uuid_time_object = uuid_time.UUIDTime(timestamp=uuid_object.time) self.assertEqual(uuid_time_object.timestamp, 0x1e19ef400911b54) uuid_time_object = uuid_time.UUIDTime() self.assertIsNone(uuid_time_object.timestamp) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" uuid_object = uuid.UUID('00911b54-9ef4-11e1-be53-525400123456') uuid_time_object = uuid_time.UUIDTime(timestamp=uuid_object.time) normalized_timestamp = uuid_time_object._GetNormalizedTimestamp() self.assertEqual( normalized_timestamp, decimal.Decimal('1337130661.6544084')) uuid_object = uuid.UUID('00911b54-9ef4-11e1-be53-525400123456') uuid_time_object = uuid_time.UUIDTime( time_zone_offset=60, timestamp=uuid_object.time) normalized_timestamp = uuid_time_object._GetNormalizedTimestamp() self.assertEqual( normalized_timestamp, decimal.Decimal('1337127061.6544084')) uuid_object = uuid.UUID('00911b54-9ef4-11e1-be53-525400123456') uuid_time_object = uuid_time.UUIDTime(timestamp=uuid_object.time) uuid_time_object.time_zone_offset = 60 normalized_timestamp = uuid_time_object._GetNormalizedTimestamp() self.assertEqual( normalized_timestamp, decimal.Decimal('1337127061.6544084')) uuid_time_object = uuid_time.UUIDTime() uuid_time_object._timestamp = 0x1fffffffffffffff normalized_timestamp = uuid_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) uuid_time_object = uuid_time.UUIDTime() uuid_time_object._timestamp = -1 normalized_timestamp = uuid_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) uuid_time_object = uuid_time.UUIDTime() normalized_timestamp = uuid_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" uuid_time_object = uuid_time.UUIDTime() uuid_time_object.CopyFromDateTimeString('2013-08-01') self.assertEqual(uuid_time_object._timestamp, 135946080000000000) self.assertEqual(uuid_time_object._time_zone_offset, None) uuid_time_object.CopyFromDateTimeString('2013-08-01 15:25:28') self.assertEqual(uuid_time_object._timestamp, 135946635280000000) self.assertEqual(uuid_time_object._time_zone_offset, None) uuid_time_object.CopyFromDateTimeString('2013-08-01 15:25:28.546875') self.assertEqual(uuid_time_object._timestamp, 135946635285468750) self.assertEqual(uuid_time_object._time_zone_offset, None) uuid_time_object.CopyFromDateTimeString('2013-08-01 15:25:28.546875-01:00') self.assertEqual(uuid_time_object._timestamp, 135946635285468750) self.assertEqual(uuid_time_object._time_zone_offset, -60) uuid_time_object.CopyFromDateTimeString('2013-08-01 15:25:28.546875+01:00') self.assertEqual(uuid_time_object._timestamp, 135946635285468750) self.assertEqual(uuid_time_object._time_zone_offset, 60) uuid_time_object.CopyFromDateTimeString('1582-10-16 00:00:00') self.assertEqual(uuid_time_object._timestamp, 864000000000) self.assertEqual(uuid_time_object._time_zone_offset, None) with self.assertRaises(ValueError): uuid_time_object.CopyFromDateTimeString('1570-01-02 00:00:00') def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" uuid_object = uuid.UUID('00911b54-9ef4-11e1-be53-525400123456') uuid_time_object = uuid_time.UUIDTime(timestamp=uuid_object.time) date_time_string = uuid_time_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2012-05-16 01:11:01.6544084') uuid_time_object = uuid_time.UUIDTime() date_time_string = uuid_time_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" uuid_object = uuid.UUID('00911b54-9ef4-11e1-be53-525400123456') uuid_time_object = uuid_time.UUIDTime(timestamp=uuid_object.time) date_time_string = uuid_time_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2012-05-16T01:11:01.6544084+00:00') def testGetDate(self): """Tests the GetDate function.""" uuid_object = uuid.UUID('00911b54-9ef4-11e1-be53-525400123456') uuid_time_object = uuid_time.UUIDTime(timestamp=uuid_object.time) date_tuple = uuid_time_object.GetDate() self.assertEqual(date_tuple, (2012, 5, 16)) uuid_time_object = uuid_time.UUIDTime() date_tuple = uuid_time_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" uuid_object = uuid.UUID('00911b54-9ef4-11e1-be53-525400123456') uuid_time_object = uuid_time.UUIDTime(timestamp=uuid_object.time) date_with_time_of_day_tuple = uuid_time_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2012, 5, 16, 1, 11, 1)) uuid_time_object = uuid_time.UUIDTime() date_with_time_of_day_tuple = uuid_time_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" uuid_object = uuid.UUID('00911b54-9ef4-11e1-be53-525400123456') uuid_time_object = uuid_time.UUIDTime(timestamp=uuid_object.time) time_of_day_tuple = uuid_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (1, 11, 1)) uuid_time_object = uuid_time.UUIDTime() time_of_day_tuple = uuid_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) if __name__ == '__main__': unittest.main() dfdatetime-20240504/tests/webkit_time.py000066400000000000000000000130661461534226200201140ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the WebKit time implementation.""" import decimal import unittest from dfdatetime import webkit_time class WebKitTimeEpochTest(unittest.TestCase): """Tests for the WebKit time epoch.""" def testInitialize(self): """Tests the __init__ function.""" webkit_epoch = webkit_time.WebKitTimeEpoch() self.assertIsNotNone(webkit_epoch) class WebKitTimeTest(unittest.TestCase): """Tests for the WebKit timestamp.""" # pylint: disable=protected-access def testProperties(self): """Tests the properties.""" webkit_time_object = webkit_time.WebKitTime(timestamp=12926120791546875) self.assertEqual(webkit_time_object.timestamp, 12926120791546875) webkit_time_object = webkit_time.WebKitTime() self.assertIsNone(webkit_time_object.timestamp) def testGetNormalizedTimestamp(self): """Tests the _GetNormalizedTimestamp function.""" webkit_time_object = webkit_time.WebKitTime(timestamp=12926120791546875) normalized_timestamp = webkit_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281647191.546875')) webkit_time_object = webkit_time.WebKitTime( time_zone_offset=60, timestamp=12926120791546875) normalized_timestamp = webkit_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643591.546875')) webkit_time_object = webkit_time.WebKitTime(timestamp=12926120791546875) webkit_time_object.time_zone_offset = 60 normalized_timestamp = webkit_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643591.546875')) webkit_time_object = webkit_time.WebKitTime(timestamp=0x1ffffffffffffffff) normalized_timestamp = webkit_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) webkit_time_object = webkit_time.WebKitTime() normalized_timestamp = webkit_time_object._GetNormalizedTimestamp() self.assertIsNone(normalized_timestamp) def testCopyFromDateTimeString(self): """Tests the CopyFromDateTimeString function.""" webkit_time_object = webkit_time.WebKitTime() webkit_time_object.CopyFromDateTimeString('2010-08-12') self.assertEqual(webkit_time_object._timestamp, 12926044800000000) self.assertEqual(webkit_time_object._time_zone_offset, None) webkit_time_object.CopyFromDateTimeString('2010-08-12 21:06:31') self.assertEqual(webkit_time_object._timestamp, 12926120791000000) self.assertEqual(webkit_time_object._time_zone_offset, None) webkit_time_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') self.assertEqual(webkit_time_object._timestamp, 12926120791546875) self.assertEqual(webkit_time_object._time_zone_offset, None) webkit_time_object.CopyFromDateTimeString( '2010-08-12 21:06:31.546875-01:00') self.assertEqual(webkit_time_object._timestamp, 12926120791546875) self.assertEqual(webkit_time_object._time_zone_offset, -60) webkit_time_object.CopyFromDateTimeString( '2010-08-12 21:06:31.546875+01:00') self.assertEqual(webkit_time_object._timestamp, 12926120791546875) self.assertEqual(webkit_time_object._time_zone_offset, 60) webkit_time_object.CopyFromDateTimeString('1601-01-02 00:00:00') self.assertEqual(webkit_time_object._timestamp, 86400 * 1000000) self.assertEqual(webkit_time_object._time_zone_offset, None) def testCopyToDateTimeString(self): """Tests the CopyToDateTimeString function.""" webkit_time_object = webkit_time.WebKitTime(timestamp=12926120791546875) date_time_string = webkit_time_object.CopyToDateTimeString() self.assertEqual(date_time_string, '2010-08-12 21:06:31.546875') webkit_time_object = webkit_time.WebKitTime() date_time_string = webkit_time_object.CopyToDateTimeString() self.assertIsNone(date_time_string) def testCopyToDateTimeStringISO8601(self): """Tests the CopyToDateTimeStringISO8601 function.""" webkit_time_object = webkit_time.WebKitTime(timestamp=12926120791546875) date_time_string = webkit_time_object.CopyToDateTimeStringISO8601() self.assertEqual(date_time_string, '2010-08-12T21:06:31.546875+00:00') def testGetDate(self): """Tests the GetDate function.""" webkit_time_object = webkit_time.WebKitTime(timestamp=12926120791546875) date_tuple = webkit_time_object.GetDate() self.assertEqual(date_tuple, (2010, 8, 12)) webkit_time_object = webkit_time.WebKitTime() date_tuple = webkit_time_object.GetDate() self.assertEqual(date_tuple, (None, None, None)) def testGetDateWithTimeOfDay(self): """Tests the GetDateWithTimeOfDay function.""" webkit_time_object = webkit_time.WebKitTime(timestamp=12926120791546875) date_with_time_of_day_tuple = webkit_time_object.GetDateWithTimeOfDay() self.assertEqual(date_with_time_of_day_tuple, (2010, 8, 12, 21, 6, 31)) webkit_time_object = webkit_time.WebKitTime() date_with_time_of_day_tuple = webkit_time_object.GetDateWithTimeOfDay() self.assertEqual( date_with_time_of_day_tuple, (None, None, None, None, None, None)) def testGetTimeOfDay(self): """Tests the GetTimeOfDay function.""" webkit_time_object = webkit_time.WebKitTime(timestamp=12926120791546875) time_of_day_tuple = webkit_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (21, 6, 31)) webkit_time_object = webkit_time.WebKitTime() time_of_day_tuple = webkit_time_object.GetTimeOfDay() self.assertEqual(time_of_day_tuple, (None, None, None)) if __name__ == '__main__': unittest.main() dfdatetime-20240504/tox.ini000066400000000000000000000024211461534226200154010ustar00rootroot00000000000000[tox] envlist = py3{8,9,10,11,12},coverage,docformatter,docs,lint,wheel [testenv] allowlist_externals = ./run_tests.py pip_pre = True passenv = CFLAGS CPPFLAGS LDFLAGS setenv = PYTHONPATH = {toxinidir} deps = -rrequirements.txt -rtest_requirements.txt coverage: coverage wheel: build setuptools >= 65 wheel commands = py3{8,9,10,11,12}: ./run_tests.py coverage: coverage erase coverage: coverage run --source=dfdatetime --omit="*_test*,*__init__*,*test_lib*" run_tests.py coverage: coverage xml wheel: python -m build --no-isolation --wheel [testenv:docformatter] usedevelop = True deps = docformatter commands = docformatter --in-place --recursive dfdatetime tests [testenv:docs] usedevelop = True deps = -rdocs/requirements.txt commands = sphinx-build -b html -d build/doctrees docs dist/docs sphinx-build -b linkcheck docs dist/docs [testenv:lint] skipsdist = True pip_pre = True passenv = CFLAGS CPPFLAGS LDFLAGS setenv = PYTHONPATH = {toxinidir} deps = -rrequirements.txt -rtest_requirements.txt docformatter pylint >= 3.0.0, < 3.1.0 setuptools commands = docformatter --version pylint --version docformatter --check --diff --recursive dfdatetime setup.py tests pylint --rcfile=.pylintrc dfdatetime setup.py tests dfdatetime-20240504/utils/000077500000000000000000000000001461534226200152275ustar00rootroot00000000000000dfdatetime-20240504/utils/__init__.py000066400000000000000000000000301461534226200173310ustar00rootroot00000000000000# -*- coding: utf-8 -*- dfdatetime-20240504/utils/check_dependencies.py000077500000000000000000000006461461534226200213750ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Script to check for the availability and version of dependencies.""" import sys # Change PYTHONPATH to include dependencies. sys.path.insert(0, '.') import utils.dependencies # pylint: disable=wrong-import-position if __name__ == '__main__': dependency_helper = utils.dependencies.DependencyHelper() if not dependency_helper.CheckDependencies(): sys.exit(1) dfdatetime-20240504/utils/dependencies.py000066400000000000000000000264771461534226200202470ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Helper to check for availability and version of dependencies.""" import configparser import os import re class DependencyDefinition(object): """Dependency definition. Attributes: dpkg_name (str): name of the dpkg package that provides the dependency. is_optional (bool): True if the dependency is optional. l2tbinaries_name (str): name of the l2tbinaries package that provides the dependency. maximum_version (str): maximum supported version, a greater or equal version is not supported. minimum_version (str): minimum supported version, a lesser version is not supported. name (str): name of (the Python module that provides) the dependency. pypi_name (str): name of the PyPI package that provides the dependency. python2_only (bool): True if the dependency is only supported by Python 2. python3_only (bool): True if the dependency is only supported by Python 3. rpm_name (str): name of the rpm package that provides the dependency. skip_check (bool): True if the dependency should be skipped by the CheckDependencies or CheckTestDependencies methods of DependencyHelper. skip_requires (bool): True if the dependency should be excluded from requirements.txt or setup.py install_requires. version_property (str): name of the version attribute or function. """ def __init__(self, name): """Initializes a dependency configuration. Args: name (str): name of the dependency. """ super(DependencyDefinition, self).__init__() self.dpkg_name = None self.is_optional = False self.l2tbinaries_name = None self.maximum_version = None self.minimum_version = None self.name = name self.pypi_name = None self.python2_only = False self.python3_only = False self.rpm_name = None self.skip_check = None self.skip_requires = None self.version_property = None class DependencyDefinitionReader(object): """Dependency definition reader.""" _VALUE_NAMES = frozenset([ 'dpkg_name', 'is_optional', 'l2tbinaries_name', 'maximum_version', 'minimum_version', 'pypi_name', 'python2_only', 'python3_only', 'rpm_name', 'skip_check', 'skip_requires', 'version_property']) def _GetConfigValue(self, config_parser, section_name, value_name): """Retrieves a value from the config parser. Args: config_parser (ConfigParser): configuration parser. section_name (str): name of the section that contains the value. value_name (str): name of the value. Returns: object: configuration value or None if the value does not exists. """ try: return config_parser.get(section_name, value_name) except configparser.NoOptionError: return None def Read(self, file_object): """Reads dependency definitions. Args: file_object (file): file-like object to read from. Yields: DependencyDefinition: dependency definition. """ config_parser = configparser.ConfigParser(interpolation=None) config_parser.read_file(file_object) for section_name in config_parser.sections(): dependency_definition = DependencyDefinition(section_name) for value_name in self._VALUE_NAMES: value = self._GetConfigValue(config_parser, section_name, value_name) setattr(dependency_definition, value_name, value) yield dependency_definition class DependencyHelper(object): """Dependency helper. Attributes: dependencies (dict[str, DependencyDefinition]): dependencies. """ _VERSION_NUMBERS_REGEX = re.compile(r'[0-9.]+') _VERSION_SPLIT_REGEX = re.compile(r'\.|\-') def __init__( self, dependencies_file='dependencies.ini', test_dependencies_file='test_dependencies.ini'): """Initializes a dependency helper. Args: dependencies_file (Optional[str]): path to the dependencies configuration file. test_dependencies_file (Optional[str]): path to the test dependencies configuration file. """ super(DependencyHelper, self).__init__() self._test_dependencies = {} self.dependencies = {} dependency_reader = DependencyDefinitionReader() with open(dependencies_file, 'r', encoding='utf-8') as file_object: for dependency in dependency_reader.Read(file_object): self.dependencies[dependency.name] = dependency if os.path.exists(test_dependencies_file): with open(test_dependencies_file, 'r', encoding='utf-8') as file_object: for dependency in dependency_reader.Read(file_object): self._test_dependencies[dependency.name] = dependency def _CheckPythonModule(self, dependency): """Checks the availability of a Python module. Args: dependency (DependencyDefinition): dependency definition. Returns: tuple: containing: bool: True if the Python module is available and conforms to the minimum required version, False otherwise. str: status message. """ module_object = self._ImportPythonModule(dependency.name) if not module_object: return False, f'missing: {dependency.name:s}' if not dependency.version_property: return True, dependency.name return self._CheckPythonModuleVersion( dependency.name, module_object, dependency.version_property, dependency.minimum_version, dependency.maximum_version) def _CheckPythonModuleVersion( self, module_name, module_object, version_property, minimum_version, maximum_version): """Checks the version of a Python module. Args: module_object (module): Python module. module_name (str): name of the Python module. version_property (str): version attribute or function. minimum_version (str): minimum version. maximum_version (str): maximum version. Returns: tuple: containing: bool: True if the Python module is available and conforms to the minimum required version, False otherwise. str: status message. """ module_version = None if not version_property.endswith('()'): module_version = getattr(module_object, version_property, None) else: version_method = getattr( module_object, version_property[:-2], None) if version_method: module_version = version_method() if not module_version: return False, ( f'unable to determine version information for: {module_name:s}') # Make sure the module version is a string. module_version = f'{module_version!s}' # Split the version string and convert every digit into an integer. # A string compare of both version strings will yield an incorrect result. # Strip any semantic suffixes such as a1, b1, pre, post, rc, dev. module_version = self._VERSION_NUMBERS_REGEX.findall(module_version)[0] if module_version[-1] == '.': module_version = module_version[:-1] try: module_version_map = list( map(int, self._VERSION_SPLIT_REGEX.split(module_version))) except ValueError: return False, ( f'unable to parse module version: {module_name:s} {module_version:s}') if minimum_version: try: minimum_version_map = list( map(int, self._VERSION_SPLIT_REGEX.split(minimum_version))) except ValueError: return False, ( f'unable to parse minimum version: {module_name:s} ' f'{minimum_version:s}') if module_version_map < minimum_version_map: return False, ( f'{module_name:s} version: {module_version!s} is too old, ' f'{minimum_version!s} or later required') if maximum_version: try: maximum_version_map = list( map(int, self._VERSION_SPLIT_REGEX.split(maximum_version))) except ValueError: return False, ( f'unable to parse maximum version: {module_name:s} ' f'{maximum_version:s}') if module_version_map > maximum_version_map: return False, ( f'{module_name:s} version: {module_version!s} is too recent, ' f'{maximum_version!s} or earlier required') return True, f'{module_name:s} version: {module_version!s}' def _ImportPythonModule(self, module_name): """Imports a Python module. Args: module_name (str): name of the module. Returns: module: Python module or None if the module cannot be imported. """ try: module_object = list(map(__import__, [module_name]))[0] except ImportError: return None # If the module name contains dots get the upper most module object. if '.' in module_name: for submodule_name in module_name.split('.')[1:]: module_object = getattr(module_object, submodule_name, None) return module_object def _PrintCheckDependencyStatus( self, dependency, result, status_message, verbose_output=True): """Prints the check dependency status. Args: dependency (DependencyDefinition): dependency definition. result (bool): True if the Python module is available and conforms to the minimum required version, False otherwise. status_message (str): status message. verbose_output (Optional[bool]): True if output should be verbose. """ if not result or dependency.is_optional: if dependency.is_optional: status_indicator = '[OPTIONAL]' else: status_indicator = '[FAILURE]' print(f'{status_indicator:s}\t{status_message:s}') elif verbose_output: print(f'[OK]\t\t{status_message:s}') def CheckDependencies(self, verbose_output=True): """Checks the availability of the dependencies. Args: verbose_output (Optional[bool]): True if output should be verbose. Returns: bool: True if the dependencies are available, False otherwise. """ print('Checking availability and versions of dependencies.') check_result = True for _, dependency in sorted(self.dependencies.items()): if dependency.skip_check: continue result, status_message = self._CheckPythonModule(dependency) if not result and not dependency.is_optional: check_result = False self._PrintCheckDependencyStatus( dependency, result, status_message, verbose_output=verbose_output) if check_result and not verbose_output: print('[OK]') print('') return check_result def CheckTestDependencies(self, verbose_output=True): """Checks the availability of the dependencies when running tests. Args: verbose_output (Optional[bool]): True if output should be verbose. Returns: bool: True if the dependencies are available, False otherwise. """ if not self.CheckDependencies(verbose_output=verbose_output): return False print('Checking availability and versions of test dependencies.') check_result = True for dependency in sorted( self._test_dependencies.values(), key=lambda dependency: dependency.name): if dependency.skip_check: continue result, status_message = self._CheckPythonModule(dependency) if not result and not dependency.is_optional: check_result = False self._PrintCheckDependencyStatus( dependency, result, status_message, verbose_output=verbose_output) if check_result and not verbose_output: print('[OK]') print('') return check_result dfdatetime-20240504/utils/update_release.sh000077500000000000000000000014131461534226200205470ustar00rootroot00000000000000#!/bin/bash # # Script that makes changes in preparation of a new release, such as updating # the version and documentation. EXIT_FAILURE=1; EXIT_SUCCESS=0; VERSION=`date -u +"%Y%m%d"` DPKG_DATE=`date -R` # Update the Python module version. sed "s/__version__ = '[0-9]*'/__version__ = '${VERSION}'/" -i dfdatetime/__init__.py # Update the version in the setuptools configuration. sed "s/version = [0-9]*/version = ${VERSION}/" -i setup.cfg # Update the version in the dpkg configuration files. cat > config/dpkg/changelog << EOT dfdatetime (${VERSION}-1) unstable; urgency=low * Auto-generated -- Log2Timeline maintainers ${DPKG_DATE} EOT # Regenerate the API documentation. tox -edocformatter,docs exit ${EXIT_SUCCESS};