pax_global_header00006660000000000000000000000064140460002720014505gustar00rootroot0000000000000052 comment=5b7aea0124ff914bc3d32d4c06242c3405791062 dfdatetime-20210509/000077500000000000000000000000001404600027200140575ustar00rootroot00000000000000dfdatetime-20210509/.github/000077500000000000000000000000001404600027200154175ustar00rootroot00000000000000dfdatetime-20210509/.github/workflows/000077500000000000000000000000001404600027200174545ustar00rootroot00000000000000dfdatetime-20210509/.github/workflows/test_docker.yml000066400000000000000000000047031404600027200225110ustar00rootroot00000000000000# Run tests on Fedora and Ubuntu Docker images using GIFT CORP and GIFT PPA on commit name: test_docker on: [push] jobs: test_fedora: runs-on: ubuntu-latest strategy: matrix: version: ['32', '33', '34'] container: image: registry.fedoraproject.org/fedora:${{ matrix.version }} steps: - uses: actions/checkout@v2 - 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 python3 python3-mock python3-pbr python3-setuptools python3-six - 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 run: | python3 ./setup.py sdist - name: Build binary distribution run: | python3 ./setup.py bdist - name: Run build and install test run: | python3 ./setup.py build python3 ./setup.py install test_ubuntu: runs-on: ubuntu-latest strategy: matrix: version: ['18.04', '20.04'] container: image: ubuntu:${{ matrix.version }} steps: - uses: actions/checkout@v2 - 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 python3 python3-distutils python3-mock python3-pbr python3-setuptools python3-six - 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 run: | python3 ./setup.py sdist - name: Build binary distribution run: | python3 ./setup.py bdist - name: Run build and install test run: | python3 ./setup.py build python3 ./setup.py install dfdatetime-20210509/.github/workflows/test_tox.yml000066400000000000000000000026061404600027200220540ustar00rootroot00000000000000# Run tox tests on Ubuntu Docker images using GIFT PPA name: test_tox on: [push, pull_request] jobs: build: runs-on: ubuntu-latest strategy: matrix: include: - python-version: 3.6 toxenv: 'py36' - python-version: 3.7 toxenv: 'py37' - python-version: 3.8 toxenv: 'py38,coverage,codecov' - python-version: 3.8 toxenv: 'pylint' - python-version: 3.8 toxenv: 'docs' container: image: ubuntu:20.04 steps: - uses: actions/checkout@v2 - 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 tox python3-distutils python3-mock python3-pbr python3-setuptools python3-six - name: Run tests env: LANG: en_US.UTF-8 run: | tox -e${{ matrix.toxenv }} dfdatetime-20210509/.gitignore000066400000000000000000000004541404600027200160520ustar00rootroot00000000000000# 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-20210509/.pylintrc000066400000000000000000000437141404600027200157350ustar00rootroot00000000000000# Pylint 2.6.x configuration file # # This file is generated by l2tdevtools update-dependencies.py, any dependency # related changes should be made in dependencies.ini. [MASTER] # 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-whitelist= # Specify a score threshold to be exceeded before program exits with error. fail-under=10.0 # Add files or directories to the blacklist. They should be base names, not # paths. ignore=CVS # Add files or directories matching the regex patterns to the blacklist. The # regex matches against base names, not paths. ignore-patterns= # 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. 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 # 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 [MESSAGES CONTROL] # Only show warnings with the listed confidence levels. Leave empty to show # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. confidence= # 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 reenable 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, deprecated-pragma, duplicate-code, eq-without-hash, file-ignored, fixme, locally-disabled, locally-enabled, logging-format-interpolation, metaclass-assignment, missing-param-doc, no-absolute-import, no-self-use, parameter-unpacking, 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, useless-object-inheritance, useless-suppression # 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= [REPORTS] # Python expression which should return a score less than or equal to 10. You # have access to the variables 'error', 'warning', 'refactor', and 'convention' # 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=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, json # and msvs (visual studio). You can also give a reporter class, e.g. # mypackage.mymodule.MyReporterClass. output-format=text # Tells whether to display a full report or only the messages. reports=no # Activate the evaluation score. score=no [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 [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 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. Default to name # with leading underscore. 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 [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 missing members accessed in mixin class should be ignored. A # mixin class is detected if its name ends with "mixin" (case insensitive). ignore-mixin-members=yes # 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 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 # 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= # 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 # List of decorators that change the signature of a decorated function. signature-mutators= [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 [BASIC] # Naming style matching correct argument names. argument-naming-style=snake_case # Regular expression matching correct argument names. Overrides argument- # naming-style. 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. 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. class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]*|(__.*__))$ # Naming style matching correct class names. class-naming-style=PascalCase # Regular expression matching correct class names. Overrides class-naming- # style. 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. 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. 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. 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. 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. 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 # Naming style matching correct variable names. variable-naming-style=snake_case # Regular expression matching correct variable names. Overrides variable- # naming-style. variable-rgx=(([a-z][a-z0-9_]*)|(_[a-z0-9_]*))$ [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= [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=' ' # Maximum number of characters on a single line. 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 [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 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 [SIMILARITIES] # Ignore comments when computing similarities. ignore-comments=yes # Ignore docstrings when computing similarities. ignore-docstrings=yes # Ignore imports when computing similarities. ignore-imports=no # Minimum lines number of a similarity. min-similarity-lines=4 [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 [DESIGN] # Maximum number of arguments for function / method. 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 [CLASSES] # List of method names used to declare (i.e. assign) instance attributes. defining-attr-methods=__init__, __new__, setUp, __post_init__ # List of member names, which should be excluded from the protected access # warning. exclude-protected=_asdict, _fields, _replace, _source, _make # 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=cls [IMPORTS] # List of modules that can be imported at any level, not just the top level # one. allow-any-import-level= # Allow wildcard imports from modules that define __all__. allow-wildcard-with-all=no # 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 # Deprecated modules which should not be used, separated by a comma. deprecated-modules=optparse,tkinter.tix # Create a graph of external dependencies in the given file (report RP0402 must # not be disabled). ext-import-graph= # Create a graph of every (i.e. internal and external) dependencies in the # given file (report RP0402 must not be disabled). import-graph= # Create a graph of internal dependencies in 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= [PARAMETER_DOCUMENTATION] # Whether to accept totally missing parameter documentation in the docstring of # a function that has parameters. accept-no-param-doc=yes # Whether to accept totally missing raises documentation in the docstring of a # function that raises an exception. accept-no-raise-doc=yes # Whether to accept totally missing return documentation in the docstring of a # function that returns a statement. accept-no-return-doc=yes # Whether to accept totally missing yields documentation in the docstring of a # generator. accept-no-yields-doc=yes # If the docstring type cannot be guessed the specified docstring type will be # used. default-docstring-type=default [EXCEPTIONS] # Exceptions that will emit a warning when being caught. Defaults to # "BaseException, Exception". overgeneral-exceptions=BaseException, Exception dfdatetime-20210509/.style.yapf000066400000000000000000000001361404600027200161560ustar00rootroot00000000000000[style] based_on_style = chromium COALESCE_BRACKETS = True SPLIT_BEFORE_FIRST_ARGUMENT = True dfdatetime-20210509/ACKNOWLEDGEMENTS000066400000000000000000000012251404600027200163340ustar00rootroot00000000000000Acknowledgements: 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-20210509/AUTHORS000066400000000000000000000005601404600027200151300ustar00rootroot00000000000000# 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-20210509/LICENSE000066400000000000000000000261361404600027200150740ustar00rootroot00000000000000 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-20210509/MANIFEST.in000066400000000000000000000007021404600027200156140ustar00rootroot00000000000000include ACKNOWLEDGEMENTS AUTHORS LICENSE README include dependencies.ini run_tests.py utils/__init__.py utils/dependencies.py include utils/check_dependencies.py 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-20210509/README000066400000000000000000000003251404600027200147370ustar00rootroot00000000000000dfDateTime, 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-20210509/appveyor.yml000066400000000000000000000033261404600027200164530ustar00rootroot00000000000000environment: pypi_token: secure: /FwQrmudDyj+Mu3DaxLEo23Y6/OEgdHJqyWyZTjkJKje8pxCOrUorN8ZlXRGXbd3UA60emClt0M+SI+xqyA/qkpqZTgd5CKohpVAGH2EfzRc/zwJSGJ4tmZmMVAG8ayk6N9zFxCeC+y0BgZPQnj/Eq/RfuS4YIuaKutIUa5gTMmhWpODFKGV/2Wx1w67xWxAoONfEC5j0Gu3R274SS7FfBb4qWyIiBIJMwHGjlgp1Onk8KlpCLauZv8/hGfQDmWEdZ+mjcsTYyQYr1xfr1/FjQ== matrix: - DESCRIPTION: "Windows with 32-bit Python 3.9" MACHINE_TYPE: "x86" APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 PYTHON: "C:\\Python39" PYTHON_VERSION: "3.9" L2TBINARIES_TRACK: "dev" - DESCRIPTION: "Windows with 64-bit Python 3.9" MACHINE_TYPE: "amd64" APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 PYTHON: "C:\\Python39-x64" PYTHON_VERSION: "3.9" L2TBINARIES_TRACK: "dev" - DESCRIPTION: "Mac OS with Python 3.9" APPVEYOR_BUILD_WORKER_IMAGE: macos HOMEBREW_NO_INSTALL_CLEANUP: 1 install: - cmd: "%PYTHON%\\python.exe -m pip install -U pip setuptools wheel" - cmd: "%PYTHON%\\python.exe -m pip install pywin32 WMI" - cmd: "%PYTHON%\\python.exe %PYTHON%\\Scripts\\pywin32_postinstall.py -install" - ps: If ($isWindows) { .\config\appveyor\install.ps1 } - sh: config/appveyor/install.sh build_script: - cmd: "%PYTHON%\\python.exe setup.py bdist_msi bdist_wheel" test_script: - cmd: "%PYTHON%\\python.exe run_tests.py" - cmd: 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) { Invoke-Expression "${env:PYTHON}\\python.exe -m twine upload dist/*.whl --username __token__ --password ${env:PYPI_TOKEN} --skip-existing" } dfdatetime-20210509/config/000077500000000000000000000000001404600027200153245ustar00rootroot00000000000000dfdatetime-20210509/config/appveyor/000077500000000000000000000000001404600027200171715ustar00rootroot00000000000000dfdatetime-20210509/config/appveyor/install.ps1000066400000000000000000000013741404600027200212710ustar00rootroot00000000000000# Script to set up tests on AppVeyor Windows. $Dependencies = "mock pbr six" $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} --msi-targetdir ${env:PYTHON} --track ${env:L2TBINARIES_TRACK} ${Dependencies} 2>&1" Write-Host (${Output} | Out-String) dfdatetime-20210509/config/appveyor/install.sh000077500000000000000000000001331404600027200211730ustar00rootroot00000000000000# Script to set up tests on AppVeyor MacOS. set -e brew update brew install tox || true dfdatetime-20210509/config/appveyor/runtests.sh000077500000000000000000000007171404600027200214240ustar00rootroot00000000000000#!/bin/sh # Script to run tests # Set the following environment variables to build pycrypto and yara-python. export CFLAGS="-I/usr/local/include -I/usr/local/opt/openssl@1.1/include ${CFLAGS}"; export LDFLAGS="-L/usr/local/lib -L/usr/local/opt/openssl@1.1/lib ${LDFLAGS}"; export TOX_TESTENV_PASSENV="CFLAGS LDFLAGS"; # Set the following environment variables to ensure tox can find Python 3.9. export PATH="/usr/local/opt/python@3.9/bin:${PATH}"; tox -e py39 dfdatetime-20210509/config/dpkg/000077500000000000000000000000001404600027200162515ustar00rootroot00000000000000dfdatetime-20210509/config/dpkg/changelog000066400000000000000000000002551404600027200201250ustar00rootroot00000000000000dfdatetime (20210509-1) unstable; urgency=low * Auto-generated -- Log2Timeline maintainers Sun, 09 May 2021 16:27:46 +0200 dfdatetime-20210509/config/dpkg/clean000066400000000000000000000000271404600027200172550ustar00rootroot00000000000000dfdatetime/*.pyc *.pyc dfdatetime-20210509/config/dpkg/compat000066400000000000000000000000021404600027200174470ustar00rootroot000000000000009 dfdatetime-20210509/config/dpkg/control000066400000000000000000000010761404600027200176600ustar00rootroot00000000000000Source: 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: ${python3: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-20210509/config/dpkg/copyright000066400000000000000000000017101404600027200202030ustar00rootroot00000000000000Format: 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-20210509/config/dpkg/rules000077500000000000000000000001721404600027200173310ustar00rootroot00000000000000#!/usr/bin/make -f %: dh $@ --buildsystem=pybuild --with=python3 .PHONY: override_dh_auto_test override_dh_auto_test: dfdatetime-20210509/config/dpkg/source/000077500000000000000000000000001404600027200175515ustar00rootroot00000000000000dfdatetime-20210509/config/dpkg/source/format000066400000000000000000000000141404600027200207570ustar00rootroot000000000000003.0 (quilt) dfdatetime-20210509/config/pylint/000077500000000000000000000000001404600027200166435ustar00rootroot00000000000000dfdatetime-20210509/config/pylint/spelling-private-dict000066400000000000000000000003451404600027200227760ustar00rootroot00000000000000argparse 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-20210509/dependencies.ini000066400000000000000000000000001404600027200171740ustar00rootroot00000000000000dfdatetime-20210509/dfdatetime.ini000066400000000000000000000007221404600027200166670ustar00rootroot00000000000000[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. dfdatetime-20210509/dfdatetime/000077500000000000000000000000001404600027200161655ustar00rootroot00000000000000dfdatetime-20210509/dfdatetime/__init__.py000066400000000000000000000013701404600027200202770ustar00rootroot00000000000000# -*- 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 fat_date_time from dfdatetime import filetime from dfdatetime import hfs_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 __version__ = '20210509' dfdatetime-20210509/dfdatetime/apfs_time.py000066400000000000000000000050401404600027200205050ustar00rootroot00000000000000# -*- 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 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 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-20210509/dfdatetime/cocoa_time.py000066400000000000000000000104611404600027200206430ustar00rootroot00000000000000# -*- 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, time_zone_offset=None, timestamp=None): """Initializes a Cocoa timestamp. Args: 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__(time_zone_offset=time_zone_offset) self._precision = definitions.PRECISION_1_SECOND 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 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. """ 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) microseconds = date_time_values.get('microseconds', None) time_zone_offset = date_time_values.get('time_zone_offset', 0) timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp += self._COCOA_TO_POSIX_BASE timestamp = float(timestamp) if microseconds is not None: timestamp += float(microseconds) / definitions.MICROSECONDS_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 '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:06d}'.format( year, month, day_of_month, hours, minutes, seconds, microseconds) factory.Factory.RegisterDateTimeValues(CocoaTime) dfdatetime-20210509/dfdatetime/decorators.py000066400000000000000000000013071404600027200207050ustar00rootroot00000000000000# -*- 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('Call to deprecated function: {0:s}.'.format( function.__name__), category=DeprecationWarning, stacklevel=2) return function(*args, **kwargs) IssueDeprecationWarning.__name__ = function.__name__ IssueDeprecationWarning.__doc__ = function.__doc__ IssueDeprecationWarning.__dict__.update(function.__dict__) return IssueDeprecationWarning dfdatetime-20210509/dfdatetime/definitions.py000066400000000000000000000020351404600027200210520ustar00rootroot00000000000000# -*- 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 """ SECONDS_PER_DAY = 24 * 60 * 60 DECISECONDS_PER_SECOND = 10 MILLISECONDS_PER_SECOND = 1000 MICROSECONDS_PER_DAY = 86400000000 MICROSECONDS_PER_SECOND = 1000000 MICROSECONDS_PER_DECISECOND = 100000 MICROSECONDS_PER_MILLISECOND = 1000 NANOSECONDS_PER_SECOND = 1000000000 PRECISION_1_DAY = '1d' PRECISION_1_HOUR = '1h' PRECISION_1_NANOSECOND = '1ns' PRECISION_100_NANOSECONDS = '100ns' PRECISION_1_MICROSECOND = '1us' PRECISION_1_MILLISECOND = '1ms' 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_100_NANOSECONDS, PRECISION_1_MICROSECOND, PRECISION_1_MILLISECOND, PRECISION_100_MILLISECONDS, PRECISION_1_MINUTE, PRECISION_1_SECOND, PRECISION_2_SECONDS]) dfdatetime-20210509/dfdatetime/delphi_date_time.py000066400000000000000000000114761404600027200220300ustar00rootroot00000000000000# -*- 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 Also see: http://docwiki.embarcadero.com/Libraries/XE3/en/System.TDateTime 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, time_zone_offset=None, timestamp=None): """Initializes a Delphi TDateTime timestamp. Args: 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__(time_zone_offset=time_zone_offset) self._precision = definitions.PRECISION_1_MILLISECOND 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 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. """ 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) microseconds = date_time_values.get('microseconds', None) time_zone_offset = date_time_values.get('time_zone_offset', 0) if year > 9999: raise ValueError('Unsupported year value: {0:d}.'.format(year)) timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp = float(timestamp) / definitions.SECONDS_PER_DAY timestamp += self._DELPHI_TO_POSIX_BASE if microseconds is not None: timestamp += float(microseconds) / definitions.MICROSECONDS_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)) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) microseconds = int( (number_of_seconds % 1) * definitions.MICROSECONDS_PER_SECOND) return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:06d}'.format( year, month, day_of_month, hours, minutes, seconds, microseconds) factory.Factory.RegisterDateTimeValues(DelphiDateTime) dfdatetime-20210509/dfdatetime/factory.py000066400000000000000000000035621404600027200202140ustar00rootroot00000000000000# -*- 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('Date and time values type: {0:s} not set.'.format( class_name)) 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('Date and time values type: {0:s} not set.'.format( class_name)) 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('Date and time values type: {0:s} already set.'.format( class_name)) cls._date_time_values_types[class_name] = date_time_values_type dfdatetime-20210509/dfdatetime/fake_time.py000066400000000000000000000076531404600027200204760ustar00rootroot00000000000000# -*- 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, time_zone_offset=None): """Initializes a fake timestamp. Args: 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__(time_zone_offset=time_zone_offset) self._microseconds = int( fraction_of_second * definitions.MICROSECONDS_PER_SECOND) self._number_of_seconds = int(timestamp) self._precision = definitions.PRECISION_1_MICROSECOND 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 or 6 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', 0) self._normalized_timestamp = None self._number_of_seconds = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) self._microseconds = date_time_values.get('microseconds', None) 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) if self._microseconds is None: return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}'.format( year, month, day_of_month, hours, minutes, seconds) return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:06d}'.format( year, month, day_of_month, hours, minutes, seconds, self._microseconds) dfdatetime-20210509/dfdatetime/fat_date_time.py000066400000000000000000000142361404600027200213320ustar00rootroot00000000000000# -*- 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, time_zone_offset=None): """Initializes a FAT date time. Args: fat_date_time (Optional[int]): FAT date time. time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. """ number_of_seconds = None if fat_date_time is not None: number_of_seconds = self._GetNumberOfSeconds(fat_date_time) super(FATDateTime, self).__init__(time_zone_offset=time_zone_offset) self._fat_date_time = fat_date_time self._number_of_seconds = number_of_seconds self._precision = definitions.PRECISION_2_SECONDS @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 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. """ 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', 0) if year < 1980 or year > (1980 + 0x7f): raise ValueError('Year value not supported: {0!s}.'.format(year)) 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 '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}'.format( year, month, day_of_month, hours, minutes, seconds) factory.Factory.RegisterDateTimeValues(FATDateTime) dfdatetime-20210509/dfdatetime/filetime.py000066400000000000000000000110711404600027200203350ustar00rootroot00000000000000# -*- 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, time_zone_offset=None, timestamp=None): """Initializes a FILETIME timestamp. Args: 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__(time_zone_offset=time_zone_offset) self._precision = definitions.PRECISION_100_NANOSECONDS 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._100NS_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 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. """ 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', 0) if year < 1601: raise ValueError('Year value not supported: {0!s}.'.format(year)) timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp += self._FILETIME_TO_POSIX_BASE timestamp *= definitions.MICROSECONDS_PER_SECOND timestamp += date_time_values.get('microseconds', 0) timestamp *= self._100NS_PER_MICROSECOND 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, remainder = divmod(self._timestamp, self._100NS_PER_SECOND) number_of_days, hours, minutes, seconds = self._GetTimeValues(timestamp) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:07d}'.format( year, month, day_of_month, hours, minutes, seconds, remainder) factory.Factory.RegisterDateTimeValues(Filetime) dfdatetime-20210509/dfdatetime/hfs_time.py000066400000000000000000000101631404600027200203360ustar00rootroot00000000000000# -*- 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, time_zone_offset=None, timestamp=None): """Initializes a HFS timestamp. Args: 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__(time_zone_offset=time_zone_offset) self._precision = definitions.PRECISION_1_SECOND 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 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. """ 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', 0) 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 '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}'.format( year, month, day_of_month, hours, minutes, seconds) factory.Factory.RegisterDateTimeValues(HFSTime) dfdatetime-20210509/dfdatetime/interface.py000066400000000000000000000706131404600027200205060ustar00rootroot00000000000000# -*- coding: utf-8 -*- """Date and time interfaces.""" import abc import calendar import decimal from dfdatetime import decorators 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. """ # pylint: disable=redundant-returns-doc _DAYS_PER_MONTH = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) _EPOCH_NORMALIZED_TIME = NormalizedTimeEpoch() _100NS_PER_SECOND = 10000000 _100NS_PER_DECISECOND = 1000000 _100NS_PER_MILLISECOND = 10000 _100NS_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 def __init__(self, time_zone_offset=None): """Initializes date time values. Args: time_zone_offset (Optional[int]): time zone offset in number of minutes from UTC or None if not set. """ super(DateTimeValues, self).__init__() self._normalized_timestamp = None self._precision = None self._time_zone_offset = time_zone_offset self.is_local_time = False @property def precision(self): """precision (str): precision of the date and time value, which should be one of the PRECISION_VALUES in definitions. """ return self._precision @property def time_zone_offset(self): """time_zone_offset (int): time zone offset in number of minutes from UTC or None if not set. """ return self._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 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, 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, microseconds, 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 microseconds is not None: date_time_values['microseconds'] = microseconds 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 or 6 digits. The seconds fraction and time zone offset are optional. Returns: tuple[int, int, int, int, int]: hours, minutes, seconds, microseconds, 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('Hours value: {0:d} out of bounds.'.format(hours)) 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('Minutes value: {0:d} out of bounds.'.format(minutes)) 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('Seconds value: {0:d} out of bounds.'.format(seconds)) microseconds = 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): 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 *= 1000 microseconds = 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, microseconds, 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('Epoch year value: {0:d} out of bounds.'.format( epoch_year)) if epoch_month not in range(1, 13): raise ValueError('Epoch month value: {0:d} out of bounds.'.format( epoch_month)) 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('Epoch day of month value: {0:d} out of bounds.'.format( epoch_day_of_month)) 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 _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 = self._DAYS_PER_MONTH[month - 1] if month == 2 and 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) 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. """ 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 year or not month or not day_of_month: return None # calendar.timegm does not sanity check the time elements. if hours is None: hours = 0 elif hours not in range(0, 24): raise ValueError('Hours value: {0!s} out of bounds.'.format(hours)) if minutes is None: minutes = 0 elif minutes not in range(0, 60): raise ValueError('Minutes value: {0!s} out of bounds.'.format(minutes)) # TODO: support a leap second? if seconds is None: seconds = 0 elif seconds not in range(0, 60): raise ValueError('Seconds value: {0!s} out of bounds.'.format(seconds)) # Note that calendar.timegm() does not raise when date is: 2013-02-29. 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.') # calendar.timegm requires the time tuple to contain at least # 6 integer values. time_elements_tuple = (year, month, day_of_month, hours, minutes, seconds) number_of_seconds = calendar.timegm(time_elements_tuple) return int(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 @decorators.deprecated def CopyFromString(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. """ self.CopyFromDateTimeString(time_string) @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 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. """ 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) # TODO: remove this method when there is no more need for it in dfvfs. def CopyToStatTimeTuple(self): """Copies the date time value to a stat timestamp tuple. Returns: tuple[int, int]: a POSIX timestamp in seconds and the remainder in 100 nano seconds or (None, None) on error. """ normalized_timestamp = self._GetNormalizedTimestamp() if normalized_timestamp is None: return None, None if self._precision in ( definitions.PRECISION_1_NANOSECOND, definitions.PRECISION_100_NANOSECONDS, definitions.PRECISION_1_MICROSECOND, definitions.PRECISION_1_MILLISECOND, definitions.PRECISION_100_MILLISECONDS): remainder = int((normalized_timestamp % 1) * self._100NS_PER_SECOND) return int(normalized_timestamp), remainder return int(normalized_timestamp), None @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') date_time_string = '{0:s}Z'.format(date_time_string) return date_time_string def GetDate(self): """Retrieves the date represented by the date and time values. 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. """ normalized_timestamp = self._GetNormalizedTimestamp() if normalized_timestamp is None: return None, None, None number_of_days, _, _, _ = self._GetTimeValues(normalized_timestamp) try: return self._GetDateValuesWithEpoch( number_of_days, self._EPOCH_NORMALIZED_TIME) except ValueError: return None, None, None def GetDateWithTimeOfDay(self): """Retrieves the date with time of day. 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 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 return year, month, day_of_month, hours, minutes, seconds # 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. """ normalized_timestamp = self._GetNormalizedTimestamp() if normalized_timestamp is None: return None normalized_timestamp *= definitions.MICROSECONDS_PER_SECOND normalized_timestamp = normalized_timestamp.quantize( 1, rounding=decimal.ROUND_HALF_UP) return int(normalized_timestamp) def GetTimeOfDay(self): """Retrieves the time of day represented by the date and time values. 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. """ normalized_timestamp = self._GetNormalizedTimestamp() if normalized_timestamp is None: return None, None, None _, hours, minutes, seconds = self._GetTimeValues(normalized_timestamp) return hours, minutes, seconds dfdatetime-20210509/dfdatetime/java_time.py000066400000000000000000000036711404600027200205050ustar00rootroot00000000000000# -*- 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-20210509/dfdatetime/ole_automation_date.py000066400000000000000000000114751404600027200225630ustar00rootroot00000000000000# -*- 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://docs.microsoft.com/en-us/dotnet/api/system.datetime.tooadate 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, time_zone_offset=None, timestamp=None): """Initializes an OLE Automation date. Args: 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__(time_zone_offset=time_zone_offset) self._precision = definitions.PRECISION_1_MICROSECOND 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 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. """ 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) microseconds = date_time_values.get('microseconds', None) time_zone_offset = date_time_values.get('time_zone_offset', 0) timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp = float(timestamp) if microseconds is not None: timestamp += float(microseconds) / definitions.MICROSECONDS_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 '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:06d}'.format( year, month, day_of_month, hours, minutes, seconds, microseconds) factory.Factory.RegisterDateTimeValues(OLEAutomationDate) dfdatetime-20210509/dfdatetime/posix_time.py000066400000000000000000000366411404600027200207310ustar00rootroot00000000000000# -*- 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, time_zone_offset=None, timestamp=None): """Initializes a POSIX timestamp. Args: 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__(time_zone_offset=time_zone_offset) self._precision = definitions.PRECISION_1_SECOND 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 or 6 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', 0) 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 '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}'.format( year, month, day_of_month, hours, minutes, seconds) 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, time_zone_offset=None, timestamp=None): """Initializes a POSIX timestamp in milliseconds. Args: 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__( time_zone_offset=time_zone_offset) self._precision = definitions.PRECISION_1_MILLISECOND 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 or 6 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) microseconds = date_time_values.get('microseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', 0) timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp *= definitions.MILLISECONDS_PER_SECOND if microseconds: milliseconds, _ = divmod( microseconds, 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 '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:03d}'.format( year, month, day_of_month, hours, minutes, seconds, milliseconds) 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, time_zone_offset=None, timestamp=None): """Initializes a POSIX timestamp in microseconds. Args: 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__( time_zone_offset=time_zone_offset) self._precision = definitions.PRECISION_1_MICROSECOND 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 or 6 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) microseconds = date_time_values.get('microseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', 0) timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp *= definitions.MICROSECONDS_PER_SECOND timestamp += microseconds 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 '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:06d}'.format( year, month, day_of_month, hours, minutes, seconds, microseconds) 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, time_zone_offset=None, timestamp=None): """Initializes a POSIX timestamp in nanoseconds. Args: 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__( time_zone_offset=time_zone_offset) self._precision = definitions.PRECISION_1_NANOSECOND 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 or 6 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) microseconds = date_time_values.get('microseconds', None) time_zone_offset = date_time_values.get('time_zone_offset', 0) timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp *= definitions.NANOSECONDS_PER_SECOND if microseconds: nanoseconds = microseconds * definitions.MILLISECONDS_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 or 6 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 '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:09d}'.format( year, month, day_of_month, hours, minutes, seconds, nanoseconds) 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-20210509/dfdatetime/precisions.py000066400000000000000000000177311404600027200207260ustar00rootroot00000000000000# -*- 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 CopyMicrosecondsToFractionOfSecond(cls, microseconds): """Copies the number of microseconds to a fraction of second value. Args: microseconds (int): number of microseconds. 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 CopyMicrosecondsToFractionOfSecond(cls, microseconds): """Copies the number of microseconds to a fraction of second value. Args: microseconds (int): number of microseconds. 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 microseconds is out of bounds. """ if microseconds < 0 or microseconds >= definitions.MICROSECONDS_PER_SECOND: raise ValueError( 'Number of microseconds value: {0:d} out of bounds.'.format( microseconds)) 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('Fraction of second value: {0:f} out of bounds.'.format( fraction_of_second)) return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}'.format( time_elements_tuple[0], time_elements_tuple[1], time_elements_tuple[2], time_elements_tuple[3], time_elements_tuple[4], time_elements_tuple[5]) class MillisecondsPrecisionHelper(DateTimePrecisionHelper): """Milliseconds precision helper.""" @classmethod def CopyMicrosecondsToFractionOfSecond(cls, microseconds): """Copies the number of microseconds to a fraction of second value. Args: microseconds (int): number of microseconds. Returns: decimal.Decimal: fraction of second, which must be a value between 0.0 and 1.0. Raises: ValueError: if the number of microseconds is out of bounds. """ if microseconds < 0 or microseconds >= definitions.MICROSECONDS_PER_SECOND: raise ValueError( 'Number of microseconds value: {0:d} out of bounds.'.format( microseconds)) milliseconds, _ = divmod( microseconds, definitions.MICROSECONDS_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('Fraction of second value: {0:f} out of bounds.'.format( fraction_of_second)) milliseconds = int(fraction_of_second * definitions.MILLISECONDS_PER_SECOND) return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:03d}'.format( time_elements_tuple[0], time_elements_tuple[1], time_elements_tuple[2], time_elements_tuple[3], time_elements_tuple[4], time_elements_tuple[5], milliseconds) class MicrosecondsPrecisionHelper(DateTimePrecisionHelper): """Microseconds precision helper.""" @classmethod def CopyMicrosecondsToFractionOfSecond(cls, microseconds): """Copies the number of microseconds to a fraction of second value. Args: microseconds (int): number of microseconds. Returns: decimal.Decimal: fraction of second, which must be a value between 0.0 and 1.0. Raises: ValueError: if the number of microseconds is out of bounds. """ if microseconds < 0 or microseconds >= definitions.MICROSECONDS_PER_SECOND: raise ValueError( 'Number of microseconds value: {0:d} out of bounds.'.format( microseconds)) 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('Fraction of second value: {0:f} out of bounds.'.format( fraction_of_second)) microseconds = int(fraction_of_second * definitions.MICROSECONDS_PER_SECOND) return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:06d}'.format( time_elements_tuple[0], time_elements_tuple[1], time_elements_tuple[2], time_elements_tuple[3], time_elements_tuple[4], time_elements_tuple[5], microseconds) class PrecisionHelperFactory(object): """Date time precision helper factory.""" _PRECISION_CLASSES = { definitions.PRECISION_1_MICROSECOND: MicrosecondsPrecisionHelper, definitions.PRECISION_1_MILLISECOND: MillisecondsPrecisionHelper, 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('Unsupported precision: {0!s}'.format(precision)) return precision_helper_class dfdatetime-20210509/dfdatetime/rfc2579_date_time.py000066400000000000000000000175161404600027200216650ustar00rootroot00000000000000# -*- 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://tools.ietf.org/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, rfc2579_date_time_tuple=None): """Initializes a RFC2579 date-time. Args: rfc2579_date_time_tuple: (Optional[tuple[int, int, int, int, int, int, int, int, 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__() self._day_of_month = None self._deciseconds = None self._hours = None self._minutes = None self._month = None self._number_of_seconds = None self._precision = definitions.PRECISION_100_MILLISECONDS 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 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 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) microseconds = date_time_values.get('microseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', 0) deciseconds, _ = divmod( microseconds, definitions.MICROSECONDS_PER_DECISECOND) if year < 0 or year > 65536: raise ValueError('Unsupported year value: {0:d}.'.format(year)) 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 '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:01d}'.format( self._year, self._month, self._day_of_month, self._hours, self._minutes, self._seconds, self._deciseconds) factory.Factory.RegisterDateTimeValues(RFC2579DateTime) dfdatetime-20210509/dfdatetime/semantic_time.py000066400000000000000000000222131404600027200213600ustar00rootroot00000000000000# -*- 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 CopyToStatTimeTuple(self): """Copies the semantic timestamp to a stat timestamp tuple. Returns: tuple[int, int]: a POSIX timestamp in seconds and the remainder in 100 nano seconds, which will always be None, None. """ return None, 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-20210509/dfdatetime/systemtime.py000066400000000000000000000147441404600027200207540ustar00rootroot00000000000000# -*- 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 } Attributes: year (int): year, 1601 through 30827. month (int): month of year, 1 through 12. day_of_week (int): day of week, 0 through 6. 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. milliseconds (int): milliseconds, 0 through 999. """ # TODO: make attributes read-only. def __init__(self, system_time_tuple=None, time_zone_offset=None): """Initializes a SYSTEMTIME structure. Args: 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__(time_zone_offset=time_zone_offset) self._number_of_seconds = None self._precision = definitions.PRECISION_1_MILLISECOND 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) 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 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 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) microseconds = date_time_values.get('microseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', 0) milliseconds, _ = divmod( microseconds, definitions.MICROSECONDS_PER_MILLISECOND) if year < 1601 or year > 30827: raise ValueError('Unsupported year value: {0:d}.'.format(year)) 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 '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:03d}'.format( self.year, self.month, self.day_of_month, self.hours, self.minutes, self.seconds, self.milliseconds) factory.Factory.RegisterDateTimeValues(Systemtime) dfdatetime-20210509/dfdatetime/time_elements.py000066400000000000000000001141071404600027200213750ustar00rootroot00000000000000# -*- 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, time_elements_tuple=None, time_zone_offset=None): """Initializes time elements. Args: 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__(time_zone_offset=time_zone_offset) self._number_of_seconds = None self._precision = definitions.PRECISION_1_SECOND self._time_elements_tuple = time_elements_tuple if time_elements_tuple: if len(time_elements_tuple) < 6: raise ValueError(( 'Invalid time elements tuple at least 6 elements required,' 'got: {0:d}').format(len(time_elements_tuple))) 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 or 6 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, microseconds, 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, microseconds, 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 microseconds is not None: date_time_values['microseconds'] = microseconds 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('Invalid weekday: {0:s}.'.format(weekday_string)) 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('Invalid day of month: {0:s}.'.format( day_of_month_string)) month_string = string_segments[1] month = self._RFC_MONTH_MAPPINGS.get(month_string) if not month: raise ValueError('Invalid month: {0:s}.'.format(month_string)) 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('Invalid year: {0:s}.'.format(year_string)) 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('Invalid weekday: {0:s}.'.format(weekday_string)) 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('Invalid day of month: {0:s}.'.format( day_of_month_string)) month_string = string_segments[1] month = self._RFC_MONTH_MAPPINGS.get(month_string) if not month: raise ValueError('Invalid month: {0:s}.'.format(month_string)) 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('Invalid year: {0:s}.'.format(year_string)) 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, microseconds, 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', 0) 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 or 6 digits. The faction of second and time zone offset are optional. Returns: tuple[int, int, int, int, int]: hours, minutes, seconds, microseconds, time zone offset in minutes. Raises: ValueError: if the time string is invalid or not supported. """ if time_string.endswith('Z'): time_string = time_string[:-1] 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('Hours value: {0:d} out of bounds.'.format(hours)) minutes = None seconds = None microseconds = 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.MICROSECONDS_PER_SECOND microseconds = int(time_fraction) if minutes is not None and minutes not in range(0, 60): raise ValueError('Minutes value: {0:d} out of bounds.'.format(minutes)) # TODO: support a leap second? if seconds is not None and seconds not in range(0, 60): raise ValueError('Seconds value: {0:d} out of bounds.'.format(seconds)) 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, microseconds, 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('Hours value: {0:d} out of bounds.'.format(hours)) 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('Minutes value: {0:d} out of bounds.'.format(minutes)) 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('Seconds value: {0:d} out of bounds.'.format(seconds)) 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('Invalid time zone: {0:s}.'.format(time_zone_string)) else: if time_zone_string[0] not in ('+', '-'): raise ValueError('Invalid time zone: {0:s}.'.format(time_zone_string)) 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 or 6 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 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. """ 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. """ if len(time_elements_tuple) < 6: raise ValueError(( 'Invalid time elements tuple at least 6 elements required,' 'got: {0:d}').format(len(time_elements_tuple))) try: year = int(time_elements_tuple[0], 10) except (TypeError, ValueError): raise ValueError('Invalid year value: {0!s}'.format( time_elements_tuple[0])) try: month = int(time_elements_tuple[1], 10) except (TypeError, ValueError): raise ValueError('Invalid month value: {0!s}'.format( time_elements_tuple[1])) try: day_of_month = int(time_elements_tuple[2], 10) except (TypeError, ValueError): raise ValueError('Invalid day of month value: {0!s}'.format( time_elements_tuple[2])) try: hours = int(time_elements_tuple[3], 10) except (TypeError, ValueError): raise ValueError('Invalid hours value: {0!s}'.format( time_elements_tuple[3])) try: minutes = int(time_elements_tuple[4], 10) except (TypeError, ValueError): raise ValueError('Invalid minutes value: {0!s}'.format( time_elements_tuple[4])) try: seconds = int(time_elements_tuple[5], 10) except (TypeError, ValueError): raise ValueError('Invalid seconds value: {0!s}'.format( time_elements_tuple[5])) 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 return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}'.format( self._time_elements_tuple[0], self._time_elements_tuple[1], self._time_elements_tuple[2], self._time_elements_tuple[3], self._time_elements_tuple[4], self._time_elements_tuple[5]) 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, 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. 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( 'Fraction of second value: {0:f} out of bounds.'.format( fraction_of_second)) super(TimeElementsWithFractionOfSecond, self).__init__( time_elements_tuple=time_elements_tuple, time_zone_offset=time_zone_offset) self._precision = None 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, microseconds, 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) microseconds = date_time_values.get('microseconds', 0) time_zone_offset = date_time_values.get('time_zone_offset', 0) precision_helper = precisions.PrecisionHelperFactory.CreatePrecisionHelper( self._precision) fraction_of_second = precision_helper.CopyMicrosecondsToFractionOfSecond( microseconds) 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.CopyMicrosecondsToFractionOfSecond( datetime_object.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. """ if len(time_elements_tuple) < 7: raise ValueError(( 'Invalid time elements tuple at least 7 elements required,' 'got: {0:d}').format(len(time_elements_tuple))) super(TimeElementsWithFractionOfSecond, self).CopyFromStringTuple( time_elements_tuple) try: fraction_of_second = decimal.Decimal(time_elements_tuple[6]) except (TypeError, ValueError): raise ValueError('Invalid fraction of second value: {0!s}'.format( time_elements_tuple[6])) if fraction_of_second < 0.0 or fraction_of_second >= 1.0: raise ValueError('Fraction of second value: {0:f} out of bounds.'.format( fraction_of_second)) 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) 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, time_elements_tuple=None, time_zone_offset=None): """Initializes time elements. Args: 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: if len(time_elements_tuple) < 7: raise ValueError(( 'Invalid time elements tuple at least 7 elements required,' 'got: {0:d}').format(len(time_elements_tuple))) 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, time_elements_tuple=time_elements_tuple, time_zone_offset=time_zone_offset) self._precision = definitions.PRECISION_1_MILLISECOND @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. """ if len(time_elements_tuple) < 7: raise ValueError(( 'Invalid time elements tuple at least 7 elements required,' 'got: {0:d}').format(len(time_elements_tuple))) year, month, day_of_month, hours, minutes, seconds, milliseconds = ( time_elements_tuple) try: milliseconds = int(milliseconds, 10) except (TypeError, ValueError): raise ValueError('Invalid millisecond value: {0!s}'.format(milliseconds)) 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) 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, time_elements_tuple=None, time_zone_offset=None): """Initializes time elements. Args: 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: if len(time_elements_tuple) < 7: raise ValueError(( 'Invalid time elements tuple at least 7 elements required,' 'got: {0:d}').format(len(time_elements_tuple))) 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, time_elements_tuple=time_elements_tuple, time_zone_offset=time_zone_offset) self._precision = definitions.PRECISION_1_MICROSECOND @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. """ if len(time_elements_tuple) < 7: raise ValueError(( 'Invalid time elements tuple at least 7 elements required,' 'got: {0:d}').format(len(time_elements_tuple))) year, month, day_of_month, hours, minutes, seconds, microseconds = ( time_elements_tuple) try: microseconds = int(microseconds, 10) except (TypeError, ValueError): raise ValueError('Invalid microsecond value: {0!s}'.format(microseconds)) 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) factory.Factory.RegisterDateTimeValues(TimeElements) factory.Factory.RegisterDateTimeValues(TimeElementsInMilliseconds) factory.Factory.RegisterDateTimeValues(TimeElementsInMicroseconds) dfdatetime-20210509/dfdatetime/uuid_time.py000066400000000000000000000113641404600027200205300ustar00rootroot00000000000000# -*- 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, time_zone_offset=None, timestamp=None): """Initializes an UUID version 1 timestamp. Args: 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__(time_zone_offset=time_zone_offset) self._precision = definitions.PRECISION_100_NANOSECONDS 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._100NS_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 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. """ 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', 0) if year < 1582: raise ValueError('Year value not supported.') timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp += self._UUID_TO_POSIX_BASE timestamp *= definitions.MICROSECONDS_PER_SECOND timestamp += date_time_values.get('microseconds', 0) timestamp *= self._100NS_PER_MICROSECOND 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, remainder = divmod(self._timestamp, self._100NS_PER_SECOND) number_of_days, hours, minutes, seconds = self._GetTimeValues(timestamp) year, month, day_of_month = self._GetDateValuesWithEpoch( number_of_days, self._EPOCH) return '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:07d}'.format( year, month, day_of_month, hours, minutes, seconds, remainder) factory.Factory.RegisterDateTimeValues(UUIDTime) dfdatetime-20210509/dfdatetime/webkit_time.py000066400000000000000000000105611404600027200210450ustar00rootroot00000000000000# -*- 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, time_zone_offset=None, timestamp=None): """Initializes a WebKit timestamp. Args: 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__(time_zone_offset=time_zone_offset) self._precision = definitions.PRECISION_1_MICROSECOND 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 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. """ 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', 0) timestamp = self._GetNumberOfSecondsFromElements( year, month, day_of_month, hours, minutes, seconds) timestamp += self._WEBKIT_TO_POSIX_BASE timestamp *= definitions.MICROSECONDS_PER_SECOND timestamp += date_time_values.get('microseconds', 0) 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 '{0:04d}-{1:02d}-{2:02d} {3:02d}:{4:02d}:{5:02d}.{6:06d}'.format( year, month, day_of_month, hours, minutes, seconds, microseconds) factory.Factory.RegisterDateTimeValues(WebKitTime) dfdatetime-20210509/docs/000077500000000000000000000000001404600027200150075ustar00rootroot00000000000000dfdatetime-20210509/docs/conf.py000066400000000000000000000117441404600027200163150ustar00rootroot00000000000000# -*- 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(['six']) 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_private_with_doc = False napoleon_include_special_with_doc = True # General information about the project. # pylint: disable=redefined-builtin project = 'dfDateTime' copyright = 'The dfDateTime Project 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-20210509/docs/index.rst000066400000000000000000000011411404600027200166450ustar00rootroot00000000000000Welcome 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` * :ref:`search` dfdatetime-20210509/docs/requirements.txt000066400000000000000000000001271404600027200202730ustar00rootroot00000000000000docutils recommonmark sphinx >= 2.0.1 sphinx-markdown-tables sphinx-rtd-theme >= 0.5.1 dfdatetime-20210509/docs/sources/000077500000000000000000000000001404600027200164725ustar00rootroot00000000000000dfdatetime-20210509/docs/sources/Date-and-time-values.md000066400000000000000000000333621404600027200226710ustar00rootroot00000000000000# 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://docs.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 Granularity](https://en.wikipedia.org/wiki/Granularity#Data_granularity) * [Wikipedia: Semantics](https://en.wikipedia.org/wiki/Semantics) * [FILETIME structure](https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime) * [File Times](https://docs.microsoft.com/en-us/windows/win32/sysinfo/file-times) * [Precision and accuracy of DateTime](https://docs.microsoft.com/en-us/archive/blogs/ericlippert/precision-and-accuracy-of-datetime), by Eric Lippert, April 8, 2010 ## 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: System.TDateTime](http://docwiki.embarcadero.com/Libraries/XE3/en/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://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-dosdatetimetofiletime) ## 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. Also see: [Latest possible FILETIME](https://stackoverflow.com/questions/9999393/latest-possible-filetime) ### 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://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime) ## 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](http://dubeiko.com/development/FileSystems/HFSPLUS/tn1150.html#HFSPlusDates) ## 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://tools.ietf.org/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://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime) ## 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/+/master/time/time.h#5) dfdatetime-20210509/docs/sources/api/000077500000000000000000000000001404600027200172435ustar00rootroot00000000000000dfdatetime-20210509/docs/sources/api/dfdatetime.rst000066400000000000000000000066131404600027200221110ustar00rootroot00000000000000dfdatetime 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.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.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.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-20210509/docs/sources/api/modules.rst000066400000000000000000000001031404600027200214370ustar00rootroot00000000000000dfdatetime ========== .. toctree:: :maxdepth: 4 dfdatetime dfdatetime-20210509/docs/sources/user/000077500000000000000000000000001404600027200174505ustar00rootroot00000000000000dfdatetime-20210509/docs/sources/user/Installation-instructions.md000066400000000000000000000031651404600027200252020ustar00rootroot00000000000000# 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 18.04 and 20.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; * 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) and [Python WMI](https://pypi.org/project/WMI). To install the release versions of the dependencies run: ``` set PYTHONPATH=. C:\Python38\python.exe tools\update.py --preset dfdatetime ``` dfdatetime-20210509/docs/sources/user/index.rst000066400000000000000000000004441404600027200213130ustar00rootroot00000000000000############### 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-20210509/requirements.txt000066400000000000000000000000151404600027200173370ustar00rootroot00000000000000pip >= 7.0.0 dfdatetime-20210509/run_tests.py000077500000000000000000000020031404600027200164550ustar00rootroot00000000000000#!/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('Using Python version {0!s}'.format(sys.version)) 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-20210509/setup.cfg000066400000000000000000000004451404600027200157030ustar00rootroot00000000000000[metadata] license_file = LICENSE [bdist_rpm] release = 1 packager = Log2Timeline maintainers doc_files = ACKNOWLEDGEMENTS AUTHORS LICENSE README build_requires = python3-setuptools [bdist_wheel] universal = 1 dfdatetime-20210509/setup.py000077500000000000000000000123301404600027200155730ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Installation and deployment script.""" import sys try: from setuptools import find_packages, setup except ImportError: from distutils.core import find_packages, setup try: from distutils.command.bdist_msi import bdist_msi except ImportError: bdist_msi = None try: from distutils.command.bdist_rpm import bdist_rpm except ImportError: bdist_rpm = None version_tuple = (sys.version_info[0], sys.version_info[1]) if version_tuple < (3, 6): print(( 'Unsupported Python version: {0:s}, version 3.6 or higher ' 'required.').format(sys.version)) sys.exit(1) # Change PYTHONPATH to include dfdatetime so that we can get the version. sys.path.insert(0, '.') import dfdatetime # pylint: disable=wrong-import-position if not bdist_msi: BdistMSICommand = None else: class BdistMSICommand(bdist_msi): """Custom handler for the bdist_msi command.""" # pylint: disable=invalid-name def run(self): """Builds an MSI.""" # Command bdist_msi does not support the library version, neither a date # as a version but if we suffix it with .1 everything is fine. self.distribution.metadata.version += '.1' bdist_msi.run(self) if not bdist_rpm: BdistRPMCommand = None else: class BdistRPMCommand(bdist_rpm): """Custom handler for the bdist_rpm command.""" # pylint: disable=invalid-name def _make_spec_file(self): """Generates the text of an RPM spec file. Returns: list[str]: lines of the RPM spec file. """ # Note that bdist_rpm can be an old style class. if issubclass(BdistRPMCommand, object): spec_file = super(BdistRPMCommand, self)._make_spec_file() else: spec_file = bdist_rpm._make_spec_file(self) python_package = 'python3' description = [] requires = '' summary = '' in_description = False python_spec_file = [] for line in iter(spec_file): if line.startswith('Summary: '): summary = line[9:] elif line.startswith('BuildRequires: '): line = 'BuildRequires: {0:s}-setuptools, {0:s}-devel'.format( python_package) elif line.startswith('Requires: '): requires = line[10:] continue elif line.startswith('%description'): in_description = True elif line.startswith('python setup.py build'): if python_package == 'python3': line = '%py3_build' else: line = '%py2_build' elif line.startswith('python setup.py install'): if python_package == 'python3': line = '%py3_install' else: line = '%py2_install' elif line.startswith('%files'): lines = [ '%files -n {0:s}-%{{name}}'.format(python_package), '%defattr(644,root,root,755)', '%license LICENSE', '%doc ACKNOWLEDGEMENTS AUTHORS README'] lines.extend([ '%{python3_sitelib}/dfdatetime/*.py', '%{python3_sitelib}/dfdatetime*.egg-info/*', '', '%exclude %{_prefix}/share/doc/*', '%exclude %{python3_sitelib}/dfdatetime/__pycache__/*']) python_spec_file.extend(lines) break elif line.startswith('%prep'): in_description = False python_spec_file.append( '%package -n {0:s}-%{{name}}'.format(python_package)) python_summary = 'Python 3 module of {0:s}'.format(summary) if requires: python_spec_file.append('Requires: {0:s}'.format(requires)) python_spec_file.extend([ 'Summary: {0:s}'.format(python_summary), '', '%description -n {0:s}-%{{name}}'.format(python_package)]) python_spec_file.extend(description) elif in_description: # Ignore leading white lines in the description. if not description and not line: continue description.append(line) python_spec_file.append(line) return python_spec_file dfdatetime_description = ( 'Digital Forensics date and time (dfDateTime).') dfdatetime_long_description = ( 'dfDateTime, or Digital Forensics date and time, provides date and time ' 'objects to preserve accuracy and precision.') setup( name='dfdatetime', version=dfdatetime.__version__, description=dfdatetime_description, long_description=dfdatetime_long_description, license='Apache License, Version 2.0', url='https://github.com/log2timeline/dfdatetime', maintainer='Log2Timeline maintainers', maintainer_email='log2timeline-maintainers@googlegroups.com', cmdclass={ 'bdist_msi': BdistMSICommand, 'bdist_rpm': BdistRPMCommand}, classifiers=[ 'Development Status :: 3 - Alpha', 'Environment :: Console', 'Operating System :: OS Independent', 'Programming Language :: Python', ], packages=find_packages('.', exclude=[ 'docs', 'tests', 'tests.*', 'utils']), package_dir={ 'dfdatetime': 'dfdatetime' }, data_files=[ ('share/doc/dfdatetime', [ 'ACKNOWLEDGEMENTS', 'AUTHORS', 'LICENSE', 'README']), ], ) dfdatetime-20210509/test_dependencies.ini000066400000000000000000000004371404600027200202510ustar00rootroot00000000000000[mock] dpkg_name: python3-mock minimum_version: 2.0.0 rpm_name: python3-mock version_property: __version__ [pbr] dpkg_name: python3-pbr minimum_version: 4.2.0 rpm_name: python3-pbr [six] dpkg_name: python3-six minimum_version: 1.1.0 rpm_name: python3-six version_property: __version__ dfdatetime-20210509/test_requirements.txt000066400000000000000000000000501404600027200203750ustar00rootroot00000000000000mock >= 2.0.0 pbr >= 4.2.0 six >= 1.1.0 dfdatetime-20210509/tests/000077500000000000000000000000001404600027200152215ustar00rootroot00000000000000dfdatetime-20210509/tests/__init__.py000066400000000000000000000000301404600027200173230ustar00rootroot00000000000000# -*- coding: utf-8 -*- dfdatetime-20210509/tests/apfs_time.py000066400000000000000000000036221404600027200175450ustar00rootroot00000000000000#!/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() 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-20210509/tests/cocoa_time.py000066400000000000000000000132171404600027200177010ustar00rootroot00000000000000#!/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() 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, 0) 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, 0) 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, 0) 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, 0) 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.546875Z') 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-20210509/tests/delphi_date_time.py000066400000000000000000000154371404600027200210650ustar00rootroot00000000000000#!/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() 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, 0) 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, 0) 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, 0) 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, 0) 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.553919Z') 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)) 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-20210509/tests/factory.py000066400000000000000000000051261404600027200172460ustar00rootroot00000000000000#!/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-20210509/tests/fake_time.py000066400000000000000000000131041404600027200175160ustar00rootroot00000000000000#!/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._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, 0) 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, 0) 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, 0) 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, 0) 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.546875Z') 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-20210509/tests/fat_date_time.py000066400000000000000000000143061404600027200203640ustar00rootroot00000000000000#!/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() 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, 0) 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, 0) 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, 0) 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, 0) 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:32Z') 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)) if __name__ == '__main__': unittest.main() dfdatetime-20210509/tests/filetime.py000066400000000000000000000122251404600027200173730ustar00rootroot00000000000000#!/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=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, 0) filetime_object.CopyFromDateTimeString('2010-08-12 21:06:31') self.assertEqual(filetime_object._timestamp, 0x1cb3a623cb6a580) self.assertEqual(filetime_object._time_zone_offset, 0) filetime_object.CopyFromDateTimeString('2010-08-12 21:06:31.546875') self.assertEqual(filetime_object._timestamp, 0x01cb3a623d0a17ce) self.assertEqual(filetime_object._time_zone_offset, 0) 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, 0) 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.5468750Z') 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-20210509/tests/hfs_time.py000066400000000000000000000122121404600027200173670ustar00rootroot00000000000000#!/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=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, 0) 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, 0) 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, 0) 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, 0) 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:28Z') 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-20210509/tests/interface.py000066400000000000000000000526051404600027200175430ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the date and time interfaces.""" import unittest 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, 'microseconds': 546875} 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, 'microseconds': 546875, '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, 'microseconds': 546875, '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) 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, 327000, None) time_tuple = date_time_values._CopyTimeFromString('20:23:56.327') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 23, 56, 327000, 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, 327124, None) time_tuple = date_time_values._CopyTimeFromString('20:23:56.327124') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 23, 56, 327124, -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(interface.DateTimeValues._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( 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( 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.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, 13, 12, 24, 6, 31) with self.assertRaises(ValueError): date_time_values._GetNumberOfSecondsFromElements( 2010, 13, 12, 21, 99, 31) with self.assertRaises(ValueError): date_time_values._GetNumberOfSecondsFromElements( 2010, 13, 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-20210509/tests/java_time.py000066400000000000000000000112011404600027200175250ustar00rootroot00000000000000#!/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() 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, 0) 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, 0) 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, 0) 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, 0) 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.546Z') 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-20210509/tests/ole_automation_date.py000066400000000000000000000137021404600027200216120ustar00rootroot00000000000000#!/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() 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, 0) 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, 0) 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, 0) 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, 0) 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.038400Z') 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-20210509/tests/posix_time.py000066400000000000000000000517401404600027200177620ustar00rootroot00000000000000#!/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() 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, 0) 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, 0) 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, 0) 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, 0) 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:31Z') # TODO: remove this method when there is no more need for it in dfvfs. def testCopyToStatTimeTuple(self): """Tests the CopyToStatTimeTuple function.""" posix_time_object = posix_time.PosixTime(timestamp=1281643591) stat_time_tuple = posix_time_object.CopyToStatTimeTuple() self.assertEqual(stat_time_tuple, (1281643591, None)) posix_time_object = posix_time.PosixTime() stat_time_tuple = posix_time_object.CopyToStatTimeTuple() self.assertEqual(stat_time_tuple, (None, None)) 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() 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, 0) 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, 0) 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, 0) 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, 0) 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.546Z') # TODO: remove this method when there is no more need for it in dfvfs. def testCopyToStatTimeTuple(self): """Tests the CopyToStatTimeTuple function.""" posix_time_object = posix_time.PosixTimeInMilliseconds( timestamp=1281643591546) stat_time_tuple = posix_time_object.CopyToStatTimeTuple() self.assertEqual(stat_time_tuple, (1281643591, 5460000)) posix_time_object = posix_time.PosixTimeInMilliseconds() stat_time_tuple = posix_time_object.CopyToStatTimeTuple() self.assertEqual(stat_time_tuple, (None, None)) 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.PosixTimeInMilliseconds( timestamp=1281643591546) normalized_timestamp = posix_time_object._GetNormalizedTimestamp() self.assertEqual(normalized_timestamp, decimal.Decimal('1281643591.546')) 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() 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, 0) 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, 0) 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, 0) 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, 0) 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.546875Z') # TODO: remove this method when there is no more need for it in dfvfs. def testCopyToStatTimeTuple(self): """Tests the CopyToStatTimeTuple function.""" posix_time_object = posix_time.PosixTimeInMicroseconds( timestamp=1281643591546875) stat_time_tuple = posix_time_object.CopyToStatTimeTuple() self.assertEqual(stat_time_tuple, (1281643591, 5468750)) posix_time_object = posix_time.PosixTimeInMicroseconds() stat_time_tuple = posix_time_object.CopyToStatTimeTuple() self.assertEqual(stat_time_tuple, (None, None)) 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() 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, 0) 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, 0) 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, 0) 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, 0) 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.987654321Z') 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-20210509/tests/precisions.py000066400000000000000000000111371404600027200177540ustar00rootroot00000000000000#!/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 testCopyMicrosecondsToFractionOfSecond(self): """Tests the CopyMicrosecondsToFractionOfSecond function.""" precision_helper = precisions.DateTimePrecisionHelper with self.assertRaises(NotImplementedError): precision_helper.CopyMicrosecondsToFractionOfSecond(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 testCopyMicrosecondsToFractionOfSecond(self): """Tests the CopyMicrosecondsToFractionOfSecond function.""" precision_helper = precisions.SecondsPrecisionHelper fraction_of_second = precision_helper.CopyMicrosecondsToFractionOfSecond( 123456) self.assertEqual(fraction_of_second, 0.0) with self.assertRaises(ValueError): precision_helper.CopyMicrosecondsToFractionOfSecond(-1) with self.assertRaises(ValueError): precision_helper.CopyMicrosecondsToFractionOfSecond(1000000) 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 MillisecondsPrecisionHelperTest(unittest.TestCase): """Tests for the milliseconds precision helper.""" def testCopyMicrosecondsToFractionOfSecond(self): """Tests the CopyMicrosecondsToFractionOfSecond function.""" precision_helper = precisions.MillisecondsPrecisionHelper fraction_of_second = precision_helper.CopyMicrosecondsToFractionOfSecond( 123456) self.assertEqual(fraction_of_second, decimal.Decimal('0.123')) with self.assertRaises(ValueError): precision_helper.CopyMicrosecondsToFractionOfSecond(-1) with self.assertRaises(ValueError): precision_helper.CopyMicrosecondsToFractionOfSecond(1000000) 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 MicrosecondsPrecisionHelperTest(unittest.TestCase): """Tests for the milliseconds precision helper.""" def testCopyMicrosecondsToFractionOfSecond(self): """Tests the CopyMicrosecondsToFractionOfSecond function.""" precision_helper = precisions.MicrosecondsPrecisionHelper fraction_of_second = precision_helper.CopyMicrosecondsToFractionOfSecond( 123456) self.assertEqual(fraction_of_second, decimal.Decimal('0.123456')) with self.assertRaises(ValueError): precision_helper.CopyMicrosecondsToFractionOfSecond(-1) with self.assertRaises(ValueError): precision_helper.CopyMicrosecondsToFractionOfSecond(1000000) 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 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-20210509/tests/rfc2579_date_time.py000066400000000000000000000266351404600027200207230ustar00rootroot00000000000000#!/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() 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, 0) 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, 0) 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, 0) 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, 0) 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.6Z') 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-20210509/tests/semantic_time.py000066400000000000000000000166101404600027200204200ustar00rootroot00000000000000#!/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() semantic_time_object1._SORT_ORDER = 1 semantic_time_object2 = semantic_time.SemanticTime() 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() 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 testCopyToStatTimeTuple(self): """Tests the CopyToStatTimeTuple function.""" semantic_time_object = semantic_time.SemanticTime() stat_time_tuple = semantic_time_object.CopyToStatTimeTuple() self.assertEqual(stat_time_tuple, (None, None)) 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() 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-20210509/tests/systemtime.py000066400000000000000000000216151404600027200200030ustar00rootroot00000000000000#!/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() 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, 0) 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, 0) 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, 0) 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, 0) 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.142Z') 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-20210509/tests/time_elements.py000066400000000000000000001601451404600027200204340ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for the time elements implementation.""" import datetime import decimal import unittest 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) 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, 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() 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, 'microseconds': 546875} 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, 'microseconds': 546875, '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, 'microseconds': 546875, '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) 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 = (8, 4, 32, None, None) time_tuple = time_elements_object._CopyTimeFromStringISO8601('08:04:32Z') 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, 327000, None) time_tuple = time_elements_object._CopyTimeFromStringISO8601('20:23:56.327') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 23, 56, 327000, 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, 327124, None) time_tuple = time_elements_object._CopyTimeFromStringISO8601( '20:23:56.327124') self.assertEqual(time_tuple, expected_time_tuple) expected_time_tuple = (20, 23, 56, 327124, -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 testCopyFromString(self): """Tests the CopyFromString function.""" time_elements_object = time_elements.TimeElements() expected_time_elements_tuple = (2010, 8, 12, 0, 0, 0) expected_number_of_seconds = 1281571200 time_elements_object.CopyFromString('2010-08-12') self.assertEqual( time_elements_object._time_elements_tuple, expected_time_elements_tuple) self.assertEqual( time_elements_object._number_of_seconds, expected_number_of_seconds) 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, 0) 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, 0) 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, 0) 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, 0) 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, 0) 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, 0) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31Z') 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, 0) 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, 0) 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, 0) 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.0000000Z') 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() 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:31Z') 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 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)) 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() 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, 0) 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, 0) 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, 0) 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, 0) 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, 0) 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, 0) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31Z') 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, 0) 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, 0) 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, 0) 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.0000000Z') 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.429Z') 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)) 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, 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.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() 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, 0) 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, 0) 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, 0) 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, 0) 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, 0) 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, 0) expected_time_elements_tuple = (2010, 8, 12, 21, 6, 31) time_elements_object.CopyFromStringISO8601('2010-08-12T21:06:31Z') 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, 0) 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, 0) 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, 0) 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.0000000Z') 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.429876Z') 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)) if __name__ == '__main__': unittest.main() dfdatetime-20210509/tests/uuid_time.py000066400000000000000000000151621404600027200175640ustar00rootroot00000000000000#!/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_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, 0) 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, 0) 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, 0) 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, 0) 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.6544084Z') 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-20210509/tests/webkit_time.py000066400000000000000000000124201404600027200200750ustar00rootroot00000000000000#!/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=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, 0) 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, 0) 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, 0) 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, 0) 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.546875Z') 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-20210509/tox.ini000066400000000000000000000022741404600027200153770ustar00rootroot00000000000000[tox] envlist = py3{6,7,8,9},coverage,docs,pylint [testenv] pip_pre = True setenv = PYTHONPATH = {toxinidir} deps = -rrequirements.txt -rtest_requirements.txt coverage: coverage commands = py3{6,7,8,9}: ./run_tests.py coverage: coverage erase coverage: coverage run --source=dfdatetime --omit="*_test*,*__init__*,*test_lib*" run_tests.py [testenv:codecov] skip_install = true passenv = GITHUB_ACTION GITHUB_HEAD_REF GITHUB_REF GITHUB_REPOSITORY GITHUB_RUN_ID GITHUB_SHA deps = codecov < 2.1.10 commands = codecov [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:pylint] skipsdist=True pip_pre = True setenv = PYTHONPATH = {toxinidir} deps = -rrequirements.txt -rtest_requirements.txt pylint >= 2.6.0, < 2.7.0 commands = pylint --version # Ignore setup.py for now due to: # setup.py:15:0: E0001: Cannot import 'distutils.command.bdist_msi' due to # syntax error 'expected an indented block (, line 347)' (syntax-error) pylint --rcfile=.pylintrc dfdatetime tests dfdatetime-20210509/utils/000077500000000000000000000000001404600027200152175ustar00rootroot00000000000000dfdatetime-20210509/utils/__init__.py000066400000000000000000000000301404600027200173210ustar00rootroot00000000000000# -*- coding: utf-8 -*- dfdatetime-20210509/utils/check_dependencies.py000077500000000000000000000006461404600027200213650ustar00rootroot00000000000000#!/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-20210509/utils/dependencies.py000066400000000000000000000267431404600027200202330ustar00rootroot00000000000000# -*- 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. 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.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', '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') 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') 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: status_message = 'missing: {0:s}'.format(dependency.name) return False, status_message 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: status_message = ( 'unable to determine version information for: {0:s}').format( module_name) return False, status_message # Make sure the module version is a string. module_version = '{0!s}'.format(module_version) # 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: status_message = 'unable to parse module version: {0:s} {1:s}'.format( module_name, module_version) return False, status_message if minimum_version: try: minimum_version_map = list( map(int, self._VERSION_SPLIT_REGEX.split(minimum_version))) except ValueError: status_message = 'unable to parse minimum version: {0:s} {1:s}'.format( module_name, minimum_version) return False, status_message if module_version_map < minimum_version_map: status_message = ( '{0:s} version: {1!s} is too old, {2!s} or later required').format( module_name, module_version, minimum_version) return False, status_message if maximum_version: try: maximum_version_map = list( map(int, self._VERSION_SPLIT_REGEX.split(maximum_version))) except ValueError: status_message = 'unable to parse maximum version: {0:s} {1:s}'.format( module_name, maximum_version) return False, status_message if module_version_map > maximum_version_map: status_message = ( '{0:s} version: {1!s} is too recent, {2!s} or earlier ' 'required').format(module_name, module_version, maximum_version) return False, status_message status_message = '{0:s} version: {1!s}'.format(module_name, module_version) return True, status_message 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('{0:s}\t{1:s}'.format(status_indicator, status_message)) elif verbose_output: print('[OK]\t\t{0:s}'.format(status_message)) 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: 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-20210509/utils/update_release.sh000077500000000000000000000014711404600027200205430ustar00rootroot00000000000000#!/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 dpkg configuration files. cat > config/dpkg/changelog << EOT dfdatetime (${VERSION}-1) unstable; urgency=low * Auto-generated -- Log2Timeline maintainers ${DPKG_DATE} EOT # Regenerate the supported formats documentation. # TODO: generate supported formats. # PYTHONPATH=. ./utils/export_supported_formats.py > docs/sources/Supported-formats.md # Regenerate the API documentation. tox -edocs exit ${EXIT_SUCCESS};