Repository: conda/conda

Issue:
ValidationError: invalid value for timestamp
MODERATOR EDIT:  This issue relates to an error that looks something like

    ValidationError: Invalid value 1505856869.685 for timestamp

The issue relates to using conda 4.3.28, and then downgrading (presumably via conda-forge) to a previous version of conda, like 4.3.27.

# SOLUTION #

### To fix on macOS:

    # in the command below, set PATH_TO_ENVIRONMENT yourself
    sed -i '' -E 's|("timestamp": [0-9]+)\.|\1|' /PATH_TO_ENVIRONMENT/conda-meta/*.json

### To fix on Linux:

    # in the command below, set PATH_TO_ENVIRONMENT yourself
    sed -i -E 's|("timestamp": [0-9]+)\.|\1|' /PATH_TO_ENVIRONMENT/conda-meta/*.json

### To fix on Windows:

Open notepad, and copy the contents below to `c:\fix_timestamps.py`

```python
PATH_TO_ENVIRONMENT="c:\\ProgramData\\Anaconda3"  # <-- fill this in yourself
                                                  # backslashes must be doubled

from glob import glob
import json
import os
for path in glob(os.path.join(PATH_TO_ENVIRONMENT, 'conda-meta', '*.json')):
    with open(path) as fh:
        content = json.load(fh)
    if 'timestamp' in content:
        old_timestamp = content['timestamp']
        content['timestamp'] = int(old_timestamp)
        if old_timestamp != content['timestamp']:
            with open(path, 'w') as fh:
               fh.write(json.dumps(content, indent=2, sort_keys=True, separators=(',', ': ')))
```

Also, change the path in the variable `PATH_TO_ENVIRONMENT` to point to the conda environment you want to fix.  Then run the script with `python c:\fix_timestamps.py`.

----

EDITED: I realized this is not a pyqt issue, it's a conda issue

### steps to reproduce

1. Install 64-bit miniconda on windows 10 (version 4.3.27)  
2. conda update to `4.3.28-py36h9daa44c_0`  
3. conda install -c anaconda spyder  
4. conda config --add channels conda-forge  

Now any calls to conda results in the print-out below.

### conda info

This prints any time I try to use a conda command.

```conda install -c dsdale24 pyqt5
An unexpected error has occurred.
Please consider posting the following information to the
conda GitHub issue tracker at:

    https://github.com/conda/conda/issues



Current conda install:

               platform : win-64
          conda version : 4.3.27
       conda is private : False
      conda-env version : 4.3.27
    conda-build version : not installed
         python version : 3.6.2.final.0
       requests version : 2.18.4
       root environment : C:\Users\jrinker\Miniconda3  (writable)
    default environment : C:\Users\jrinker\Miniconda3
       envs directories : C:\Users\jrinker\Miniconda3\envs
                          C:\Users\jrinker\AppData\Local\conda\conda\envs
                          C:\Users\jrinker\.conda\envs
          package cache : C:\Users\jrinker\Miniconda3\pkgs
                          C:\Users\jrinker\AppData\Local\conda\conda\pkgs
           channel URLs : https://conda.anaconda.org/dsdale24/win-64
                          https://conda.anaconda.org/dsdale24/noarch
                          https://conda.anaconda.org/conda-forge/win-64
                          https://conda.anaconda.org/conda-forge/noarch
                          https://repo.continuum.io/pkgs/main/win-64
                          https://repo.continuum.io/pkgs/main/noarch
                          https://repo.continuum.io/pkgs/free/win-64
                          https://repo.continuum.io/pkgs/free/noarch
                          https://repo.continuum.io/pkgs/r/win-64
                          https://repo.continuum.io/pkgs/r/noarch
                          https://repo.continuum.io/pkgs/pro/win-64
                          https://repo.continuum.io/pkgs/pro/noarch
                          https://repo.continuum.io/pkgs/msys2/win-64
                          https://repo.continuum.io/pkgs/msys2/noarch
            config file : C:\Users\jrinker\.condarc
             netrc file : None
           offline mode : False
             user-agent : conda/4.3.27 requests/2.18.4 CPython/3.6.2 Windows/10 Windows/10.0.15063
          administrator : False

`$ C:\Users\jrinker\Miniconda3\Scripts\conda install -c dsdale24 pyqt5`




    Traceback (most recent call last):
      File "C:\Users\jrinker\Miniconda3\lib\site-packages\conda\exceptions.py", line 640, in conda_exception_handler
        return_value = func(*args, **kwargs)
      File "C:\Users\jrinker\Miniconda3\lib\site-packages\conda\cli\main.py", line 140, in _main
        exit_code = args.func(args, p)
      File "C:\Users\jrinker\Miniconda3\lib\site-packages\conda\cli\main_install.py", line 80, in execute
        install(args, parser, 'install')
      File "C:\Users\jrinker\Miniconda3\lib\site-packages\conda\cli\install.py", line 160, in install
        linked_dists = install_linked(prefix)
      File "C:\Users\jrinker\Miniconda3\lib\site-packages\conda\core\linked_data.py", line 123, in linked
        return set(linked_data(prefix, ignore_channels=ignore_channels).keys())
      File "C:\Users\jrinker\Miniconda3\lib\site-packages\conda\core\linked_data.py", line 115, in linked_data
        load_linked_data(prefix, dist_name, ignore_channels=ignore_channels)
      File "C:\Users\jrinker\Miniconda3\lib\site-packages\conda\core\linked_data.py", line 68, in load_linked_data
        linked_data_[prefix][dist] = rec = IndexRecord(**rec)
      File "C:\Users\jrinker\Miniconda3\lib\site-packages\conda\_vendor\auxlib\entity.py", line 702, in __call__
        instance = super(EntityType, cls).__call__(*args, **kwargs)
      File "C:\Users\jrinker\Miniconda3\lib\site-packages\conda\_vendor\auxlib\entity.py", line 719, in __init__
        setattr(self, key, kwargs[key])
      File "C:\Users\jrinker\Miniconda3\lib\site-packages\conda\_vendor\auxlib\entity.py", line 424, in __set__
        instance.__dict__[self.name] = self.validate(instance, self.box(instance, val))
      File "C:\Users\jrinker\Miniconda3\lib\site-packages\conda\_vendor\auxlib\entity.py", line 465, in validate
        raise ValidationError(getattr(self, 'name', 'undefined name'), val)
    conda._vendor.auxlib.exceptions.ValidationError: Invalid value 1505856869.685 for timestamp
```  

### things I've tried

- Removing conda forge from channels
- `conda update conda` (still prints out error)


Discussion:
@kalefranz, @msarahan, is this something to do with the * 1000 thing? Is there some code that's dividing these numbers back down again? Was such code every released in the wild?
v4.3.28 has this: https://github.com/conda/conda/commit/527c0d3c6d988ae44280b324da58950005f2bbdd

Python2 will:
```
>>> 1505856869685/1000
1505856869
```
Python3 will:
```
>>> 1505856869685/1000
1505856869.685
```
sorry, i dont understand. How should I fix it?
If the issue is line 60 in `conda/models/index_record.py`, then a possible bug fix is just changing it to  
`val //= 1000`  

But I don't know enough about the code to know if this is what should be changed.
The problems seems mushroming 
#6106 
#6107 
#6103 
#6105 
We are working to identify the proper fix.  We are removing conda 4.3.28 from defaults in the meantime to prevent any further breakage.

For those who are already affected by this bug, we are working to come up with a simple series of steps to restore your functionality.
@msarahan Hopefully it can be smoothly fixed without the need to reinstall everything.
@Lemmaho yes, that is most certainly our goal.  @nehaljwani will be posting the fix he's been testing shortly.

Resolving patch:
diff --git a/conda/models/index_record.py b/conda/models/index_record.py
--- a/conda/models/index_record.py
+++ b/conda/models/index_record.py
@@ -54,12 +54,31 @@ def box(self, instance, val):
 
 class TimestampField(NumberField):
 
-    def box(self, instance, val):
-        val = super(TimestampField, self).box(instance, val)
-        if val and val > 253402300799:  # 9999-12-31
-            val /= 1000  # convert milliseconds to seconds; see conda/conda-build#1988
+    @staticmethod
+    def _make_seconds(val):
+        if val:
+            val = int(val)
+            if val > 253402300799:  # 9999-12-31
+                val //= 1000  # convert milliseconds to seconds; see conda/conda-build#1988
         return val
 
+    # @staticmethod
+    # def _make_milliseconds(val):
+    #     if val:
+    #         val = int(val)
+    #         if val < 253402300799:  # 9999-12-31
+    #             val *= 1000  # convert seconds to milliseconds
+    #     return val
+
+    def box(self, instance, val):
+        return self._make_seconds(super(TimestampField, self).box(instance, val))
+
+    def unbox(self, instance, instance_type, val):
+        return self._make_seconds(super(TimestampField, self).unbox(instance, instance_type, val))
+
+    def dump(self, val):
+        return self._make_seconds(super(TimestampField, self).dump(val))
+
 
 class Link(DictSafeMixin, Entity):
     source = StringField()


Test patch:
diff --git a/tests/models/test_index_record.py b/tests/models/test_index_record.py
new file mode 100644
--- /dev/null
+++ b/tests/models/test_index_record.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+from __future__ import absolute_import, division, print_function, unicode_literals
+from logging import getLogger
+from math import floor
+
+from conda.models.index_record import IndexRecord
+
+log = getLogger(__name__)
+
+
+def test_index_record_timestamp():
+    # regression test for #6096
+    ts = 1507565728.999
+    rec = IndexRecord(
+        name='test-package',
+        version='1.2.3',
+        build='2',
+        build_number=2,
+        timestamp=ts
+    )
+    assert rec.timestamp == floor(ts)
+    assert rec.dump()['timestamp'] == floor(ts)