Skip to content

Timezone configuration #914

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Parse-Dashboard/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ let configUserId = program.userId || process.env.PARSE_DASHBOARD_USER_ID;
let configUserPassword = program.userPassword || process.env.PARSE_DASHBOARD_USER_PASSWORD;
let configSSLKey = program.sslKey || process.env.PARSE_DASHBOARD_SSL_KEY;
let configSSLCert = program.sslCert || process.env.PARSE_DASHBOARD_SSL_CERT;
let configTimezone = program.timezone || process.env.PARSE_DASHBOARD_TIMEZONE;
if (!program.config && !process.env.PARSE_DASHBOARD_CONFIG) {
if (configServerURL && configMasterKey && configAppId) {
configFromCLI = {
Expand All @@ -62,6 +63,7 @@ if (!program.config && !process.env.PARSE_DASHBOARD_CONFIG) {
serverURL: configServerURL,
masterKey: configMasterKey,
appName: configAppName,
timezone: configTimezone,
},
]
}
Expand Down
1 change: 1 addition & 0 deletions Parse-Dashboard/parse-dashboard-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"appId": "hello",
"masterKey": "world",
"appName": "",
"timezone": null,
"iconName": "",
"primaryBackgroundColor": "",
"secondaryBackgroundColor": ""
Expand Down
32 changes: 27 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Parse Dashboard is a standalone dashboard for managing your Parse apps. You can
* [Multiple apps](#multiple-apps)
* [Single app](#single-app)
* [Managing Multiple Apps](#managing-multiple-apps)
* [Timezone configuration](#timezone-configuration)
* [App Icon Configuration](#app-icon-configuration)
* [App Background Color Configuration](#app-background-color-configuration)
* [Other Configuration Options](#other-configuration-options)
Expand Down Expand Up @@ -69,7 +70,8 @@ You can also start the dashboard from the command line with a config file. To d
"serverURL": "http://localhost:1337/parse",
"appId": "myAppId",
"masterKey": "myMasterKey",
"appName": "MyApp"
"appName": "MyApp",
"timezone": "America/Bahia"
}
]
}
Expand Down Expand Up @@ -100,6 +102,7 @@ PARSE_DASHBOARD_SERVER_URL: "http://localhost:1337/parse"
PARSE_DASHBOARD_MASTER_KEY: "myMasterKey"
PARSE_DASHBOARD_APP_ID: "myAppId"
PARSE_DASHBOARD_APP_NAME: "MyApp"
PARSE_DASHBOARD_TIMEZONE: "America/Bahia"
PARSE_DASHBOARD_USER_ID: "user1"
PARSE_DASHBOARD_USER_PASSWORD: "pass"
PARSE_DASHBOARD_SSL_KEY: "sslKey"
Expand Down Expand Up @@ -129,7 +132,26 @@ You can manage self-hosted [Parse Server](https://github.com/ParsePlatform/parse
"serverURL": "http://localhost:1337/parse", // Self-hosted Parse Server
"appId": "myAppId",
"masterKey": "myMasterKey",
"appName": "My Parse Server App"
"appName": "My Parse Server App",
"timezone": "America/Chicago",
}
]
}
```

## Timezone Configuration

Parse Dashboard supports adding an optional timezone for each app, if you dont set any timezone, the UTC will be used like default. Just set a `timezone` with a valid timezone name.

```json
{
"apps": [
{
"serverURL": "http://localhost:1337/parse",
"appId": "myAppId",
"masterKey": "myMasterKey",
"appName": "My Parse Server App",
"timezone": "America/Bahia",
}
]
}
Expand Down Expand Up @@ -367,15 +389,15 @@ You can mark a user as a read-only user:
"appId": "myAppId1",
"masterKey": "myMasterKey1",
"readOnlyMasterKey": "myReadOnlyMasterKey1",
"serverURL": "myURL1",
"serverURL": "myURL1",
"port": 4040,
"production": true
},
{
"appId": "myAppId2",
"masterKey": "myMasterKey2",
"readOnlyMasterKey": "myReadOnlyMasterKey2",
"serverURL": "myURL2",
"serverURL": "myURL2",
"port": 4041,
"production": true
}
Expand Down Expand Up @@ -410,7 +432,7 @@ You can give read only access to a user on a per-app basis:
"appId": "myAppId1",
"masterKey": "myMasterKey1",
"readOnlyMasterKey": "myReadOnlyMasterKey1",
"serverURL": "myURL",
"serverURL": "myURL",
"port": 4040,
"production": true
},
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
"json-file-plus": "^3.2.0",
"package-json": "^5.0.0",
"passport": "^0.4.0",
"passport-local": "^1.0.0"
"passport-local": "^1.0.0",
"timezone-support": "^1.5.5"
},
"devDependencies": {
"@babel/core": "7.1.2",
Expand Down
6 changes: 3 additions & 3 deletions src/components/BrowserCell/BrowserCell.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*/
import { dateStringUTC } from 'lib/DateUtils';
import { dateString } from 'lib/DateUtils';
import getFileName from 'lib/getFileName';
import Parse from 'parse';
import Pill from 'components/Pill/Pill.react';
import React from 'react';
import styles from 'components/BrowserCell/BrowserCell.scss';
import { unselectable } from 'stylesheets/base.scss';

let BrowserCell = ({ type, value, hidden, width, current, onSelect, onEditChange, setRelation, onPointerClick }) => {
let BrowserCell = ({ type, value, hidden, width, current, timezone, onSelect, onEditChange, setRelation, onPointerClick }) => {
let content = value;
let classes = [styles.cell, unselectable];
if (hidden) {
Expand All @@ -39,7 +39,7 @@ let BrowserCell = ({ type, value, hidden, width, current, onSelect, onEditChange
</a>
);
} else if (type === 'Date') {
content = dateStringUTC(value);
content = dateString(value, timezone);
} else if (type === 'Boolean') {
content = value ? 'True' : 'False';
} else if (type === 'Array') {
Expand Down
6 changes: 3 additions & 3 deletions src/components/PushCerts/CertsTable.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@
*/
import FormTable from 'components/FormTable/FormTable.react';
import React from 'react';
import { dateStringUTC } from 'lib/DateUtils';
import { dateString } from 'lib/DateUtils';

const MONTH_IN_MS = 1000 * 60 * 60 * 24 * 30;

let CertsTable = ({ certs, onDelete, uploadPending }) => {
let CertsTable = ({ certs, timezone, onDelete, uploadPending }) => {
let tableData = certs.map(c => {
let color = '';
let expiresKeyColor = '';
Expand All @@ -36,7 +36,7 @@ let CertsTable = ({ certs, onDelete, uploadPending }) => {
{
key: isExpired ? 'Expired' : 'Expires',
keyColor: expiresKeyColor,
value: dateStringUTC(new Date(c.expiration)),
value: dateString(new Date(c.expiration), timezone),
}
]
};
Expand Down
3 changes: 2 additions & 1 deletion src/components/PushCerts/PushCerts.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ PushCerts.propTypes = {
onDelete: PropTypes.func.isRequired.describe(
'A handler for when a Push has been deleted from the server'
),
timezone: PropTypes.string,
};

export default PushCerts;
export default PushCerts;
4 changes: 2 additions & 2 deletions src/dashboard/Account/AccountOverview.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import renderFlowFooterChanges from 'lib/renderFlowFooterChanges';
import styles from 'dashboard/Settings/Settings.scss';
import TextInput from 'components/TextInput/TextInput.react';
import Toolbar from 'components/Toolbar/Toolbar.react';
import { dateStringUTC } from 'lib/DateUtils';
import { dateString } from 'lib/DateUtils';

const DEFAULT_LABEL_WIDTH = 56;
const XHR_KEY = 'AccountOverview';
Expand Down Expand Up @@ -136,7 +136,7 @@ export default class AccountOverview extends React.Component {
},
{
key: 'Expires',
value: dateStringUTC(new Date(key.expiresAt)),
value: dateString(new Date(key.expiresAt)),
},
],
}))} />
Expand Down
1 change: 1 addition & 0 deletions src/dashboard/Data/Browser/BrowserTable.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export default class BrowserTable extends React.Component {
onPointerClick={this.props.onPointerClick}
setRelation={this.props.setRelation}
value={attr}
timezone={this.props.timezone}
hidden={hidden} />
);
})}
Expand Down
3 changes: 2 additions & 1 deletion src/dashboard/Data/Browser/DataBrowser.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import BrowserToolbar from 'dashboard/Data/Browser/BrowserToolbar.react'
import * as ColumnPreferences from 'lib/ColumnPreferences';
import ParseApp from 'lib/ParseApp';
import React from 'react';
import PropTypes from 'lib/PropTypes';
import PropTypes from 'lib/PropTypes';
import { SpecialClasses } from 'lib/Constants';

/**
Expand Down Expand Up @@ -200,6 +200,7 @@ export default class DataBrowser extends React.Component {
handleResize={this.handleResize.bind(this)}
setEditing={this.setEditing.bind(this)}
setCurrent={this.setCurrent.bind(this)}
timezone={this.context.currentApp.timezone}
{...other} />
<BrowserToolbar
hidePerms={className === '_Installation'}
Expand Down
3 changes: 2 additions & 1 deletion src/dashboard/Data/Jobs/JobEdit.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*/
import PropTypes from 'lib/PropTypes';
import PropTypes from 'lib/PropTypes';
import { ActionTypes } from 'lib/stores/JobsStore';
import history from 'dashboard/history';
import JobsForm from 'dashboard/Data/Jobs/JobsForm.react';
Expand Down Expand Up @@ -113,6 +113,7 @@ class JobEdit extends React.Component {
return (
<JobsForm
{...this.props}
timezone={this.context.currentApp.timezone}
submitForm={this.submitForm.bind(this)}
initialFields={{}} />
);
Expand Down
3 changes: 2 additions & 1 deletion src/dashboard/Data/Jobs/Jobs.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,11 @@ class Jobs extends TableView {
</tr>
);
} else if (this.props.params.section === 'status') {
let { timezone } = this.context.currentApp;
return (
<tr key={data.objectId}>
<td style={{width: '20%'}}>{data.jobName}</td>
<td style={{width: '20%'}}>{DateUtils.dateStringUTC(new Date(data.createdAt))}</td>
<td style={{width: '20%'}}>{DateUtils.dateString(new Date(data.createdAt), timezone)}</td>
<td style={{width: '40%'}}>
<div style={{ fontSize: 12, whiteSpace: 'normal', lineHeight: '16px' }}>
{data.message}
Expand Down
4 changes: 2 additions & 2 deletions src/dashboard/Data/Jobs/JobsForm.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import TextInput from 'components/TextInput/TextInput.react';
import TimeInput from 'components/TimeInput/TimeInput.react';
import Toggle from 'components/Toggle/Toggle.react';
import Toolbar from 'components/Toolbar/Toolbar.react';
import { hoursFrom, dateStringUTC } from 'lib/DateUtils';
import { hoursFrom, dateString } from 'lib/DateUtils';

export default class JobsForm extends DashboardView {
constructor(props) {
Expand Down Expand Up @@ -235,7 +235,7 @@ export default class JobsForm extends DashboardView {
if (fields.immediate) {
pieces.push(<strong>immediately</strong>, '.')
} else {
pieces.push('on ', <strong>{dateStringUTC(fields.runAt)}</strong>, '.');
pieces.push('on ', <strong>{dateString(fields.runAt, this.props.timezone)}</strong>, '.');
}
if (fields.repeat) {
pieces.push(' It will repeat ');
Expand Down
5 changes: 3 additions & 2 deletions src/dashboard/Settings/AppleCerts.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*/
import PropTypes from 'lib/PropTypes';
import PropTypes from 'lib/PropTypes';
import Modal from 'components/Modal/Modal.react';
import ParseApp from 'lib/ParseApp';
import PushCerts from 'components/PushCerts/PushCerts.react';
Expand Down Expand Up @@ -57,7 +57,8 @@ export default class AppleCerts extends React.Component {
error={this.state.error}
uploadPending={this.state.uploadPending}
onUpload={this.handleUpload.bind(this)}
onDelete={this.handleDelete.bind(this)} />
onDelete={this.handleDelete.bind(this)}
timezone={this.context.currentApp.timezone} />
{this.state.deletePending === null ? null :
<Modal
type={Modal.Types.DANGER}
Expand Down
40 changes: 27 additions & 13 deletions src/lib/DateUtils.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
/*
import { native } from '../../Parse-Dashboard/public/bundles/dashboard.bundle';
* Copyright (c) 2016-present, Parse, LLC
* All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*/
import { findTimeZone, getZonedTime } from 'timezone-support';

export const MONTHS = [
'January',
'February',
Expand Down Expand Up @@ -110,21 +113,32 @@ export function monthsFrom(date, delta) {
);
}

export function dateStringUTC(date) {
let full = String(date.getUTCDate()) + ' ' +
shortMonth(date.getUTCMonth()) + ' ' +
String(date.getUTCFullYear()) + ' at ';
let time = {
hours: String(date.getUTCHours()),
minutes: String(date.getUTCMinutes()),
seconds: String(date.getUTCSeconds())
export function dateString(nativeDate, timezone = null) {
let date = {
year: nativeDate.getUTCFullYear(),
month: nativeDate.getUTCMonth(),
day: nativeDate.getUTCDate(),
hours: nativeDate.getUTCHours(),
minutes: nativeDate.getUTCMinutes(),
seconds: nativeDate.getUTCSeconds(),
};
for (let k in time) {
if (time[k].length < 2) {
time[k] = '0' + time[k];
}

if (timezone) {
date = getZonedTime(nativeDate, findTimeZone(timezone));
}
full += time.hours + ':' + time.minutes + ':' + time.seconds + ' UTC';

let full = String(date.day) + ' ' +
shortMonth(date.month) + ' ' +
String(date.year) + ' at ';

['hours', 'minutes', 'seconds'].forEach(key => {
date[key] = String(date[key]);
if (date[key].length < 2) {
date[key] = '0' + date[key];
}
});

full += date.hours + ':' + date.minutes + ':' + date.seconds + ' UTC';
return full;
}

Expand Down
2 changes: 2 additions & 0 deletions src/lib/ParseApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export default class ParseApp {
apiKey,
serverURL,
serverInfo,
timezone,
production,
iconName,
primaryBackgroundColor,
Expand All @@ -61,6 +62,7 @@ export default class ParseApp {
this.production = production;
this.serverURL = serverURL;
this.serverInfo = serverInfo;
this.timezone = timezone;
this.icon = iconName;
this.primaryBackgroundColor=primaryBackgroundColor;
this.secondaryBackgroundColor=secondaryBackgroundColor;
Expand Down