This file is indexed.

/usr/lib/python2.7/dist-packages/chaco/scales/time_scale.py is in python-chaco 4.5.0-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
"""
A scale for time and calendar intervals.
"""

from math import floor

from scales import AbstractScale, ScaleSystem, frange, heckbert_interval
from formatters import TimeFormatter
from safetime import (safe_fromtimestamp, datetime, timedelta, EPOCH,
                      MINYEAR, MAXYEAR)

# Labels for date and time units.
datetime_scale = ["microsecond", "second", "minute", "hour",
                  "day", "month", "year"]
datetime_zeros = zip(datetime_scale, [0, 0, 0, 0, 1, 1, 1])


__all__ = ["TimeScale", "CalendarScaleSystem", "HMSScales", "MDYScales",
           "trange", "tfrac", "td_to_sec", "dt_to_sec"]


def td_to_sec(td):
    """ Returns the floating point number of seconds in a timedelta object.
    """
    return td.days * 24 * 3600 + td.seconds + td.microseconds * 1e-6


def dt_to_sec(t):
    """ Returns the floating point number of seconds since the UNIX epoch
    corresponding to the given datetime instance.

    This value is more accurate than mktime(t.timetuple()) because it
    preserves milliseconds.
    """
    return td_to_sec(t - EPOCH)


def tfrac(t, **time_unit):
    """ Performs a calendar-aware split of a time into (aligned_time, frac)
    over an interval that is a multiple of one of the following time units:

        "microseconds" "milliseconds", "seconds", "minutes", "hours", "days", "years"

    Settings of milliseconds..hours are truncated towards 0, days are counted
    from January 1st of their respective year, and years are counted from 1 AD.
    This may lead to unexpected rounding if multi-day or multi-year intervals
    are used.

    For example:

    If it is currently 4:15pm on January 3rd, 2007, calling:
    ``tfrac(time.time(), hours=3)``
    returns the UNIX number of seconds corresponding to
    "January 3rd, 2007 15:00:00"
    as the aligned time, and the number of seconds in 1 hour and 15 minutes as
    the fractional part.

    Parameters
    ==========
    t : float
        time in seconds
    ``**time_unit`` : dict
        a single (interval=value) item

    Returns
    =======
    A tuple: (aligned time as UNIX time, remainder in seconds)
    """
    unit, period = time_unit.items()[0]
    if unit == "milliseconds":
        unit = "microsecond"
        period *= 1000
    else:
        unit = unit[:-1]  # strip off the 's'

    # Find the nearest round date
    dt = safe_fromtimestamp(t)
    amt = getattr(dt, unit)
    ndx = datetime_scale.index(unit)
    closest_multiple = int(floor(amt / period) * period)
    if closest_multiple == 0 and unit in ("day", "year"):
        # TODO: this isn't really quite right for intervals of days > 1...
        closest_multiple = 1
    whole = dt.replace(**{unit: closest_multiple})
    whole = whole.replace(**dict(datetime_zeros[:ndx]))
    frac = td_to_sec(dt - whole)

    return dt_to_sec(whole), frac

def _advance_month(dt, months):
    """ Advance a datetime object by a given number of months.
    """
    new_month = dt.month + months
    years, extra_months = divmod(new_month-1, 12)
    new_month = extra_months + 1
    return dt.replace(year=dt.year+years, month=new_month)

def trange_months(start, end, months):
    """ Create a range of timestamps separated by a given number of months.

    The start of the iteration is always aligned to Jan 1 2000.
    """
    dt_start = safe_fromtimestamp(start)
    dt_end = safe_fromtimestamp(end)
    dmonths = (12 * (dt_start.year - 2000) + dt_start.month - 1) % months
    dt = _advance_month(dt_start.replace(day=1, hour=0, minute=0, second=0,
        microsecond=0), -dmonths)
    while dt < dt_start:
        dt = _advance_month(dt, months)
    timestamps = []
    while dt <= dt_end:
        timestamps.append(dt_to_sec(dt))
        dt = _advance_month(dt, months)
    return timestamps

def _advance_years(dt, years):
    """ Advance a datetime object by a given number of years.
    """
    return dt.replace(year=dt.year+years)

def trange_years(start, end, years):
    """ Create a range of timestamps separated by a given number of years.

    The start of the iteration is aligned to Jan 1 2000.
    """
    dt_start = safe_fromtimestamp(start)
    dt_end = safe_fromtimestamp(end)
    dyears = (dt_start.year - 2000) % years
    if dyears < 0:
        dyears += years
    dt = datetime(dt_start.year-dyears, 1, 1, 0, 0, 0, 0)
    while dt < dt_start:
        dt = _advance_years(dt, years)
    timestamps = []
    while dt <= dt_end:
        timestamps.append(dt_to_sec(dt))
        dt = _advance_years(dt, years)
    return timestamps

def trange(start, end, **time_unit):
    """ Like range(), but for times, and with "natural" alignment depending on
    the interval.

    For example::

        t_range(time.time(), time.time()+76*3600, days=2)
        t_range(start, end, months=3)

    Parameters
    ==========
    start, end : float
        Time in seconds.  *end* must be later than *start*.
    time_unit : a single (key, int_value) pair
        The units to use. *key* must be in the list: "milliseconds", "seconds",
        "minutes", "hours", "days", "months", "years".  Months are treated as
        30 days, and years are treated as 365 days.

    Returns
    =======
    A list of times that nicely span the interval, or an empty list if *start*
    and *end* fall within the same interval.
    """
    if len(time_unit) > 1:
        raise ValueError("trange() only takes one keyword argument, got %d" % len(time_unit))

    # Months and years are non-uniform, so we special-case them.
    unit, value = time_unit.items()[0]
    if unit == 'months':
        return trange_months(start, end, value)
    elif unit == 'years':
        return trange_years(start, end, value)

    # Express start and end ticks as (date, frac) where date is calendar-aligned
    # with the interval in time_unit.
    start_whole, start_frac = tfrac(start, **time_unit)
    end_whole, end_frac = tfrac(end, **time_unit)

    # Handle some corner-cases
    if start_whole == end_whole:
        return []

    if start_frac < 1e-6:
        first_tick_ndx = 0
    else:
        first_tick_ndx = 1

    # Convert months and years into days
    time_unit["days"] = time_unit.setdefault("days", 0) + \
                            365 * time_unit.pop("years", 0) + \
                            30 * time_unit.pop("months", 0)
    delta = td_to_sec(timedelta(**time_unit))
    count = (end_whole - start_whole) / delta

    ticks = [start_whole + i*delta for i in range(int(round(count))+1)]
    return ticks[first_tick_ndx:]


class TimeScale(AbstractScale):
    """ A scale based on time intervals and calendar dates. The valid
    intervals are:

    Natural time:
        microseconds, milliseconds, seconds, minutes, hours, days, years
    Calendar time:
        day_of_month, month_of_year

    For calendar times, a list of hours/days/months is set.
    By default, intervals are aligned to January 1st.
    """

    # This is used to compute an approximate resolution for each type of scale.
    SECS_PER_UNIT = {"microseconds": 1e-6,
                     "milliseconds": 1e-3,
                     "seconds": 1,
                     "minutes": 60,
                     "hours": 3600,
                     "days": 24*3600,
                     "day_of_month": 30*24*3600,
                     "month_of_year": 365*24*3600,
                     "years": 365*24*3600,
                     }

    CALENDAR_UNITS = ("day_of_month", "month_of_year")

    def __init__(self, **kw_interval):
        """ Defines the time period that this scale uses.
        """
        self.formatter = kw_interval.pop("formatter", TimeFormatter())
        unit, val = kw_interval.items()[0]
        self.unit = unit
        if "_of_" in unit:
            # Calendar time interval - divide by the number of ticks per larger
            # unit of time to get an average resolution
            if type(val) in (int, float):
                val = [val]
            self.vals = val
            self.resolution = self.SECS_PER_UNIT[unit] / float(len(val))
        else:
            self.val = val
            self.resolution = val * self.SECS_PER_UNIT[unit]
        return

    def num_ticks(self, start, end, desired_ticks=None):
        """ Returns an approximate number of ticks that this scale
        produces for the given interval.

        Implements AbstractScale.
        """
        # This is only approximate, but puts us in the ballpark
        if self.unit in ("milliseconds", "microseconds"):
            ticks = self.ticks(start, end, desired_ticks=8)
            coarsest_scale_count = (end - start) / (500 * self.SECS_PER_UNIT[self.unit])
            return max(len(ticks), coarsest_scale_count)
        else:
            return (end - start) / self.resolution

    def ticks(self, start, end, desired_ticks=None):
        """ Returns the set of "nice" positions on this scale that enclose and
        fall inside the interval (*start*,*end*).

        Implements AbstractScale. The *start* and *end* parameters are
        floating-point seconds since the epoch.
        """

        if self.unit in self.CALENDAR_UNITS:
            return self.cal_ticks(start, end)
        elif self.unit in ("milliseconds", "microseconds"):
            if start == end or (end - start) < self.SECS_PER_UNIT[self.unit]:
                return [start]
            secs_per_unit = self.SECS_PER_UNIT[self.unit]
            start /= secs_per_unit
            end /= secs_per_unit
            if desired_ticks is None:
                min, max, delta = heckbert_interval(start, end, enclose=True)
            else:
                min, max, delta = heckbert_interval(start, end, desired_ticks,
                                                    enclose=True)
            min *= secs_per_unit
            max *= secs_per_unit
            delta *= secs_per_unit
            return frange(min, max, delta)
        else:
            return trange(start, end, **{self.unit: self.val})

    def cal_ticks(self, start, end):
        """ ticks() method for calendar-based intervals """

        try:
            start = datetime.fromtimestamp(start)
        except ValueError:
            start = datetime(MINYEAR, 1, 1, 0, 0, 0)
        try:
            end = datetime.fromtimestamp(end)
        except ValueError:
            end = datetime(MAXYEAR, 1, 1, 0, 0, 0)

        if self.unit == "day_of_month":
            s = start.year + 1/12.0 * start.month
            e = end.year + 1/12.0 * end.month
            num_months = int(round((e - s) * 12)) + 1   # add 1 for fencepost
            start_year = start.year
            start_month = start.month
            ym = [divmod(i, 12)
                  for i in range(start_month-1, start_month-1+num_months)]
            months = [start.replace(year=start_year+y, month=m+1, day=1)
                      for (y,m) in ym]
            ticks = [dt.replace(day=i) for dt in months for i in self.vals]

        elif self.unit == "month_of_year":
            years = [start.replace(year=newyear, day=1)
                     for newyear in range(start.year, end.year+1)]
            ticks = [dt.replace(month=i, day=1)
                     for dt in years for i in self.vals]

        else:
            raise ValueError("Unknown calendar unit '%s'" % self.unit)

        if len(ticks) > 0:
            # Find the first and last index in all_ticks that falls
            # within (start,end)
            for start_ndx in range(len(ticks)):
                if ticks[start_ndx] >= start:
                    break
            for end_ndx in range(len(ticks)-1, 0, -1):
                if ticks[end_ndx] <= end:
                    break
            ticks = ticks[start_ndx : end_ndx+1]

        return map(dt_to_sec, ticks)

    def labels(self, start, end, numlabels=None, char_width=None):
        """ Returns a series of ticks and corresponding strings for labels
        that fall inside the interval (*start*,*end*).

        Overrides AbstractScale.
        """
        ticks = self.ticks(start, end, numlabels)
        labels = self.formatter.format(ticks, numlabels, char_width, ticker=self)
        return zip(ticks, labels)

    def label_width(self, start, end, numlabels=None, char_width=None):
        """ Returns an estimate of total number of characters used by the
        the labels that this scale will produce for the given set of
        inputs, as well as the number of labels.

        Overrides AbstractScale.
        """
        return self.formatter.estimate_width(start, end, numlabels, char_width,
                                             ticker=self)


# Declare some default scale systems

# Default time scale for hours, minutes, seconds, and milliseconds.
HMSScales = [TimeScale(microseconds=1), TimeScale(milliseconds=1)] + \
            [TimeScale(seconds=dt) for dt in (1, 5, 15, 30)] + \
            [TimeScale(minutes=dt) for dt in (1, 5, 15, 30)] + \
            [TimeScale(hours=dt) for dt in (1, 2, 3, 4, 6, 12, 24)]

# Default time scale for months, days, and years.
MDYScales = [TimeScale(day_of_month=range(1,31,3)),
             TimeScale(day_of_month=(1,8,15,22)),
             TimeScale(day_of_month=(1,15)),
             TimeScale(month_of_year=range(1,13)),
             TimeScale(month_of_year=range(1,13,3)),
             TimeScale(month_of_year=(1,7)),
             TimeScale(month_of_year=(1,)),] + \
            [TimeScale(years=dt) for dt in (1,2,5,10)]

class CalendarScaleSystem(ScaleSystem):
    """ Scale system for calendars.

    This class has a pre-defined set of nice "time points" to use for ticking
    and labelling.
    """

    def __init__(self, *scales, **kw):
        """ Creates a new CalendarScaleSystem.

        If scales are not provided, then it defaults to HMSScales and MDYScales.
        """
        if len(scales) == 0:
            scales = HMSScales + MDYScales
        super(CalendarScaleSystem, self).__init__(*scales, **kw)

    def _get_scale(self, start, end, numticks):
        if len(self.scales) == 0:
            if self.default_scale is not None:
                closest_scale = self.default_scale
            else:
                raise ValueError("CalendarScaleSystem has not be configured "
                                 "with any scales.")
        elif end - start < 1e-6 or end - start > 1e5 * 365 * 24 * 3600:
            closest_scale = self.default_scale
        else:
            closest_scale = self._get_scale_np(start, end, numticks)

        return closest_scale