Fix many issues in calculation and improve output formatting
This commit is contained in:
parent
50144cfc99
commit
6572f95389
3 changed files with 86 additions and 30 deletions
|
@ -3,7 +3,23 @@ import { Duration } from 'dayjs/plugin/duration.js';
|
|||
|
||||
export const formatTimestamp = (timestamp: Dayjs): string => timestamp.format('YYYY-MM-DD HH:mm');
|
||||
|
||||
export const formatDuration = (unLogged: Duration): string => unLogged.format('HH[ hours and ]mm [minutes]');
|
||||
export const formatTime = (time: Dayjs): string => time.format('HH:mm');
|
||||
|
||||
export const formatDuration = (duration: Duration, short?: boolean): string => {
|
||||
if (duration.hours() === 0 && duration.minutes() === 0) {
|
||||
return 'none';
|
||||
}
|
||||
|
||||
const formatString = short
|
||||
? 'HH:mm'
|
||||
: duration.hours() > 0 && duration.minutes() > 0
|
||||
? `H [hour${duration.hours() > 1 ? 's' : ''} and] m [minute${duration.minutes() > 1 ? 's' : ''}]`
|
||||
: duration.hours() > 0
|
||||
? `H [hour${duration.hours() > 1 ? 's' : ''}]`
|
||||
: `m [minute${duration.minutes() > 1 ? 's' : ''}]`;
|
||||
|
||||
return duration.format(formatString);
|
||||
};
|
||||
|
||||
export const getHoursRoundedStr = (duration: Duration) =>
|
||||
`(${getHoursRounded(duration)} as hours rounded to next even 15 minutes)`;
|
||||
|
|
92
src/main.ts
92
src/main.ts
|
@ -1,10 +1,12 @@
|
|||
import chalk from 'chalk';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import * as readline from 'readline/promises';
|
||||
import { formatDuration, formatTimestamp, getHoursRoundedStr } from './format.js';
|
||||
import { Duration } from 'dayjs/plugin/duration';
|
||||
import { formatDuration, formatTime, formatTimestamp, getHoursRoundedStr } from './format.js';
|
||||
import duration, { Duration } from 'dayjs/plugin/duration.js';
|
||||
import { parseDuration, parseTimestamp } from './parse.js';
|
||||
|
||||
dayjs.extend(duration);
|
||||
|
||||
const { log, error } = console;
|
||||
const defaultStartTime = '08:00';
|
||||
const lunchBreakDuration = dayjs.duration({ minutes: 30 });
|
||||
|
@ -16,14 +18,18 @@ const main = async () => {
|
|||
output: process.stdout,
|
||||
});
|
||||
|
||||
let started: Dayjs | undefined = undefined;
|
||||
let startedAt: Dayjs | undefined = undefined;
|
||||
const now = dayjs();
|
||||
|
||||
try {
|
||||
// Get work day duration
|
||||
let workDayDuration: Duration | undefined = undefined;
|
||||
|
||||
const durationAnswer = await rl.question(
|
||||
`How long is your work day today, excluding the lunch break? [${formatDuration(defaultWorkDayDuration)}] `,
|
||||
`How long is your work day today, excluding the lunch break? [${formatDuration(
|
||||
defaultWorkDayDuration,
|
||||
true,
|
||||
)}] `,
|
||||
);
|
||||
if (durationAnswer !== '') {
|
||||
workDayDuration = parseDuration(durationAnswer);
|
||||
|
@ -44,8 +50,8 @@ const main = async () => {
|
|||
// Calculate worked time
|
||||
const startTimeAnswer = await rl.question(`What time did you start work today? [${defaultStartTime}] `);
|
||||
if (startTimeAnswer !== '') {
|
||||
started = parseTimestamp(startTimeAnswer);
|
||||
if (!started.isValid()) {
|
||||
startedAt = parseTimestamp(startTimeAnswer);
|
||||
if (!startedAt.isValid()) {
|
||||
error(
|
||||
chalk.red(
|
||||
`Failed to parse ${startTimeAnswer} to time, using default start time ${defaultStartTime}`,
|
||||
|
@ -54,54 +60,88 @@ const main = async () => {
|
|||
}
|
||||
}
|
||||
|
||||
if (!started?.isValid()) {
|
||||
started = parseTimestamp(defaultStartTime);
|
||||
if (!startedAt?.isValid()) {
|
||||
startedAt = parseTimestamp(defaultStartTime);
|
||||
}
|
||||
|
||||
let stopped: Dayjs | undefined = undefined;
|
||||
let stoppedWorking = false;
|
||||
let stoppedAt: Dayjs | undefined = undefined;
|
||||
const stoppedAnswer = await rl.question(
|
||||
"What time did you stop working (leave empty if you didn't stop yet)? ",
|
||||
`What time did you stop working (default is current time if you didn't stop yet)? [${formatTime(now)}] `,
|
||||
);
|
||||
|
||||
if (stoppedAnswer === '') {
|
||||
stopped = dayjs();
|
||||
stoppedAt = now;
|
||||
} else {
|
||||
stopped = parseTimestamp(stoppedAnswer);
|
||||
if (!stopped.isValid()) {
|
||||
stoppedWorking = true;
|
||||
stoppedAt = parseTimestamp(stoppedAnswer);
|
||||
if (!stoppedAt.isValid()) {
|
||||
error(`Failed to parse ${stoppedAnswer} to time, using current time`);
|
||||
stopped = dayjs();
|
||||
stoppedAt = dayjs();
|
||||
}
|
||||
}
|
||||
|
||||
let worked = dayjs.duration(stopped.diff(started));
|
||||
if (stoppedAt.isSame(startedAt) || stoppedAt.isBefore(startedAt)) {
|
||||
error(
|
||||
chalk.red(
|
||||
`Start time (${formatTime(startedAt)}) needs to be before stop time (${formatTime(
|
||||
stoppedAt,
|
||||
)}). Exiting`,
|
||||
),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let worked = dayjs.duration(stoppedAt.diff(startedAt));
|
||||
|
||||
if ((await rl.question('Did you have a lunch break? [Y/n] ')).toLowerCase() !== 'n') {
|
||||
worked = worked.subtract(lunchBreakDuration);
|
||||
}
|
||||
|
||||
// Calculate unlogged time
|
||||
let unLogged: Duration | undefined = undefined;
|
||||
let loggedAnswer = await rl.question('How many hours did you log already? [00:00] ');
|
||||
if (loggedAnswer === '') {
|
||||
loggedAnswer = '00:00';
|
||||
}
|
||||
const logged = parseDuration(loggedAnswer);
|
||||
const unLoggedDuration = workDayDuration.subtract(logged);
|
||||
if (unLoggedDuration.asMinutes() > 0) {
|
||||
unLogged = unLoggedDuration;
|
||||
let unLogged: Duration | undefined = undefined;
|
||||
|
||||
if (logged.asMinutes() === worked.asMinutes()) {
|
||||
unLogged = worked.subtract(logged);
|
||||
} else {
|
||||
unLogged = worked.subtract(logged);
|
||||
}
|
||||
|
||||
// Log result
|
||||
log();
|
||||
log('Started working at', formatTimestamp(started));
|
||||
log('Stopped working at', formatTimestamp(stopped));
|
||||
log('Total worked today:', chalk.green(formatDuration(worked)), chalk.yellow(getHoursRoundedStr(worked)));
|
||||
log('Started working at:', formatTimestamp(startedAt));
|
||||
log((stoppedWorking ? 'Stopped working' : 'Hours calculated') + ' at:', formatTimestamp(stoppedAt));
|
||||
log('Worked today:', chalk.green(formatDuration(worked)), chalk.yellow(getHoursRoundedStr(worked)));
|
||||
|
||||
if (unLogged.asMinutes() == 0) {
|
||||
log('Unlogged today:', chalk.green('none'));
|
||||
} else if (unLogged.asMinutes() > 0) {
|
||||
log('Unlogged today:', chalk.red(formatDuration(unLogged)), chalk.yellow(getHoursRoundedStr(unLogged)));
|
||||
} else if (unLogged.asMinutes() < 0) {
|
||||
log(
|
||||
'Unlogged today:',
|
||||
unLogged
|
||||
? `${chalk.red(formatDuration(unLogged))} ${chalk.yellow(getHoursRoundedStr(unLogged))}`
|
||||
: chalk.green('none'),
|
||||
chalk.red(`You have logged ${formatDuration(unLogged)} more than you worked today!`),
|
||||
chalk.yellow(getHoursRoundedStr(unLogged)),
|
||||
);
|
||||
}
|
||||
|
||||
const workLeft = workDayDuration.subtract(worked);
|
||||
const workLeftMinutes = workLeft.asMinutes();
|
||||
if (workLeftMinutes > 0) {
|
||||
log('You still have to work', chalk.green(formatDuration(workLeft)), 'more today');
|
||||
} else if (workLeft.asMinutes() < 0) {
|
||||
log(
|
||||
'You worked',
|
||||
chalk.green(
|
||||
formatDuration(dayjs.duration({ minutes: Math.round(workLeftMinutes * -1) })),
|
||||
'overtime today',
|
||||
),
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
rl.close();
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import duration, { Duration } from 'dayjs/plugin/duration.js';
|
|||
dayjs.extend(customParseFormat);
|
||||
dayjs.extend(duration);
|
||||
|
||||
export const parseTimestamp = (time: string): Dayjs => dayjs(time, 'H:mm', true);
|
||||
export const parseTimestamp = (time: string): Dayjs => dayjs(time, 'HH:mm', true);
|
||||
|
||||
export const parseDuration = (time: string): Duration => {
|
||||
const [hours, minutes] = time.split(':').map(Number);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue