Skip to content

Commit

Permalink
LCSD-7141 - Convert User Entered Time into PST (3) (#4263)
Browse files Browse the repository at this point in the history
unifying time zone changes into one pr

Co-authored-by: W. Ross White <[email protected]>
  • Loading branch information
williamrosswhite and rossAtQuartech authored Aug 29, 2024
1 parent 654585a commit 315c84b
Show file tree
Hide file tree
Showing 8 changed files with 3,377 additions and 1,234 deletions.
4,212 changes: 3,092 additions & 1,120 deletions cllc-public-app/ClientApp/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions cllc-public-app/ClientApp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"leaflet": "^1.7.1",
"lodash": "^4.17.21",
"material-design-icons": "^3.0.1",
"moment-timezone": "^0.5.45",
"mygovbc-bootstrap-theme": "^0.4.1",
"ng-busy": "^12.0.2",
"ng2-currency-mask": "^12.0.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ export class PoliceSummaryComponent extends FormBase implements OnInit {
if (loc.eventDates?.length > 0) {
const formatterdDates = [];
loc.eventDates.forEach(ed => {
ed = Object.assign(new SepSchedule(null), ed);
ed = Object.assign(new SepSchedule(null, null), ed);
formatterdDates.push({ ed, ...ed.toEventFormValue() });
});
loc.eventDates = formatterdDates;
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export class EventComponent extends FormBase implements OnInit {
validationMessages: string[];
previewCities: AutoCompleteItem[] = [];
autocompleteCities: AutoCompleteItem[] = [];
isPacificTimeZone: boolean;
isOpen: boolean[][] = [];
get minDate() {
return new Date();
Expand Down Expand Up @@ -86,7 +85,6 @@ export class EventComponent extends FormBase implements OnInit {
}

ngOnInit(): void {
this.isPacificTimeZone = true;
// create a form for the basic details
this.form = this.fb.group({
sepCity: ["", [Validators.required, Validators.minLength(2)]],
Expand Down Expand Up @@ -122,9 +120,6 @@ export class EventComponent extends FormBase implements OnInit {
}
});
}
checkTimeZone() {
this.isPacificTimeZone = !this.isPacificTimeZone;
}
setFormValue(app: SepApplication) {
// if there's an app
if (app) {
Expand Down Expand Up @@ -244,7 +239,6 @@ export class EventComponent extends FormBase implements OnInit {
this.locations.push(locationForm);
}


removeLocation(locationIndex: number) {
this.locations.removeAt(locationIndex);
}
Expand All @@ -271,21 +265,49 @@ export class EventComponent extends FormBase implements OnInit {
eventEndValue: ["10:00 PM", [Validators.required]],
serviceStartValue: ["9:00 AM", [Validators.required]],
serviceEndValue: ["9:30 PM", [Validators.required]],
mountainAdjustedPacificEventStartValue: ["8:00 AM", [Validators.required]],
mountainAdjustedPacificEventEndValue: ["9:00 PM", [Validators.required]],
mountainAdjustedPacificServiceStartValue: ["8:00 AM", [Validators.required]],
mountainAdjustedPacificServiceEndValue: ["8:30 PM", [Validators.required]],
liquorServiceHoursExtensionReason: [""],
disturbancePreventionMeasuresDetails: [""]
disturbancePreventionMeasuresDetails: [""],
isPacificTimeZone: [true]
}, { validators: eventTimesValidator });

eventDate = Object.assign(new SepSchedule(null), eventDate);
eventDate = Object.assign(new SepSchedule(null, true), eventDate);
const val = eventDate.toEventFormValue();

// Set default to event start date
if (!val.eventDate) {
val.eventDate = this?.sepApplication?.eventStartDate;
}

datesForm.patchValue(val);

datesForm.get('isPacificTimeZone').valueChanges.subscribe(value => {
localStorage.setItem('isPacificTimeZone', JSON.stringify(value));
});

const updateMountainTime = (controlName: string, mountainControlName: string) => {

const mountainTimeControlValue = datesForm.get(controlName).value;
const newMountainValue = this.getPacificTimeFromMountain(mountainTimeControlValue);
datesForm.get(mountainControlName).setValue(newMountainValue, { emitEvent: false });

datesForm.get(controlName).valueChanges.subscribe(value => {
const newMountainValue = this.getPacificTimeFromMountain(value);
datesForm.get(mountainControlName).setValue(newMountainValue, { emitEvent: false });
});
};

updateMountainTime('eventStartValue', 'mountainAdjustedPacificEventStartValue');
updateMountainTime('eventEndValue', 'mountainAdjustedPacificEventEndValue');
updateMountainTime('serviceStartValue', 'mountainAdjustedPacificServiceStartValue');
updateMountainTime('serviceEndValue', 'mountainAdjustedPacificServiceEndValue');

return datesForm;
}

getIsOpen(locationIndex: number, eventIndex: number): boolean {
if(this.isOpen[locationIndex] === undefined) {
return true;
Expand Down Expand Up @@ -450,7 +472,7 @@ export class EventComponent extends FormBase implements OnInit {
formData?.eventLocations.forEach(location => {
const dateValues = [];
location?.eventDates.forEach(sched => {
dateValues.push(new SepSchedule(sched));
dateValues.push(new SepSchedule(sched, sched.isPacificTimeZone));
});
location.eventDates = dateValues;
});
Expand Down Expand Up @@ -496,6 +518,40 @@ export class EventComponent extends FormBase implements OnInit {
return outdoorAreaExists;
}

getPacificTimeFromMountain(time: string): string {

// Convert 12-hour format with AM/PM to 24-hour format
const [timePart, modifier] = time.split(' ');
let [hours, minutes] = timePart.split(':').map(Number);

if (modifier === 'PM' && hours < 12) {
hours += 12;
}
if (modifier === 'AM' && hours === 12) {
hours = 0;
}

// Create a new Date object and adjust for the time zone difference
const date = new Date();
date.setHours(hours, minutes); // Set the time to the Mountain Time
date.setHours(date.getHours() - 1); // Convert from Mountain to Pacific Time

// Convert back to 12-hour format with AM/PM
let pacificHours = date.getHours();
const pacificMinutes = date.getMinutes();
const pacificModifier = pacificHours >= 12 ? 'PM' : 'AM';

if (pacificHours > 12) {
pacificHours -= 12;
} else if (pacificHours === 0) {
pacificHours = 12;
}

const convertedTime = `${pacificHours}:${pacificMinutes.toString().padStart(2, '0')} ${pacificModifier}`;

return convertedTime;
}

next() {
this.showValidationMessages = false;
if (this.isValid() && this.form.get('sepCity')?.value?.id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,14 +360,25 @@ <h4> SERVICE AREA ({{k+1}} of {{loc.serviceAreas.length}})</h4>
<section *ngFor="let eventDate of loc.eventDates; let j = index;">
<h4>EVENT DATE ({{j+1}} of {{loc.eventDates.length}})</h4>
<div class="d-flex">
<label>Start Date:</label> <span>{{eventDate.eventDate | date: 'dd MMM yyyy'}}</span>
<label>Start Date:</label> <span>{{eventDate.eventDate | date: 'dd MMM yyyy'}}</span>
</div>
<div class="d-flex">
<label>Start Time:</label> <span>{{eventDate.eventStartValue}} - {{eventDate.eventEndValue}}</span>
<label>
<ng-container *ngIf="isPacificTimeZone !== false; else mountainTime">
Start Time (Pacific):
</ng-container>
<ng-template #mountainTime>Start Time (Mountain):</ng-template>
</label>
<span>{{eventDate.eventStartValue}} - {{eventDate.eventEndValue}}</span>
</div>
<div class="d-flex">
<label>Liquor Service:</label> <span>{{eventDate.serviceStartValue}} -
{{eventDate.serviceEndValue}}</span>
<label>
<ng-container *ngIf="isPacificTimeZone !== false; else mountainTime">
Liquor Service (Pacific):
</ng-container>
<ng-template #mountainTime>Start Time (Mountain):</ng-template>
</label>
<span> {{eventDate.serviceStartValue}} - {{eventDate.serviceEndValue}}</span>
</div>
</section>
</mat-expansion-panel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,12 +242,12 @@ export class SummaryComponent implements OnInit {
if (this?.application?.eventLocations?.length > 0) {
this.application.eventLocations.forEach(loc => {
if (loc.eventDates?.length > 0) {
const formatterdDates = [];
const formattedDates = [];
loc.eventDates.forEach(ed => {
ed = Object.assign(new SepSchedule(null), ed);
formatterdDates.push({ ed, ...ed.toEventFormValue() });
ed = Object.assign(new SepSchedule(null, null), ed);
formattedDates.push({ ed, ...ed.toEventFormValue() });
});
loc.eventDates = formatterdDates;
loc.eventDates = formattedDates;
}
});
}
Expand Down
121 changes: 86 additions & 35 deletions cllc-public-app/ClientApp/src/app/models/sep-schedule.model.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { format, addDays } from "date-fns";
import * as moment from 'moment-timezone';

export class SepSchedule {
id: string; // server side primary key
Expand All @@ -8,85 +9,131 @@ export class SepSchedule {
eventEnd: Date;
serviceStart: Date;
serviceEnd: Date;
mountainAdjustedPacificEventStart: Date;
mountainAdjustedPacificEventEnd: Date;
mountainAdjustedPacificServiceStart: Date;
mountainAdjustedPacificServiceEnd: Date;
liquorServiceHoursExtensionReason: string;
disturbancePreventionMeasuresDetails: string;
readonly vancouverTimeZone: string = "America/Vancouver";
readonly edmontonTimeZone: string = "America/Edmonton";
isPacificTimeZone: boolean;

constructor(sched: IEventFormValue, isPacificTimeZone: boolean) {

this.isPacificTimeZone = isPacificTimeZone;

constructor(sched: IEventFormValue) {
if (sched) {

this.id = sched.id;
this.eventStart = this.formatDate(sched.eventDate, sched.eventStartValue);
this.eventEnd = this.formatDate(sched.eventDate, sched.eventEndValue);
this.serviceStart = this.formatDate(sched.eventDate, sched.serviceStartValue);
this.serviceEnd = this.formatDate(sched.eventDate, sched.serviceEndValue);
this.liquorServiceHoursExtensionReason = sched.liquorServiceHoursExtensionReason;
this.disturbancePreventionMeasuresDetails = sched.disturbancePreventionMeasuresDetails;

if(isPacificTimeZone) {
this.eventStart = this.formatDate(sched.eventDate, sched.eventStartValue, true);
this.eventEnd = this.formatDate(sched.eventDate, sched.eventEndValue, true);
this.serviceStart = this.formatDate(sched.eventDate, sched.serviceStartValue, true);
this.serviceEnd = this.formatDate(sched.eventDate, sched.serviceEndValue, true);
} else {
this.eventStart = this.formatDate(sched.eventDate, sched.eventStartValue, false);
this.eventEnd = this.formatDate(sched.eventDate, sched.eventEndValue, false);
this.serviceStart = this.formatDate(sched.eventDate, sched.serviceStartValue, false);
this.serviceEnd = this.formatDate(sched.eventDate, sched.serviceEndValue, false);
}
}
}

// Convert the SepSchedule object to an IEventFormValue object
toEventFormValue(): IEventFormValue {
const result = { id: this.id } as IEventFormValue;
result.eventDate = this.eventStart;



if (this.eventStart) {
result.eventStartValue = format(new Date(this.eventStart), "h:mm aa");
if(this.mountainAdjustedPacificEventStart) {
result.mountainAdjustedPacificEventStartValue = format(new Date(this.mountainAdjustedPacificEventStart), "h:mm aa");
}
}

if (this.eventEnd) {
result.eventEndValue = format(new Date(this.eventEnd), "h:mm aa");
if(this.mountainAdjustedPacificEventEnd) {
result.mountainAdjustedPacificEventEndValue = format(new Date(this.mountainAdjustedPacificEventEnd), "h:mm aa");
}
}

if (this.serviceStart) {
result.serviceStartValue = format(new Date(this.serviceStart), "h:mm aa");
if(this.mountainAdjustedPacificServiceStart) {
result.mountainAdjustedPacificServiceStartValue = format(new Date(this.mountainAdjustedPacificServiceStart), "h:mm aa");
}
}

if (this.serviceEnd) {
result.serviceEndValue = format(new Date(this.serviceEnd), "h:mm aa");
if(this.mountainAdjustedPacificServiceEnd) {
result.mountainAdjustedPacificServiceEndValue = format(new Date(this.mountainAdjustedPacificServiceEnd), "h:mm aa");
}
}

result.liquorServiceHoursExtensionReason = this.liquorServiceHoursExtensionReason;
result.disturbancePreventionMeasuresDetails = this.disturbancePreventionMeasuresDetails;

return result;
}

// Get the number of service hours
getServiceHours(): number {
const serviceHours = parseInt(format(new Date(this.serviceEnd), "H")) - parseInt(format(new Date(this.serviceStart), "H"));
return serviceHours;
}

private isNextDay(time: string): boolean {
const dayBreakIndex = TIME_SLOTS.indexOf(TIME_SLOTS.find(slot => slot.dayBreak === true));
const timeIndex = TIME_SLOTS.indexOf(TIME_SLOTS.find(slot => slot.value === time));
return timeIndex >= dayBreakIndex;
}

/**
*
* @param eventDate
* @param time, assumed format "HH:MM [AM,PM]" e.g. '6:30 PM'
*/
private formatDate(eventDate: Date, time: string): Date {

// Convert the time to 24-hour format and convert the date to Pacific Time
private formatDate(eventDate: Date, time: string, isPacificTimeZone: boolean): Date {
let tempDate = new Date(eventDate);

// let day = parseInt(format(new Date(eventDate), "dd"), 10);

// console.log("formatting date: ", eventDate, time)

if (this.isNextDay(time)) {
// console.log("is next day")
tempDate = addDays(tempDate, 1);
// day += 1;
}
// const dateString = `${day} ${format(tempDate, "d MMM yyyy")} ${time}`;
const dateString = `${format(tempDate, "d MMM yyyy")} ${time}`;
// console.log("dateString:",dateString);
const result = new Date(dateString);
// console.log("result is: ", result);
return result;

// Convert time to 24-hour format
const time24 = convertTo24Hour(time);

// Create a date string in the format "yyyy-MM-ddTHH:mm:ss"
const dateString = `${format(tempDate, "yyyy-MM-dd")}T${time24}`;

// Convert the date string to Pacific Time
let dateInPacific = moment.tz(dateString, "America/Los_Angeles");

// Convert to UTC, subtract an hour if isPacificTimeZone is false
if (!isPacificTimeZone) {
dateInPacific = dateInPacific.clone().tz("UTC").add(-1, 'hours');
} else {
dateInPacific = dateInPacific.clone().tz("UTC");
}

return dateInPacific.toDate();
}
}

private isNextDay(time: string): boolean {
const dayBreakIndex = TIME_SLOTS.indexOf(TIME_SLOTS.find(slot => slot.dayBreak === true));
const timeIndex = TIME_SLOTS.indexOf(TIME_SLOTS.find(slot => slot.value === time));
return timeIndex >= dayBreakIndex;
// Convert the time to 24-hour format
function convertTo24Hour(time) {
const [main, period] = time.split(' ');
let [hours, minutes] = main.split(':');

if (period === 'PM' && hours !== '12') {
hours = (Number(hours) + 12).toString();
} else if (period === 'AM' && hours === '12') {
hours = '00';
}

return `${hours.padStart(2, '0')}:${minutes}`;
}


export const TIME_SLOTS = [
{ value: "8:00 AM", name: "8:00 AM" },
{ value: "8:30 AM", name: "8:30 AM" },
Expand Down Expand Up @@ -138,14 +185,18 @@ export const TIME_SLOTS = [
{ value: "7:30 AM", name: "7:30 AM" }
];


export interface IEventFormValue {
id: string;
eventDate: Date;
eventStartValue: string;
eventEndValue: string;
serviceStartValue: string;
serviceEndValue: string;
mountainAdjustedPacificEventStartValue: string;
mountainAdjustedPacificEventEndValue: string;
mountainAdjustedPacificServiceStartValue: string;
mountainAdjustedPacificServiceEndValue: string;
liquorServiceHoursExtensionReason: string;
disturbancePreventionMeasuresDetails: string;
}
isPacificTimeZone: boolean;
}

0 comments on commit 315c84b

Please sign in to comment.