コード例 #1
0
ファイル: dt.py プロジェクト: maniacs-oss/timetagger
def time2str(t, utc_offset=None):
    """Convert a time int into a textual representation. If utc_offset is None, the
    representation is in local time. Otherwise the given offset (in hours) is
    used. Use utc_offset=0 for UTC. In all cases the zone is explicit in the result.
    """
    t = to_time_int(t)
    if this_is_js():  # pragma: no cover
        if utc_offset is None:
            utc_offset = -(Date(t * 1000).getTimezoneOffset() // 60)
        t += utc_offset * 3600
        s = Date(t * 1000).toISOString()
        s = s.split(".")[0]
        if utc_offset == 0:
            s += "Z"
        else:
            s += f"{utc_offset:+03.0f}"
    else:  # py
        import datetime

        if utc_offset is None:
            utc_offset = (
                datetime.datetime.fromtimestamp(t) -
                datetime.datetime.utcfromtimestamp(t)).total_seconds() // 3600
        tz = datetime.timezone(datetime.timedelta(hours=utc_offset))
        dt = datetime.datetime.fromtimestamp(t, tz)
        if utc_offset == 0:
            s = dt.strftime("%Y-%m-%dT%H:%M:%SZ")
        else:
            s = dt.strftime("%Y-%m-%dT%H:%M:%S%z")
    return s
コード例 #2
0
ファイル: dt.py プロジェクト: maniacs-oss/timetagger
def get_timezone_info(t):
    d = Date(t * 1000)
    d_winter = Date(d.getFullYear(), 0, 1)
    d_summer = Date(d.getFullYear(), 6, 1)
    #
    offset = -d.getTimezoneOffset() / 60
    offset_winter = -d_winter.getTimezoneOffset() / 60
    offset_summer = -d_summer.getTimezoneOffset() / 60
    return offset, offset_winter, offset_summer
コード例 #3
0
ファイル: dt.py プロジェクト: maniacs-oss/timetagger
def get_year_month_day(t):
    if this_is_js():
        d = Date(t * 1000)
        return d.getFullYear(), d.getMonth() + 1, d.getDate()
    else:
        import datetime

        dt = datetime.datetime.fromtimestamp(t)
        return dt.year, dt.month, dt.day
コード例 #4
0
ファイル: dt.py プロジェクト: maniacs-oss/timetagger
def now():
    """Get the current time in seconds, as a float."""
    if this_is_js():
        return Date().getTime() / 1000
    else:
        import time

        return time.time()
コード例 #5
0
ファイル: dt.py プロジェクト: maniacs-oss/timetagger
def to_time_int(t):
    """Get a time (in int seconds since epoch), given float/str input.
    String inputs can be:

    * 'now': get current time.
    * E.g. '2018-04-24 11:23:00' for a local time (except in Safari :/).
    * E.g. '2018-04-24 11:23:00Z' for a time in UTC.
    * E.g. '2018-04-24 11:23:00+0200' for AMS summertime in this case.

    In the above, one can use a 'T' instead of a space between data and time
    to comply with ISO 8601.
    """
    if this_is_js():
        if isinstance(t, Date):
            t = t.getTime() / 1000
    if isinstance(t, str):
        t = t.strip()
        if this_is_js():  # pragma: no cover
            if t.lower() == "now":
                t = Date().getTime() / 1000
            else:
                if t.count(" ") == 1 and t[10] == " ":
                    t = t.replace(" ", "T")  # Otherwise Safari wont take it
                t = Date(t).getTime() / 1000  # Let browser handle date parsing
        else:  # py
            import datetime

            t = t.replace("T", " ")
            if t.lower() == "now":
                t = datetime.datetime.now().timestamp()
            elif t.endswith("Z") or t[-5] in "-+":  # ISO 8601
                t = (t[:-1] + "+0000") if t.endswith("Z") else t
                t = datetime.datetime.strptime(
                    t, "%Y-%m-%d %H:%M:%S%z").timestamp()
            else:
                t = datetime.datetime.strptime(
                    t, "%Y-%m-%d %H:%M:%S").timestamp()
    if not isinstance(t, (int, float)):
        raise RuntimeError(f"Time must be a number, not {t!r}")
    return int(t)
コード例 #6
0
ファイル: dt.py プロジェクト: maniacs-oss/timetagger
def add(t, delta):
    """Add a delta to the given date. Delta can be an int (number of seconds),
    or a delta string like '4h', "21s", "2M" or "2Y". Works correctly for months
    and keeps leap years into account.
    """

    if isinstance(delta, (float, int)):
        delta = str(delta) + "s"

    deltaName = delta[-1]
    deltaFactor = float(delta[:-1])
    if isNaN(deltaFactor):
        raise RuntimeError(f"Cannot create delta from {delta!r}")

    d = Date(t * 1000)
    tup = (
        d.getFullYear(),
        d.getMonth(),
        d.getDate(),
        d.getHours(),
        d.getMinutes(),
        d.getSeconds(),
    )
    if deltaName == "s":
        tup[5] += deltaFactor
    elif deltaName == "m":
        tup[4] += deltaFactor
    elif deltaName == "h":
        tup[3] += deltaFactor
    elif deltaName == "D":
        tup[2] += deltaFactor
    elif deltaName == "W":
        tup[2] += deltaFactor * 7
    elif deltaName == "M":
        tup[1] += deltaFactor
    elif deltaName == "Y":
        tup[0] += deltaFactor
    else:
        raise RuntimeError("Invalid datetime delta: " + delta)

    return Date(tup[0], tup[1], tup[2], tup[3], tup[4],
                tup[5]).getTime() / 1000
コード例 #7
0
ファイル: dt.py プロジェクト: maniacs-oss/timetagger
def get_weeknumber(t):
    """Get the ISO 8601 week number."""
    # From https://weeknumber.net/how-to/javascript
    date = Date(t * 1000)  # noqa
    RawJS("""
    date.setHours(0, 0, 0, 0);
    // Thursday in current week decides the year.
    date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
    // January 4 is always in week 1.
    var week1 = new Date(date.getFullYear(), 0, 4);
    // Adjust to Thursday in week 1 and count number of weeks from date to week1.
    var res = 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000
                               - 3 + (week1.getDay() + 6) % 7) / 7);
    """)
    return res  # noqa
コード例 #8
0
ファイル: client_code.py プロジェクト: carehart/mypaas
    def draw(self):
        PSCRIPT_OVERLOAD = False  # noqa

        ctx = self.canvas.getContext("2d")

        # Prepare hidpi mode for canvas  (flush state just in case)
        for i in range(4):
            ctx.restore()
        ctx.save()
        ctx.scale(self.pixel_ratio, self.pixel_ratio)

        # Flip y-axis
        ctx.scale(1, -1)
        ctx.translate(0, -self.height)

        # Clear bg
        ctx.clearRect(0, 0, self.width, self.height)

        # Determine drawing area
        x0 = 45
        y0 = 35
        width = self.width - x0 - 15
        height = self.height - y0 - 5

        data = data_per_db[self.dbname]
        if len(data) == 0:
            return

        # Get bounding box
        t1 = data[0].time_start
        t2 = data[-1].time_stop
        mi, ma = self._get_min_max()
        if ma <= mi:
            return
        hscale = width / (t2 - t1)
        vscale = height / (ma - mi)

        unix_from_utc_tuple = Date.UTC  # avoid triggering new
        utc = get_hash_info().get("utc", False)
        xticks = {}

        # Prepare x ticks for hours (one hour is the smallest granularity)
        hourly_tick_units = (1, 3600), (2, 7200), (6, 21600)
        min_tick_dist = 60
        for nhours, tick_unit in hourly_tick_units:
            if tick_unit * hscale >= min_tick_dist:
                break
        else:
            tick_unit = 0
        #
        if tick_unit > 0:
            d = Date(t1 * 1000)
            if utc:
                tup = [
                    d.getUTCFullYear(),
                    d.getUTCMonth(),
                    d.getUTCDate(),
                    d.getUTCHours(),
                ]
                tup[-1] = nhours * int(tup[-1] / nhours)
                t = unix_from_utc_tuple(tup[0], tup[1], tup[2], tup[3]) / 1000
            else:
                tup = [
                    d.getFullYear(),
                    d.getMonth(),
                    d.getDate(),
                    d.getHours()
                ]
                tup[-1] = nhours * int(tup[-1] / nhours)
                t = Date(tup[0], tup[1], tup[2], tup[3]).getTime() / 1000
            while t <= t2:
                if t >= t1:
                    d = Date(t * 1000)
                    if utc:
                        xticks[
                            t] = f"{d.getUTCHours():02i}:{d.getUTCMinutes():02i}"
                    else:
                        xticks[t] = f"{d.getHours():02i}:{d.getMinutes():02i}"
                t += tick_unit

        # Prepare x ticks for days/months
        day_tick_units = (2, 1), (2, 2), (2, 5), (1, 1), (1, 2), (1, 3), (0,
                                                                          365)
        min_tick_dist = 60
        for dindex, nsomething in day_tick_units:
            tick_unit = nsomething * [365 * 86400, 30 * 86400, 86400][dindex]
            if tick_unit * hscale >= min_tick_dist:
                break
        else:
            tick_unit = nsomething = 0
        #
        n_date_ticks = 0
        if nsomething > 0:
            d = Date(t1 * 1000)
            if utc:
                tup = [d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()]
                tup[dindex] = nsomething * int(tup[dindex] / nsomething)
                t = unix_from_utc_tuple(tup[0], tup[1], tup[2]) / 1000
            else:
                tup = [d.getFullYear(), d.getMonth(), d.getDate()]
                tup[dindex] = nsomething * int(tup[dindex] / nsomething)
                t = Date(tup[0], tup[1], tup[2]).getTime() / 1000
            while t <= t2:
                if t >= t1:
                    n_date_ticks += 1
                    d = Date(t * 1000)
                    if utc:
                        dd = f"{d.getUTCDate():02i}"
                        mm = f"{d.getUTCMonth()+1:02i}"
                        yy = f"{d.getUTCFullYear()}"
                        xticks[t] = f"{dd}-{mm}-{yy}"
                    else:
                        dd = f"{d.getDate():02i}"
                        mm = f"{d.getMonth()+1:02i}"
                        yy = f"{d.getFullYear()}"
                        xticks[t] = f"{dd}-{mm}-{yy}"
                tup[dindex] += nsomething
                if utc:
                    t = unix_from_utc_tuple(tup[0], tup[1], tup[2]) / 1000
                else:
                    t = Date(tup[0], tup[1], tup[2]).getTime() / 1000
        #
        extra_x_tick = ""
        if n_date_ticks < 2:
            xtickskeys = xticks.keys()
            if len(xtickskeys) > 0 and hscale * (xtickskeys[0] - t1) < 30:
                xticks.pop(xtickskeys[0])
            d = Date(t1 * 1000)
            if utc:
                extra_x_tick = (
                    f"{d.getUTCFullYear()}-{d.getUTCMonth()+1:02i}-{d.getUTCDate():02i}"
                )
            else:
                extra_x_tick = (
                    f"{d.getFullYear()}-{d.getMonth()+1:02i}-{d.getDate():02i}"
                )

        # Prepare y ticks
        yticks = self._get_ticks(vscale, mi, ma, 25)  # text -> value

        # Prepare drawing
        ctx.lineWidth = 1

        # Draw grid lines
        ctx.strokeStyle = "rgba(128, 128, 128, 0.3)"
        ctx.beginPath()
        for v, text in yticks.items():
            y = y0 + (float(v) - mi) * vscale
            ctx.moveTo(x0, y)
            ctx.lineTo(x0 + width, y)
        ctx.stroke()

        # Draw x ticks
        ctx.strokeStyle = text_color
        ctx.fillStyle = text_color
        ctx.textAlign = "center"
        ctx.textBaseline = "top"  # middle
        ctx.beginPath()
        for t, text in xticks.items():
            x = x0 + (float(t) - t1) * hscale
            ctx.moveTo(x, y0)
            ctx.lineTo(x, y0 - 4)
        ctx.stroke()
        for t, text in xticks.items():
            x = x0 + (float(t) - t1) * hscale
            angle = 0  # -0.15 * Math.PI
            x = min(x, x0 + width - 15)
            self._draw_text(ctx, text, x, y0 - 10, angle)
        if extra_x_tick:
            ctx.textAlign = "left"
            ctx.textBaseline = "bottom"
            self._draw_text(ctx, extra_x_tick, 0, 0)

        # Draw y ticks
        ctx.textAlign = "right"
        ctx.textBaseline = "middle"
        ctx.beginPath()
        for v, text in yticks.items():
            y = y0 + (float(v) - mi) * vscale
            ctx.moveTo(x0 - 4, y)
            ctx.lineTo(x0, y)
        ctx.stroke()
        for v, text in yticks.items():
            y = y0 + (float(v) - mi) * vscale
            self._draw_text(ctx, text, x0 - 8, y)

        # Draw axis
        ctx.strokeStyle = text_color
        ctx.beginPath()
        ctx.moveTo(x0, y0)
        ctx.lineTo(x0 + width, y0)
        ctx.moveTo(x0, y0)
        ctx.lineTo(x0, y0 + height)
        ctx.stroke()

        # Draw content
        self._draw_content(ctx, mi, ma, t1, t2, x0, y0, hscale, vscale)

        # Draw local / UTC
        ctx.fillStyle = "rgba(128, 128, 128, 0.5)"
        ctx.textAlign = "right"
        ctx.textBaseline = "bottom"
        self._draw_text(ctx, "UTC" if utc else "Local time", self.width, 0)
コード例 #9
0
ファイル: stores.py プロジェクト: maniacs-oss/timetagger
    def _create_one_year_of_data(self, y):
        PSCRIPT_OVERLOAD = False  # noqa

        now = dt.now()
        nowyear, nowmonth, nowday = dt.get_year_month_day(dt.now())
        sy = str(y)
        rr = []

        rpd = list(range(3, 9))  # number of records per day

        for m in range(1, 13):
            sm = f"{m:02}"

            # For each month, we select tags from the tag groups,
            # we may not have all, and we may have duplicates.
            tags = []
            for i in range(1):
                tag_group = self._tag_groups1[int(random() *
                                                  len(self._tag_groups1))]
                for tag in tag_group:
                    tags.append(tag)
            for i in range(2):
                tag_group = self._tag_groups2[int(random() *
                                                  len(self._tag_groups2))]
                for tag in tag_group:
                    tags.append(tag)

            for d in range(1, 32):
                sd = f"{d:02}"

                # Don't make records in the current future
                if y > nowyear:
                    continue
                elif y == nowyear and m > nowmonth:
                    continue
                elif y == nowyear and m == nowmonth and d > nowday:
                    continue

                # Is this date ok?
                if this_is_js():  # pragma: no cover
                    weekday = Date(f"{sy}-{sm}-{sd}").getDay()  # 0 is Sunday
                    # Put some predictable stuff on today, whatever day it is.
                    if y == nowyear and m == nowmonth and d == nowday:
                        for start, stop, tag in [
                            ("08:51", "09:11", "#admin"),
                            ("09:11", "10:27", "#client1 #meeting"),
                            ("10:29", "11:52", "#client1 #code"),
                            ("12:51", "13:32", "#client1 #code"),
                            ("13:32", "14:28", "#client2 #meeting"),
                            ("14:34", "16:11", "#client2 #design"),
                        ]:
                            t1 = dt.to_time_int(f"{sy}-{sm}-{sd}T{start}:00")
                            t2 = dt.to_time_int(f"{sy}-{sm}-{sd}T{stop}:00")
                            if t2 > now:
                                continue
                            works = [
                                "work", "stuff", "things", "administration"
                            ]
                            ds = "Did some " + works[int(
                                random() * len(works))]
                            ds += " " + tag
                            record = self.records.create(t1, t2, ds)
                            record.st = now
                            rr.append(record)
                        continue
                    elif weekday not in (1, 2, 3, 4, 5):
                        continue  # no NaN (invalid date) or weekends
                else:
                    if d > 28:
                        continue  # on Python, during tests

                t1 = dt.to_time_int(f"{sy}-{sm}-{sd}T08:00:00")

                for h in range(rpd[int(random() * len(rpd))]):
                    tag = tags[int(random() * len(tags))]
                    t1 += [0, 10 * 60,
                           20 * 60][int(random() * 3)]  # pause in secs
                    t2 = t1 + 60 * (60 + int(random() * 120))  # 1-3 hours
                    if t2 > now - 60:
                        break
                    works = ["work", "stuff", "things", "administration"]
                    ds = "Did some " + works[int(random() * len(works))]
                    ds += " " + tag
                    record = self.records.create(t1, t2, ds)
                    record.st = now
                    t1 = t2  # next
                    rr.append(record)

        # Store records
        self.records._put_received(*rr)
コード例 #10
0
ファイル: dt.py プロジェクト: maniacs-oss/timetagger
def is_first_day_of_week(t):
    d = Date(t * 1000)
    return d.getDay() == 1  # Monday
コード例 #11
0
ファイル: dt.py プロジェクト: maniacs-oss/timetagger
def get_weekday_longname(t):
    d = Date(t * 1000)
    return DAYS_LONG[d.getDay()]
コード例 #12
0
ファイル: dt.py プロジェクト: maniacs-oss/timetagger
def get_weekday_shortname(t):
    d = Date(t * 1000)
    return DAYS_SHORT[
        d.getDay()]  # getDay starts at zero, which represents Sunday
コード例 #13
0
ファイル: dt.py プロジェクト: maniacs-oss/timetagger
def get_month_shortname(t):
    d = Date(t * 1000)
    return MONTHS_SHORT[d.getMonth()]
コード例 #14
0
ファイル: dt.py プロジェクト: maniacs-oss/timetagger
def floor(t, res):
    """Round the given date down to the nearest smaller resolution step."""
    resName = res[-1]
    resFactor = float(res[:-1])

    d = Date(t * 1000)
    tup = (
        d.getFullYear(),
        d.getMonth(),
        d.getDate(),
        d.getHours(),
        d.getMinutes(),
        d.getSeconds(),
    )
    if resName == "s":
        tup[-1] = int(tup[-1] / resFactor) * resFactor
    elif resName == "m":
        tup = tup[:5]
        tup[-1] = int(tup[-1] / resFactor) * resFactor
    elif resName == "h":
        tup = tup[:4]
        tup[-1] = int(tup[-1] / resFactor) * resFactor
    elif resName == "D":
        tup = tup[:3]
        tup[-1] = int(
            (tup[-1] - 1) / resFactor) * resFactor + 1  # days are 1-based
    elif resName == "W":
        d.setHours(0, 0, 0, 0)
        daysoff = (d.getDay() + 6) % 7  # Align to a monday
        d = Date(d.getTime() - 86_400_000 * daysoff)
        tup = d.getFullYear(), d.getMonth(), d.getDate()
    elif resName == "M":
        tup = tup[:2]  # Note that it is zero-based (jan is zero)
        tup[-1] = int(tup[-1] / resFactor) * resFactor
        tup.extend([1])
    elif resName == "Y":
        tup = tup[:1]
        tup[-1] = int(tup[-1] / resFactor) * resFactor
        tup.extend([0, 1])
    else:
        raise RuntimeError("Invalid resolution: " + res)

    while len(tup) < 6:
        tup.append(0)
    return (Date(tup[0], tup[1], tup[2], tup[3], tup[4], tup[5]).getTime() /
            1000)  # cant do *tup