Skip to content
1 change: 1 addition & 0 deletions docs/user-guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ stop_on_restart = false
date_format = %Y.%m.%d
time_format = %H:%M:%S%z
week_start = monday
day_start_hour = 0
log_current = false
pager = true
report_current = false
Expand Down
4 changes: 3 additions & 1 deletion watson/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
get_rename_old_name,
get_rename_types,
)
from .frames import Frame
from .frames import Frame, shifted_from
from .utils import (
apply_weekday_offset,
build_csv,
Expand Down Expand Up @@ -1069,6 +1069,8 @@ def log(watson, current, reverse, from_, to, projects, tags, ignore_projects,
if _ is not None):
from_ = start_time

from_ = shifted_from(from_)

if from_ > to:
raise click.ClickException("'from' must be anterior to 'to'")

Expand Down
36 changes: 33 additions & 3 deletions watson/frames.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,36 @@
HEADERS = ('start', 'stop', 'project', 'id', 'tags', 'updated_at')


hour_shift = 0


def set_hour_shift(hour):
global hour_shift
hour_shift = hour


def shifted_floor(time, timeframe):
time = time.shift(hours=-hour_shift)
time = time.floor(timeframe)
time = time.shift(hours=hour_shift)
return time


def shifted_ceil(time, timeframe):
time = time.shift(hours=-hour_shift)
time = time.ceil(timeframe)
time = time.shift(hours=hour_shift)
return time


def shifted_from(from_):
if from_ == from_.floor('day'):
if arrow.now().hour < hour_shift:
from_ = from_.shift(days=-1)
from_ = from_.shift(hours=hour_shift)
return from_


class Frame(namedtuple('Frame', HEADERS)):
def __new__(cls, start, stop, project, id, tags=None, updated_at=None,):
try:
Expand Down Expand Up @@ -45,7 +75,7 @@ def dump(self):

@property
def day(self):
return self.start.floor('day')
return shifted_floor(self.start, 'day')

def __lt__(self, other):
return self.start < other.start
Expand All @@ -63,8 +93,8 @@ def __gte__(self, other):
class Span(object):
def __init__(self, start, stop, timeframe='day'):
self.timeframe = timeframe
self.start = start.floor(self.timeframe)
self.stop = stop.ceil(self.timeframe)
self.start = shifted_floor(start, self.timeframe)
self.stop = shifted_ceil(stop, self.timeframe)

def overlaps(self, frame):
return frame.start <= self.stop and frame.stop >= self.start
Expand Down
6 changes: 5 additions & 1 deletion watson/watson.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import subprocess

from .config import ConfigParser
from .frames import Frames
from .frames import Frames, set_hour_shift, shifted_from
from .utils import deduplicate, make_json_writer, safe_save, sorted_groupby
from .version import version as __version__ # noqa

Expand Down Expand Up @@ -66,6 +66,8 @@ def __init__(self, **kwargs):
if 'last_sync' in kwargs:
self.last_sync = kwargs['last_sync']

set_hour_shift(self.config.getint('options', 'day_start_hour', 0))

def _load_json_file(self, filename, type=dict):
"""
Return the content of the the given JSON file.
Expand Down Expand Up @@ -533,6 +535,8 @@ def report(self, from_, to, current=None, projects=None, tags=None,
if _ is not None):
from_ = start_time

from_ = shifted_from(from_)

if not self._validate_report_options(projects, ignore_projects):
raise WatsonError(
"given projects can't be ignored at the same time")
Expand Down