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 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) =>
|
export const getHoursRoundedStr = (duration: Duration) =>
|
||||||
`(${getHoursRounded(duration)} as hours rounded to next even 15 minutes)`;
|
`(${getHoursRounded(duration)} as hours rounded to next even 15 minutes)`;
|
||||||
|
|
96
src/main.ts
96
src/main.ts
|
@ -1,10 +1,12 @@
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import dayjs, { Dayjs } from 'dayjs';
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
import * as readline from 'readline/promises';
|
import * as readline from 'readline/promises';
|
||||||
import { formatDuration, formatTimestamp, getHoursRoundedStr } from './format.js';
|
import { formatDuration, formatTime, formatTimestamp, getHoursRoundedStr } from './format.js';
|
||||||
import { Duration } from 'dayjs/plugin/duration';
|
import duration, { Duration } from 'dayjs/plugin/duration.js';
|
||||||
import { parseDuration, parseTimestamp } from './parse.js';
|
import { parseDuration, parseTimestamp } from './parse.js';
|
||||||
|
|
||||||
|
dayjs.extend(duration);
|
||||||
|
|
||||||
const { log, error } = console;
|
const { log, error } = console;
|
||||||
const defaultStartTime = '08:00';
|
const defaultStartTime = '08:00';
|
||||||
const lunchBreakDuration = dayjs.duration({ minutes: 30 });
|
const lunchBreakDuration = dayjs.duration({ minutes: 30 });
|
||||||
|
@ -16,14 +18,18 @@ const main = async () => {
|
||||||
output: process.stdout,
|
output: process.stdout,
|
||||||
});
|
});
|
||||||
|
|
||||||
let started: Dayjs | undefined = undefined;
|
let startedAt: Dayjs | undefined = undefined;
|
||||||
|
const now = dayjs();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Get work day duration
|
// Get work day duration
|
||||||
let workDayDuration: Duration | undefined = undefined;
|
let workDayDuration: Duration | undefined = undefined;
|
||||||
|
|
||||||
const durationAnswer = await rl.question(
|
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 !== '') {
|
if (durationAnswer !== '') {
|
||||||
workDayDuration = parseDuration(durationAnswer);
|
workDayDuration = parseDuration(durationAnswer);
|
||||||
|
@ -44,8 +50,8 @@ const main = async () => {
|
||||||
// Calculate worked time
|
// Calculate worked time
|
||||||
const startTimeAnswer = await rl.question(`What time did you start work today? [${defaultStartTime}] `);
|
const startTimeAnswer = await rl.question(`What time did you start work today? [${defaultStartTime}] `);
|
||||||
if (startTimeAnswer !== '') {
|
if (startTimeAnswer !== '') {
|
||||||
started = parseTimestamp(startTimeAnswer);
|
startedAt = parseTimestamp(startTimeAnswer);
|
||||||
if (!started.isValid()) {
|
if (!startedAt.isValid()) {
|
||||||
error(
|
error(
|
||||||
chalk.red(
|
chalk.red(
|
||||||
`Failed to parse ${startTimeAnswer} to time, using default start time ${defaultStartTime}`,
|
`Failed to parse ${startTimeAnswer} to time, using default start time ${defaultStartTime}`,
|
||||||
|
@ -54,54 +60,88 @@ const main = async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!started?.isValid()) {
|
if (!startedAt?.isValid()) {
|
||||||
started = parseTimestamp(defaultStartTime);
|
startedAt = parseTimestamp(defaultStartTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
let stopped: Dayjs | undefined = undefined;
|
let stoppedWorking = false;
|
||||||
|
let stoppedAt: Dayjs | undefined = undefined;
|
||||||
const stoppedAnswer = await rl.question(
|
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 === '') {
|
if (stoppedAnswer === '') {
|
||||||
stopped = dayjs();
|
stoppedAt = now;
|
||||||
} else {
|
} else {
|
||||||
stopped = parseTimestamp(stoppedAnswer);
|
stoppedWorking = true;
|
||||||
if (!stopped.isValid()) {
|
stoppedAt = parseTimestamp(stoppedAnswer);
|
||||||
|
if (!stoppedAt.isValid()) {
|
||||||
error(`Failed to parse ${stoppedAnswer} to time, using current time`);
|
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') {
|
if ((await rl.question('Did you have a lunch break? [Y/n] ')).toLowerCase() !== 'n') {
|
||||||
worked = worked.subtract(lunchBreakDuration);
|
worked = worked.subtract(lunchBreakDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate unlogged time
|
// Calculate unlogged time
|
||||||
let unLogged: Duration | undefined = undefined;
|
|
||||||
let loggedAnswer = await rl.question('How many hours did you log already? [00:00] ');
|
let loggedAnswer = await rl.question('How many hours did you log already? [00:00] ');
|
||||||
if (loggedAnswer === '') {
|
if (loggedAnswer === '') {
|
||||||
loggedAnswer = '00:00';
|
loggedAnswer = '00:00';
|
||||||
}
|
}
|
||||||
const logged = parseDuration(loggedAnswer);
|
const logged = parseDuration(loggedAnswer);
|
||||||
const unLoggedDuration = workDayDuration.subtract(logged);
|
let unLogged: Duration | undefined = undefined;
|
||||||
if (unLoggedDuration.asMinutes() > 0) {
|
|
||||||
unLogged = unLoggedDuration;
|
if (logged.asMinutes() === worked.asMinutes()) {
|
||||||
|
unLogged = worked.subtract(logged);
|
||||||
|
} else {
|
||||||
|
unLogged = worked.subtract(logged);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log result
|
// Log result
|
||||||
log();
|
log();
|
||||||
log('Started working at', formatTimestamp(started));
|
log('Started working at:', formatTimestamp(startedAt));
|
||||||
log('Stopped working at', formatTimestamp(stopped));
|
log((stoppedWorking ? 'Stopped working' : 'Hours calculated') + ' at:', formatTimestamp(stoppedAt));
|
||||||
log('Total worked today:', chalk.green(formatDuration(worked)), chalk.yellow(getHoursRoundedStr(worked)));
|
log('Worked today:', chalk.green(formatDuration(worked)), chalk.yellow(getHoursRoundedStr(worked)));
|
||||||
log(
|
|
||||||
'Unlogged today:',
|
if (unLogged.asMinutes() == 0) {
|
||||||
unLogged
|
log('Unlogged today:', chalk.green('none'));
|
||||||
? `${chalk.red(formatDuration(unLogged))} ${chalk.yellow(getHoursRoundedStr(unLogged))}`
|
} else if (unLogged.asMinutes() > 0) {
|
||||||
: chalk.green('none'),
|
log('Unlogged today:', chalk.red(formatDuration(unLogged)), chalk.yellow(getHoursRoundedStr(unLogged)));
|
||||||
);
|
} else if (unLogged.asMinutes() < 0) {
|
||||||
|
log(
|
||||||
|
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 {
|
} finally {
|
||||||
rl.close();
|
rl.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import duration, { Duration } from 'dayjs/plugin/duration.js';
|
||||||
dayjs.extend(customParseFormat);
|
dayjs.extend(customParseFormat);
|
||||||
dayjs.extend(duration);
|
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 => {
|
export const parseDuration = (time: string): Duration => {
|
||||||
const [hours, minutes] = time.split(':').map(Number);
|
const [hours, minutes] = time.split(':').map(Number);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue