From 25fa6964fc3fb53b6af48dee5a4daf2e50c56e82 Mon Sep 17 00:00:00 2001
From: wood1986 <5212215+wood1986@users.noreply.github.com>
Date: Wed, 12 Jul 2023 04:47:14 -0700
Subject: [PATCH 1/7] feat: add timeZoneName props
---
.gitignore | 2 +
.../rndatetimepicker/Common.java | 93 +++-
.../rndatetimepicker/DatePickerModule.java | 74 +--
.../KeepDateInRangeListener.java | 65 ---
.../rndatetimepicker/RNConstants.java | 1 +
.../rndatetimepicker/RNDate.java | 12 +-
.../RNDatePickerDialogFragment.java | 63 +--
.../rndatetimepicker/TimePickerModule.java | 57 ++-
example/App.js | 461 ++++++++++--------
example/e2e/detoxTest.spec.js | 310 ++++++++++--
example/e2e/utils/actions.js | 2 +-
example/e2e/utils/matchers.js | 4 -
example/ios/Podfile.lock | 381 +--------------
ios/RNDateTimePicker.m | 2 +-
ios/RNDateTimePickerManager.m | 16 +-
ios/RNDateTimePickerShadowView.h | 2 +
ios/RNDateTimePickerShadowView.m | 27 +-
ios/fabric/RNDateTimePickerComponentView.mm | 63 ++-
jest/index.js | 13 +-
package.json | 1 +
src/DateTimePickerAndroid.android.js | 27 +-
src/androidUtils.js | 23 +-
src/datetimepicker.android.js | 2 +
src/datetimepicker.ios.js | 4 +-
src/index.d.ts | 10 +-
src/specs/NativeComponentDateTimePicker.js | 2 +
src/types.js | 19 +-
src/utils.js | 17 +-
yarn.lock | 9 +-
29 files changed, 838 insertions(+), 924 deletions(-)
delete mode 100644 android/src/main/java/com/reactcommunity/rndatetimepicker/KeepDateInRangeListener.java
diff --git a/.gitignore b/.gitignore
index 2145c4cf..964f0b81 100644
--- a/.gitignore
+++ b/.gitignore
@@ -59,3 +59,5 @@ buck-out/
# CocoaPods
example/ios/Pods/
+
+.xcode.env
\ No newline at end of file
diff --git a/android/src/main/java/com/reactcommunity/rndatetimepicker/Common.java b/android/src/main/java/com/reactcommunity/rndatetimepicker/Common.java
index b8c7acb3..665000cc 100644
--- a/android/src/main/java/com/reactcommunity/rndatetimepicker/Common.java
+++ b/android/src/main/java/com/reactcommunity/rndatetimepicker/Common.java
@@ -18,8 +18,15 @@
import androidx.fragment.app.FragmentManager;
import com.facebook.react.bridge.Promise;
+import com.facebook.react.bridge.ReadableMap;
+import com.facebook.react.util.RNLog;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.HashSet;
import java.util.Locale;
+import java.util.SimpleTimeZone;
+import java.util.TimeZone;
public class Common {
@@ -63,21 +70,18 @@ public static int getDefaultDialogButtonTextColor(@NonNull Context activity) {
@NonNull
public static DialogInterface.OnShowListener setButtonTextColor(@NonNull final Context activityContext, final AlertDialog dialog, final Bundle args, final boolean needsColorOverride) {
- return new DialogInterface.OnShowListener() {
- @Override
- public void onShow(DialogInterface dialogInterface) {
- // change text color only if custom color is set or if spinner mode is set
- // because spinner suffers from https://github.com/react-native-datetimepicker/datetimepicker/issues/543
-
- Button positiveButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
- Button negativeButton = dialog.getButton(AlertDialog.BUTTON_NEGATIVE);
- Button neutralButton = dialog.getButton(AlertDialog.BUTTON_NEUTRAL);
-
- int textColorPrimary = getDefaultDialogButtonTextColor(activityContext);
- setTextColor(positiveButton, POSITIVE, args, needsColorOverride, textColorPrimary);
- setTextColor(negativeButton, NEGATIVE, args, needsColorOverride, textColorPrimary);
- setTextColor(neutralButton, NEUTRAL, args, needsColorOverride, textColorPrimary);
- }
+ return dialogInterface -> {
+ // change text color only if custom color is set or if spinner mode is set
+ // because spinner suffers from https://github.com/react-native-datetimepicker/datetimepicker/issues/543
+
+ Button positiveButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
+ Button negativeButton = dialog.getButton(AlertDialog.BUTTON_NEGATIVE);
+ Button neutralButton = dialog.getButton(AlertDialog.BUTTON_NEUTRAL);
+
+ int textColorPrimary = getDefaultDialogButtonTextColor(activityContext);
+ setTextColor(positiveButton, POSITIVE, args, needsColorOverride, textColorPrimary);
+ setTextColor(negativeButton, NEGATIVE, args, needsColorOverride, textColorPrimary);
+ setTextColor(neutralButton, NEUTRAL, args, needsColorOverride, textColorPrimary);
};
}
@@ -139,4 +143,63 @@ private static void setButtonLabel(Bundle buttonConfig, AlertDialog dialog, int
}
dialog.setButton(whichButton, buttonConfig.getString(LABEL), listener);
}
+
+ public static TimeZone getTimeZone(Bundle args) {
+ if (args != null && args.containsKey(RNConstants.ARG_TZOFFSET_MINS)) {
+ return new SimpleTimeZone((int)args.getLong(RNConstants.ARG_TZOFFSET_MINS) * 60 * 1000, "GMT");
+ }
+
+ if (args != null && args.containsKey(RNConstants.ARG_TZ_NAME)) {
+ String timeZoneName = args.getString(RNConstants.ARG_TZ_NAME);
+ if ("GMT".equals(timeZoneName)) {
+ return TimeZone.getTimeZone("GMT");
+ } else if (!"GMT".equals(TimeZone.getTimeZone(timeZoneName).getID())) {
+ return TimeZone.getTimeZone(timeZoneName);
+ }
+ RNLog.w(null, "'" + timeZoneName + "' does not exist in TimeZone.getAvailableIDs() fallback to TimeZone.getDefault()=" + TimeZone.getDefault().getID());
+ }
+
+ return TimeZone.getDefault();
+ }
+
+ public static long maxDateWithTimeZone(Bundle args) {
+ if (!args.containsKey(RNConstants.ARG_MAXDATE)) {
+ return Long.MAX_VALUE;
+ }
+
+ Calendar maxDate = Calendar.getInstance(getTimeZone(args));
+ maxDate.setTimeInMillis(args.getLong(RNConstants.ARG_MAXDATE));
+ maxDate.set(Calendar.HOUR_OF_DAY, 23);
+ maxDate.set(Calendar.MINUTE, 59);
+ maxDate.set(Calendar.SECOND, 59);
+ maxDate.set(Calendar.MILLISECOND, 999);
+ return maxDate.getTimeInMillis();
+ }
+
+ public static long minDateWithTimeZone(Bundle args) {
+ if (!args.containsKey(RNConstants.ARG_MINDATE)) {
+ return 0;
+ }
+
+ Calendar minDate = Calendar.getInstance(getTimeZone(args));
+ minDate.setTimeInMillis(args.getLong(RNConstants.ARG_MINDATE));
+ minDate.set(Calendar.HOUR_OF_DAY, 0);
+ minDate.set(Calendar.MINUTE, 0);
+ minDate.set(Calendar.SECOND, 0);
+ minDate.set(Calendar.MILLISECOND, 0);
+ return minDate.getTimeInMillis();
+ }
+
+ public static Bundle createFragmentArguments(ReadableMap options) {
+ final Bundle args = new Bundle();
+
+ if (options.hasKey(RNConstants.ARG_VALUE) && !options.isNull(RNConstants.ARG_VALUE)) {
+ args.putLong(RNConstants.ARG_VALUE, (long) options.getDouble(RNConstants.ARG_VALUE));
+ }
+ if (options.hasKey(RNConstants.ARG_TZ_NAME) && !options.isNull(RNConstants.ARG_TZ_NAME)) {
+ args.putString(RNConstants.ARG_TZ_NAME, options.getString(RNConstants.ARG_TZ_NAME));
+ }
+
+ return args;
+ }
}
diff --git a/android/src/main/java/com/reactcommunity/rndatetimepicker/DatePickerModule.java b/android/src/main/java/com/reactcommunity/rndatetimepicker/DatePickerModule.java
index 881ffa4d..441f258c 100644
--- a/android/src/main/java/com/reactcommunity/rndatetimepicker/DatePickerModule.java
+++ b/android/src/main/java/com/reactcommunity/rndatetimepicker/DatePickerModule.java
@@ -22,8 +22,6 @@
import com.facebook.react.module.annotations.ReactModule;
import static com.reactcommunity.rndatetimepicker.Common.dismissDialog;
-import static com.reactcommunity.rndatetimepicker.KeepDateInRangeListener.isDateAfterMaxDate;
-import static com.reactcommunity.rndatetimepicker.KeepDateInRangeListener.isDateBeforeMinDate;
import java.util.Calendar;
@@ -41,8 +39,9 @@ public DatePickerModule(ReactApplicationContext reactContext) {
super(reactContext);
}
+ @NonNull
@Override
- public @NonNull String getName() {
+ public String getName() {
return NAME;
}
@@ -60,31 +59,15 @@ public DatePickerDialogListener(final Promise promise, Bundle arguments) {
@Override
public void onDateSet(DatePicker view, int year, int month, int day) {
if (!mPromiseResolved && getReactApplicationContext().hasActiveReactInstance()) {
+ final RNDate date = new RNDate(mArgs);
+ Calendar calendar = Calendar.getInstance(Common.getTimeZone(mArgs));
+ calendar.set(year, month, day, date.hour(), date.minute(), 0);
+ calendar.set(Calendar.MILLISECOND, 0);
+
WritableMap result = new WritableNativeMap();
result.putString("action", RNConstants.ACTION_DATE_SET);
- result.putInt("year", year);
- result.putInt("month", month);
- result.putInt("day", day);
-
- // https://issuetracker.google.com/issues/169602180
- // TODO revisit day, month, year with timezoneoffset fixes
- if (isDateAfterMaxDate(mArgs, year, month, day)) {
- Calendar maxDate = Calendar.getInstance();
- maxDate.setTimeInMillis(mArgs.getLong(RNConstants.ARG_MAXDATE));
-
- result.putInt("year", maxDate.get(Calendar.YEAR));
- result.putInt("month", maxDate.get(Calendar.MONTH) );
- result.putInt("day", maxDate.get(Calendar.DAY_OF_MONTH));
- }
-
- if (isDateBeforeMinDate(mArgs, year, month, day)) {
- Calendar minDate = Calendar.getInstance();
- minDate.setTimeInMillis(mArgs.getLong(RNConstants.ARG_MINDATE));
-
- result.putInt("year", minDate.get(Calendar.YEAR));
- result.putInt("month", minDate.get(Calendar.MONTH) );
- result.putInt("day", minDate.get(Calendar.DAY_OF_MONTH));
- }
+ result.putDouble("timestamp", calendar.getTimeInMillis());
+ result.putDouble("utcOffset", calendar.getTimeZone().getOffset(calendar.getTimeInMillis()) / 1000 / 60);
mPromise.resolve(result);
mPromiseResolved = true;
@@ -157,35 +140,32 @@ public void open(final ReadableMap options, final Promise promise) {
final FragmentManager fragmentManager = activity.getSupportFragmentManager();
- UiThreadUtil.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- RNDatePickerDialogFragment oldFragment =
- (RNDatePickerDialogFragment) fragmentManager.findFragmentByTag(NAME);
+ UiThreadUtil.runOnUiThread(() -> {
+ RNDatePickerDialogFragment oldFragment =
+ (RNDatePickerDialogFragment) fragmentManager.findFragmentByTag(NAME);
- if (oldFragment != null) {
- oldFragment.update(createFragmentArguments(options));
- return;
- }
+ Bundle arguments = createFragmentArguments(options);
- RNDatePickerDialogFragment fragment = new RNDatePickerDialogFragment();
+ if (oldFragment != null) {
+ oldFragment.update(arguments);
+ return;
+ }
- fragment.setArguments(createFragmentArguments(options));
+ RNDatePickerDialogFragment fragment = new RNDatePickerDialogFragment();
- final DatePickerDialogListener listener = new DatePickerDialogListener(promise, createFragmentArguments(options));
- fragment.setOnDismissListener(listener);
- fragment.setOnDateSetListener(listener);
- fragment.setOnNeutralButtonActionListener(listener);
- fragment.show(fragmentManager, NAME);
- }
+ fragment.setArguments(arguments);
+
+ final DatePickerDialogListener listener = new DatePickerDialogListener(promise, arguments);
+ fragment.setOnDismissListener(listener);
+ fragment.setOnDateSetListener(listener);
+ fragment.setOnNeutralButtonActionListener(listener);
+ fragment.show(fragmentManager, NAME);
});
}
private Bundle createFragmentArguments(ReadableMap options) {
- final Bundle args = new Bundle();
- if (options.hasKey(RNConstants.ARG_VALUE) && !options.isNull(RNConstants.ARG_VALUE)) {
- args.putLong(RNConstants.ARG_VALUE, (long) options.getDouble(RNConstants.ARG_VALUE));
- }
+ final Bundle args = Common.createFragmentArguments(options);
+
if (options.hasKey(RNConstants.ARG_MINDATE) && !options.isNull(RNConstants.ARG_MINDATE)) {
args.putLong(RNConstants.ARG_MINDATE, (long) options.getDouble(RNConstants.ARG_MINDATE));
}
diff --git a/android/src/main/java/com/reactcommunity/rndatetimepicker/KeepDateInRangeListener.java b/android/src/main/java/com/reactcommunity/rndatetimepicker/KeepDateInRangeListener.java
deleted file mode 100644
index 0da88bfe..00000000
--- a/android/src/main/java/com/reactcommunity/rndatetimepicker/KeepDateInRangeListener.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.reactcommunity.rndatetimepicker;
-
-import android.os.Bundle;
-import android.widget.DatePicker;
-
-import androidx.annotation.NonNull;
-
-import java.util.Calendar;
-
-// fix for https://issuetracker.google.com/issues/169602180
-// TODO revisit day, month, year with timezoneoffset fixes
-public class KeepDateInRangeListener implements DatePicker.OnDateChangedListener {
-
- private final Bundle args;
-
- public KeepDateInRangeListener(@NonNull Bundle args) {
- this.args = args;
- }
-
- @Override
- public void onDateChanged(DatePicker view, int year, int month, int day) {
- fixPotentialMaxDateBug(view, year, month, day);
- fixPotentialMinDateBug(view, year, month, day);
- }
-
- private void fixPotentialMaxDateBug(DatePicker datePicker, int year, int month, int day) {
- if (!isDateAfterMaxDate(args, year, month, day)) {
- return;
- }
- Calendar maxDate = Calendar.getInstance();
- maxDate.setTimeInMillis(args.getLong(RNConstants.ARG_MAXDATE));
- datePicker.updateDate(maxDate.get(Calendar.YEAR), maxDate.get(Calendar.MONTH), maxDate.get(Calendar.DAY_OF_MONTH));
- }
-
- private void fixPotentialMinDateBug(DatePicker datePicker, int year, int month, int day) {
- if (!isDateBeforeMinDate(args, year, month, day)) {
- return;
- }
- Calendar c = Calendar.getInstance();
- c.setTimeInMillis(args.getLong(RNConstants.ARG_MINDATE));
- datePicker.updateDate(c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH));
- }
-
- public static boolean isDateAfterMaxDate(Bundle args, int year, int month, int day) {
- if (!args.containsKey(RNConstants.ARG_MAXDATE)) {
- return false;
- }
- Calendar maxDate = Calendar.getInstance();
- maxDate.setTimeInMillis(args.getLong(RNConstants.ARG_MAXDATE));
- return (year > maxDate.get(Calendar.YEAR) ||
- (year == maxDate.get(Calendar.YEAR) && month > maxDate.get(Calendar.MONTH)) ||
- (year == maxDate.get(Calendar.YEAR) && month == maxDate.get(Calendar.MONTH) && day > maxDate.get(Calendar.DAY_OF_MONTH)));
- }
-
- public static boolean isDateBeforeMinDate(Bundle args, int year, int month, int day) {
- if (!args.containsKey(RNConstants.ARG_MINDATE)) {
- return false;
- }
- Calendar minDate = Calendar.getInstance();
- minDate.setTimeInMillis(args.getLong(RNConstants.ARG_MINDATE));
- return (year < minDate.get(Calendar.YEAR) ||
- (year == minDate.get(Calendar.YEAR) && month < minDate.get(Calendar.MONTH)) ||
- (year == minDate.get(Calendar.YEAR) && month == minDate.get(Calendar.MONTH) && day < minDate.get(Calendar.DAY_OF_MONTH)));
- }
-}
diff --git a/android/src/main/java/com/reactcommunity/rndatetimepicker/RNConstants.java b/android/src/main/java/com/reactcommunity/rndatetimepicker/RNConstants.java
index 291cb1ab..99530d51 100644
--- a/android/src/main/java/com/reactcommunity/rndatetimepicker/RNConstants.java
+++ b/android/src/main/java/com/reactcommunity/rndatetimepicker/RNConstants.java
@@ -12,6 +12,7 @@ public final class RNConstants {
public static final String ARG_DISPLAY = "display";
public static final String ARG_DIALOG_BUTTONS = "dialogButtons";
public static final String ARG_TZOFFSET_MINS = "timeZoneOffsetInMinutes";
+ public static final String ARG_TZ_NAME = "timeZoneName";
public static final String ARG_TESTID = "testID";
public static final String ACTION_DATE_SET = "dateSetAction";
public static final String ACTION_TIME_SET = "timeSetAction";
diff --git a/android/src/main/java/com/reactcommunity/rndatetimepicker/RNDate.java b/android/src/main/java/com/reactcommunity/rndatetimepicker/RNDate.java
index 7cbdac72..137869a9 100644
--- a/android/src/main/java/com/reactcommunity/rndatetimepicker/RNDate.java
+++ b/android/src/main/java/com/reactcommunity/rndatetimepicker/RNDate.java
@@ -11,19 +11,11 @@ public RNDate(Bundle args) {
now = Calendar.getInstance();
if (args != null && args.containsKey(RNConstants.ARG_VALUE)) {
- set(args.getLong(RNConstants.ARG_VALUE));
+ now.setTimeInMillis((args.getLong(RNConstants.ARG_VALUE)));
}
- if (args != null && args.containsKey(RNConstants.ARG_TZOFFSET_MINS)) {
- now.setTimeZone(TimeZone.getTimeZone("GMT"));
- Long timeZoneOffsetInMinutesFallback = args.getLong(RNConstants.ARG_TZOFFSET_MINS);
- Integer timeZoneOffsetInMinutes = args.getInt(RNConstants.ARG_TZOFFSET_MINS, timeZoneOffsetInMinutesFallback.intValue());
- now.add(Calendar.MILLISECOND, timeZoneOffsetInMinutes * 60000);
- }
- }
- public void set(long value) {
- now.setTimeInMillis(value);
+ now.setTimeZone(Common.getTimeZone(args));
}
public int year() { return now.get(Calendar.YEAR); }
diff --git a/android/src/main/java/com/reactcommunity/rndatetimepicker/RNDatePickerDialogFragment.java b/android/src/main/java/com/reactcommunity/rndatetimepicker/RNDatePickerDialogFragment.java
index 735e578c..fd55a382 100644
--- a/android/src/main/java/com/reactcommunity/rndatetimepicker/RNDatePickerDialogFragment.java
+++ b/android/src/main/java/com/reactcommunity/rndatetimepicker/RNDatePickerDialogFragment.java
@@ -30,7 +30,6 @@
import java.util.Calendar;
import java.util.Locale;
-import java.util.TimeZone;
@SuppressLint("ValidFragment")
public class RNDatePickerDialogFragment extends DialogFragment {
@@ -95,8 +94,6 @@ DatePickerDialog getDialog(
private DatePickerDialog createDialog(Bundle args) {
Context activityContext = getActivity();
- final Calendar c = Calendar.getInstance();
-
DatePickerDialog dialog = getDialog(args, activityContext, mOnDateSetListener);
if (args != null) {
@@ -109,40 +106,27 @@ private DatePickerDialog createDialog(Bundle args) {
}
final DatePicker datePicker = dialog.getDatePicker();
+ final long minDate = Common.minDateWithTimeZone(args);
+ final long maxDate = Common.maxDateWithTimeZone(args);
- Integer timeZoneOffsetInMilliseconds = getTimeZoneOffset(args);
- if (timeZoneOffsetInMilliseconds != null) {
- c.setTimeZone(TimeZone.getTimeZone("GMT"));
- }
-
- if (args != null && args.containsKey(RNConstants.ARG_MINDATE)) {
- // Set minDate to the beginning of the day. We need this because of clowniness in datepicker
- // that causes it to throw an exception if minDate is greater than the internal timestamp
- // that it generates from the y/m/d passed in the constructor.
- c.setTimeInMillis(args.getLong(RNConstants.ARG_MINDATE));
- c.set(Calendar.HOUR_OF_DAY, 0);
- c.set(Calendar.MINUTE, 0);
- c.set(Calendar.SECOND, 0);
- c.set(Calendar.MILLISECOND, 0);
- datePicker.setMinDate(c.getTimeInMillis() - getOffset(c, timeZoneOffsetInMilliseconds));
- } else {
- // This is to work around a bug in DatePickerDialog where it doesn't display a title showing
- // the date under certain conditions.
- datePicker.setMinDate(RNConstants.DEFAULT_MIN_DATE);
+ if (args.containsKey(RNConstants.ARG_MINDATE)) {
+ datePicker.setMinDate(minDate);
}
- if (args != null && args.containsKey(RNConstants.ARG_MAXDATE)) {
- // Set maxDate to the end of the day, same reason as for minDate.
- c.setTimeInMillis(args.getLong(RNConstants.ARG_MAXDATE));
- c.set(Calendar.HOUR_OF_DAY, 23);
- c.set(Calendar.MINUTE, 59);
- c.set(Calendar.SECOND, 59);
- c.set(Calendar.MILLISECOND, 999);
- datePicker.setMaxDate(c.getTimeInMillis() - getOffset(c, timeZoneOffsetInMilliseconds));
+ if (args.containsKey(RNConstants.ARG_MAXDATE)) {
+ datePicker.setMaxDate(maxDate);
}
if (args != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
&& (args.containsKey(RNConstants.ARG_MAXDATE) || args.containsKey(RNConstants.ARG_MINDATE))) {
- datePicker.setOnDateChangedListener(new KeepDateInRangeListener(args));
+ datePicker.setOnDateChangedListener((view, year, monthOfYear, dayOfMonth) -> {
+ Calendar calendar = Calendar.getInstance(Common.getTimeZone(args));
+ calendar.set(year, monthOfYear, dayOfMonth, 0, 0, 0);
+ long timestamp = Math.min(Math.max(calendar.getTimeInMillis(), minDate), maxDate);
+ calendar.setTimeInMillis(timestamp);
+ if (datePicker.getYear() != calendar.get(Calendar.YEAR) || datePicker.getMonth() != calendar.get(Calendar.MONTH) || datePicker.getDayOfMonth() != calendar.get(Calendar.DAY_OF_MONTH)) {
+ datePicker.updateDate(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH));
+ }
+ });
}
if (args != null && args.containsKey(RNConstants.ARG_TESTID)) {
@@ -152,23 +136,6 @@ private DatePickerDialog createDialog(Bundle args) {
return dialog;
}
- private static Integer getTimeZoneOffset(Bundle args) {
- if (args != null && args.containsKey(RNConstants.ARG_TZOFFSET_MINS)) {
- long timeZoneOffsetInMinutesFallback = args.getLong(RNConstants.ARG_TZOFFSET_MINS);
- int timeZoneOffsetInMinutes = args.getInt(RNConstants.ARG_TZOFFSET_MINS, (int) timeZoneOffsetInMinutesFallback);
- return timeZoneOffsetInMinutes * 60000;
- }
-
- return null;
- }
-
- private static int getOffset(Calendar c, Integer timeZoneOffsetInMilliseconds) {
- if (timeZoneOffsetInMilliseconds != null) {
- return TimeZone.getDefault().getOffset(c.getTimeInMillis()) - timeZoneOffsetInMilliseconds;
- }
- return 0;
- }
-
@Override
public void onDismiss(@NonNull DialogInterface dialog) {
super.onDismiss(dialog);
diff --git a/android/src/main/java/com/reactcommunity/rndatetimepicker/TimePickerModule.java b/android/src/main/java/com/reactcommunity/rndatetimepicker/TimePickerModule.java
index f948d7ad..034df515 100644
--- a/android/src/main/java/com/reactcommunity/rndatetimepicker/TimePickerModule.java
+++ b/android/src/main/java/com/reactcommunity/rndatetimepicker/TimePickerModule.java
@@ -25,6 +25,8 @@
import static com.reactcommunity.rndatetimepicker.Common.dismissDialog;
+import java.util.Calendar;
+
/**
* {@link NativeModule} that allows JS to show a native time picker dialog and get called back when
* the user selects a time.
@@ -47,19 +49,27 @@ public String getName() {
private class TimePickerDialogListener implements OnTimeSetListener, OnDismissListener, OnClickListener {
private final Promise mPromise;
+ private final Bundle mArgs;
private boolean mPromiseResolved = false;
- public TimePickerDialogListener(Promise promise) {
+ public TimePickerDialogListener(Promise promise, Bundle arguments) {
mPromise = promise;
+ mArgs = arguments;
}
@Override
public void onTimeSet(TimePicker view, int hour, int minute) {
if (!mPromiseResolved && getReactApplicationContext().hasActiveReactInstance()) {
+ final RNDate date = new RNDate(mArgs);
+ Calendar calendar = Calendar.getInstance(Common.getTimeZone(mArgs));
+ calendar.set(date.year(), date.month(), date.day(), hour, minute, 0);
+ calendar.set(Calendar.MILLISECOND, 0);
+
WritableMap result = new WritableNativeMap();
result.putString("action", RNConstants.ACTION_TIME_SET);
- result.putInt("hour", hour);
- result.putInt("minute", minute);
+ result.putDouble("timestamp", calendar.getTimeInMillis());
+ result.putDouble("utcOffset", calendar.getTimeZone().getOffset(calendar.getTimeInMillis()) / 1000 / 60);
+
mPromise.resolve(result);
mPromiseResolved = true;
}
@@ -105,35 +115,32 @@ public void open(final ReadableMap options, final Promise promise) {
// (for apps that use it for legacy reasons). This unfortunately leads to some code duplication.
final FragmentManager fragmentManager = activity.getSupportFragmentManager();
- UiThreadUtil.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- RNTimePickerDialogFragment oldFragment =
- (RNTimePickerDialogFragment) fragmentManager.findFragmentByTag(NAME);
+ UiThreadUtil.runOnUiThread(() -> {
+ RNTimePickerDialogFragment oldFragment =
+ (RNTimePickerDialogFragment) fragmentManager.findFragmentByTag(NAME);
+
+ Bundle arguments = createFragmentArguments(options);
- if (oldFragment != null) {
- oldFragment.update(createFragmentArguments(options));
- return;
- }
+ if (oldFragment != null) {
+ oldFragment.update(arguments);
+ return;
+ }
- RNTimePickerDialogFragment fragment = new RNTimePickerDialogFragment();
+ RNTimePickerDialogFragment fragment = new RNTimePickerDialogFragment();
- fragment.setArguments(createFragmentArguments(options));
+ fragment.setArguments(arguments);
- final TimePickerDialogListener listener = new TimePickerDialogListener(promise);
- fragment.setOnDismissListener(listener);
- fragment.setOnTimeSetListener(listener);
- fragment.setOnNeutralButtonActionListener(listener);
- fragment.show(fragmentManager, NAME);
- }
+ final TimePickerDialogListener listener = new TimePickerDialogListener(promise, arguments);
+ fragment.setOnDismissListener(listener);
+ fragment.setOnTimeSetListener(listener);
+ fragment.setOnNeutralButtonActionListener(listener);
+ fragment.show(fragmentManager, NAME);
});
}
private Bundle createFragmentArguments(ReadableMap options) {
- final Bundle args = new Bundle();
- if (options.hasKey(RNConstants.ARG_VALUE) && !options.isNull(RNConstants.ARG_VALUE)) {
- args.putLong(RNConstants.ARG_VALUE, (long) options.getDouble(RNConstants.ARG_VALUE));
- }
+ final Bundle args = Common.createFragmentArguments(options);
+
if (options.hasKey(RNConstants.ARG_IS24HOUR) && !options.isNull(RNConstants.ARG_IS24HOUR)) {
args.putBoolean(RNConstants.ARG_IS24HOUR, options.getBoolean(RNConstants.ARG_IS24HOUR));
}
@@ -147,7 +154,7 @@ private Bundle createFragmentArguments(ReadableMap options) {
args.putInt(RNConstants.ARG_INTERVAL, options.getInt(RNConstants.ARG_INTERVAL));
}
if (options.hasKey(RNConstants.ARG_TZOFFSET_MINS) && !options.isNull(RNConstants.ARG_TZOFFSET_MINS)) {
- args.putInt(RNConstants.ARG_TZOFFSET_MINS, options.getInt(RNConstants.ARG_TZOFFSET_MINS));
+ args.putLong(RNConstants.ARG_TZOFFSET_MINS, (long) options.getDouble(RNConstants.ARG_TZOFFSET_MINS));
}
return args;
}
diff --git a/example/App.js b/example/App.js
index 95ce3de6..4cad63de 100644
--- a/example/App.js
+++ b/example/App.js
@@ -11,13 +11,14 @@ import {
useColorScheme,
Switch,
Alert,
+ FlatList,
} from 'react-native';
import DateTimePicker from '@react-native-community/datetimepicker';
import SegmentedControl from './SegmentedControl';
import {Colors} from 'react-native/Libraries/NewAppScreen';
import React, {useRef, useState} from 'react';
import {Picker} from 'react-native-windows';
-import moment from 'moment';
+import moment from 'moment-timezone';
import {
ANDROID_MODE,
DAY_OF_WEEK,
@@ -25,7 +26,22 @@ import {
ANDROID_DISPLAY,
IOS_DISPLAY,
} from '../src/constants';
-// import * as RNLocalize from 'react-native-localize';
+import * as RNLocalize from 'react-native-localize';
+
+const timezone = [
+ 120,
+ 0,
+ -120,
+ undefined,
+ 'America/New_York',
+ 'America/Vancouver',
+ 'Europe/London',
+ 'Europe/Istanbul',
+ 'Asia/Hong_Kong',
+ 'Australia/Brisbane',
+ 'Australia/Sydney',
+ 'Australia/Adelaide',
+];
const ThemedText = (props) => {
const isDarkMode = useColorScheme() === 'dark';
@@ -49,6 +65,17 @@ const ThemedTextInput = (props) => {
});
};
+const Info = ({testID, title, body}) => {
+ return (
+
+ {title}
+
+ {body}
+
+
+ );
+};
+
const MODE_VALUES = Platform.select({
ios: Object.values(IOS_MODE),
android: Object.values(ANDROID_MODE),
@@ -63,10 +90,11 @@ const MINUTE_INTERVALS = [1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30];
export const App = () => {
// Sat, 13 Nov 2021 10:00:00 GMT (local: Saturday, November 13, 2021 11:00:00 AM GMT+01:00)
- const sourceMoment = moment.unix(1636797600);
+ const sourceMoment = moment.unix(1636765200);
const sourceDate = sourceMoment.local().toDate();
const [date, setDate] = useState(sourceDate);
const [tzOffsetInMinutes, setTzOffsetInMinutes] = useState(undefined);
+ const [tzName, setTzName] = useState(RNLocalize.getTimeZone());
const [mode, setMode] = useState(MODE_VALUES[0]);
const [show, setShow] = useState(false);
const [textColor, setTextColor] = useState();
@@ -80,8 +108,8 @@ export const App = () => {
// Windows-specific
const [time, setTime] = useState(undefined);
- const [maxDate, setMinDate] = useState(new Date('2021'));
- const [minDate, setMaxDate] = useState(new Date('2018'));
+ const [maxDate] = useState(new Date('2021'));
+ const [minDate] = useState(new Date('2018'));
const [is24Hours, set24Hours] = useState(false);
const [firstDayOfWeek, setFirstDayOfWeek] = useState(DAY_OF_WEEK.Monday);
const [dateFormat, setDateFormat] = useState('longdate');
@@ -132,8 +160,29 @@ export const App = () => {
backgroundColor: isDarkMode ? Colors.dark : Colors.lighter,
};
+ const renderItem = ({item}) => {
+ const isNumber = !isNaN(item);
+ const title = isNumber
+ ? item > 0
+ ? `+${item} mins`
+ : `${item} mins`
+ : item;
+ return (
+
+
+ );
+ };
+
const toggleMinMaxDateInUTC = () => {
setTzOffsetInMinutes(0);
+ setTzName(undefined);
const startOfTodayUTC = sourceMoment.utc().startOf('day').toDate();
setMinimumDate(maximumDate ? undefined : startOfTodayUTC);
@@ -147,8 +196,72 @@ export const App = () => {
if (Platform.OS !== 'windows') {
return (
-
-
+
+
+ {global.HermesInternal != null && (
+
+
+ Engine: Hermes
+
+
+ )}
+
+
+ Example DateTime Picker
+
+
+
+
+
+ {(tzName || !isNaN(tzOffsetInMinutes)) && (
+ <>
+ {
+ if (tzName) {
+ return moment(date).tz(tzName).format();
+ }
+ if (tzOffsetInMinutes !== undefined) {
+ return moment(date).utcOffset(tzOffsetInMinutes).format();
+ }
+ return '';
+ })()}
+ />
+ {
+ if (tzName) {
+ return 'Overridden TzName:';
+ }
+ if (tzOffsetInMinutes !== undefined) {
+ return 'Overridden TzOffset:';
+ }
+ return '';
+ })()}
+ body={tzName || `${tzOffsetInMinutes} mins`}
+ />
+ >
+ )}
+
+
{
scrollRef.current?.scrollToEnd({animated: true});
}
}}>
- {global.HermesInternal != null && (
-
-
- Engine: Hermes
-
-
- )}
-
-
-
- Example DateTime Picker
-
-
-
-
- {/*TZ: {RNLocalize.getTimeZone()}, original:{' '}*/}
- {moment(sourceDate).format('MM/DD/YYYY HH:mm')}
-
-
- , TZOffset:{new Date().getTimezoneOffset() / 60}
-
+ mode prop:
+ {
+ setMode(MODE_VALUES[event.nativeEvent.selectedSegmentIndex]);
+ }}
+ />
+ display prop:
+ {
+ setDisplay(
+ DISPLAY_VALUES[event.nativeEvent.selectedSegmentIndex],
+ );
+ }}
+ />
+ minute interval prop:
+ {
+ setMinInterval(
+ MINUTE_INTERVALS[event.nativeEvent.selectedSegmentIndex],
+ );
+ }}
+ />
+
+
+ text color (iOS only)
+
+ {
+ setTextColor(text.toLowerCase());
+ }}
+ placeholder="textColor"
+ />
+
+
+
+ accent color (iOS only)
+
+ {
+ setAccentColor(text.toLowerCase());
+ }}
+ placeholder="accentColor"
+ />
+
+
+
+ disabled (iOS only)
+
+
+
- mode prop:
- {
- setMode(MODE_VALUES[event.nativeEvent.selectedSegmentIndex]);
+
+
+
+ neutralButtonLabel (android only)
+
+
+
+
+
+ [android] show and dismiss picker after 3 secs
+
+
+
+
+
+
+
+
+ {
+ toggleMinMaxDateInUTC();
+ setShow(true);
}}
+ title="toggleMinMaxDate"
/>
-
-
- text color (iOS only)
-
- {
- setTextColor(text.toLowerCase());
- }}
- placeholder="textColor"
- />
-
-
-
- accent color (iOS only)
-
- {
- setAccentColor(text.toLowerCase());
- }}
- placeholder="accentColor"
- />
-
-
-
- disabled (iOS only)
-
-
-
-
-
- neutralButtonLabel (android only)
-
-
-
-
-
- [android] show and dismiss picker after 3 secs
-
-
-
- {
- setShow(true);
- setTimeout(() => {
- setShow(false);
- }, 6000);
- }}
- title="Show and dismiss picker!"
- />
-
-
- {
- setShow(true);
- }}
- title="Show picker!"
- />
- setShow(false)}
- title="Hide picker!"
- />
-
-
-
- {moment(date).format('MM/DD/YYYY')}
-
-
-
- {moment(date).format('HH:mm')}
-
-
-
- tzOffset: {tzOffsetInMinutes ?? 'auto'}
-
-
-
- {
- setTzOffsetInMinutes(0);
- }}
- title="setTzOffsetInMinutes to 0"
- />
-
-
- {
- setTzOffsetInMinutes(120);
- }}
- title="setTzOffsetInMinutes to 120"
- />
-
-
- {
- toggleMinMaxDateInUTC();
- setShow(true);
- }}
- title="toggleMinMaxDate"
+
+
+ {/* This label ensures there is no regression in this former bug: https://github.com/react-native-datetimepicker/datetimepicker/issues/409 */}
+
+ This is a very very very very very very long text to showcase
+ behavior
+
+ {show && (
+
-
-
- {/* This label ensures there is no regression in this former bug: https://github.com/react-native-datetimepicker/datetimepicker/issues/409 */}
-
- This is a very very very very very very long text to showcase
- behavior
-
- {show && (
-
- )}
-
+ )}
diff --git a/example/e2e/detoxTest.spec.js b/example/e2e/detoxTest.spec.js
index 2dd494c0..f43d69d4 100644
--- a/example/e2e/detoxTest.spec.js
+++ b/example/e2e/detoxTest.spec.js
@@ -1,6 +1,4 @@
const {
- getTimeText,
- getDateText,
elementById,
elementByText,
getDateTimePickerIOS,
@@ -15,7 +13,7 @@ const {
userTapsOkButtonAndroid,
userDismissesCompactDatePicker,
} = require('./utils/actions');
-const {isIOS, wait, Platform} = require('./utils/utils');
+const {isIOS, isAndroid, wait, Platform} = require('./utils/utils');
const {device} = require('detox');
const {describe} = require('jest-circus');
@@ -41,9 +39,17 @@ describe('e2e tests', () => {
.withTimeout(5000);
});
- it.skip('timeInfo heading has expected content', async () => {
- await expect(elementById('timeInfo')).toHaveText(
- 'TZ: Europe/Prague, original: 11/13/2021 11:00',
+ it('timeInfo heading has expected content', async () => {
+ await expect(element(by.id('utcTime'))).toHaveText('2021-11-13T01:00:00Z');
+ await expect(element(by.id('deviceTime'))).toHaveText(
+ '2021-11-13T02:00:00+01:00',
+ );
+ await expect(element(by.id('deviceTzName'))).toHaveText('Europe/Prague');
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '2021-11-13T02:00:00+01:00',
+ );
+ await expect(element(by.id('overriddenTzName'))).toHaveText(
+ 'Europe/Prague',
);
});
@@ -82,7 +88,13 @@ describe('e2e tests', () => {
}
await elementByText('great').tap();
- await expect(getDateText()).toHaveText('11/13/2021');
+ await expect(element(by.id('utcTime'))).toHaveText('2021-11-13T01:00:00Z');
+ await expect(element(by.id('deviceTime'))).toHaveText(
+ '2021-11-13T02:00:00+01:00',
+ );
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '2021-11-13T02:00:00+01:00',
+ );
});
it('should update dateTimeText when date changes', async () => {
@@ -90,7 +102,7 @@ describe('e2e tests', () => {
if (isIOS()) {
const testElement = getDateTimePickerControlIOS();
- await testElement.setDatePickerDate('2021-11-02', 'yyyy-MM-dd');
+ await testElement.setDatePickerDate('2021-11-02T01:00:00Z', 'ISO8601');
} else {
const uiDevice = device.getUiDevice();
const focusSecondOfNovemberInCalendar = async () => {
@@ -104,7 +116,14 @@ describe('e2e tests', () => {
await userTapsOkButtonAndroid();
}
- await expect(getDateText()).toHaveText('11/02/2021');
+
+ await expect(element(by.id('utcTime'))).toHaveText('2021-11-02T01:00:00Z');
+ await expect(element(by.id('deviceTime'))).toHaveText(
+ '2021-11-02T02:00:00+01:00',
+ );
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '2021-11-02T02:00:00+01:00',
+ );
});
it('should show time picker after tapping timePicker button', async () => {
@@ -131,7 +150,13 @@ describe('e2e tests', () => {
await userTapsCancelButtonAndroid();
await elementByText('great').tap();
}
- await expect(getTimeText()).toHaveText('11:00');
+ await expect(element(by.id('utcTime'))).toHaveText('2021-11-13T01:00:00Z');
+ await expect(element(by.id('deviceTime'))).toHaveText(
+ '2021-11-13T02:00:00+01:00',
+ );
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '2021-11-13T02:00:00+01:00',
+ );
});
it('should change time text when time changes', async () => {
@@ -140,47 +165,168 @@ describe('e2e tests', () => {
if (isIOS()) {
const testElement = getDateTimePickerControlIOS();
// TODO
- await testElement.setDatePickerDate('15:44', 'HH:mm');
+ await testElement.setDatePickerDate('2021-11-13T14:44:00Z', 'ISO8601');
} else {
await userChangesTimeValue({hours: 15, minutes: 44});
await userTapsOkButtonAndroid();
}
- await expect(getTimeText()).toHaveText('15:44');
+
+ await expect(element(by.id('utcTime'))).toHaveText('2021-11-13T14:44:00Z');
+ await expect(element(by.id('deviceTime'))).toHaveText(
+ '2021-11-13T15:44:00+01:00',
+ );
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '2021-11-13T15:44:00+01:00',
+ );
+ });
+
+ describe('IANA time zone', () => {
+ it('should show utcTime, deviceTime, overriddenTime correctly', async () => {
+ await expect(element(by.id('utcTime'))).toHaveText(
+ '2021-11-13T01:00:00Z',
+ );
+ await expect(element(by.id('deviceTime'))).toHaveText(
+ '2021-11-13T02:00:00+01:00',
+ );
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '2021-11-13T02:00:00+01:00',
+ );
+
+ await expect(element(by.id('deviceTzName'))).toHaveText('Europe/Prague');
+ await expect(element(by.id('overriddenTzName'))).toHaveText(
+ 'Europe/Prague',
+ );
+
+ await element(by.id('timezone')).swipe('left', 'fast', 0.5);
+
+ let timeZone = 'America/Vancouver';
+ if (isAndroid()) {
+ timeZone = timeZone.toUpperCase();
+ }
+
+ await waitFor(elementByText(timeZone)).toBeVisible().withTimeout(1000);
+
+ await elementByText(timeZone).tap();
+
+ await expect(element(by.id('utcTime'))).toHaveText(
+ '2021-11-13T01:00:00Z',
+ );
+ await expect(element(by.id('deviceTime'))).toHaveText(
+ '2021-11-13T02:00:00+01:00',
+ );
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '2021-11-12T17:00:00-08:00',
+ );
+ await expect(element(by.id('deviceTzName'))).toHaveText('Europe/Prague');
+ await expect(element(by.id('overriddenTzName'))).toHaveText(
+ 'America/Vancouver',
+ );
+ });
+
+ it('daylight saving should work properly', async () => {
+ await element(by.id('timezone')).swipe('left', 'fast', 0.5);
+
+ let timeZone = 'America/Vancouver';
+ if (isAndroid()) {
+ timeZone = timeZone.toUpperCase();
+ }
+
+ await waitFor(elementByText(timeZone)).toBeVisible().withTimeout(1000);
+
+ await elementByText(timeZone).tap();
+
+ await userOpensPicker({mode: 'date', display: getPickerDisplay()});
+
+ if (isIOS()) {
+ const testElement = getDateTimePickerControlIOS();
+
+ await testElement.setDatePickerDate('2021-03-14T10:00:00Z', 'ISO8601');
+ } else {
+ const uiDevice = device.getUiDevice();
+
+ // Ensure you can't select yesterday (Android)
+ const focusFourteenthOfMarchInCalendar = async () => {
+ for (let i = 0; i < 3; i++) {
+ await uiDevice.pressDPadDown();
+ }
+
+ await uiDevice.pressDPadUp();
+
+ for (let i = 0; i < 8; i++) {
+ await uiDevice.pressEnter();
+ }
+
+ for (let i = 0; i < 2; i++) {
+ await uiDevice.pressDPadDown();
+ }
+ };
+ await focusFourteenthOfMarchInCalendar();
+ await uiDevice.pressEnter();
+ await userTapsOkButtonAndroid();
+
+ await userOpensPicker({mode: 'time', display: getPickerDisplay()});
+ await userChangesTimeValue({hours: '2', minutes: '0'});
+ await userTapsOkButtonAndroid();
+ }
+
+ await expect(element(by.id('utcTime'))).toHaveText(
+ '2021-03-14T10:00:00Z',
+ );
+ await expect(element(by.id('deviceTime'))).toHaveText(
+ '2021-03-14T11:00:00+01:00',
+ );
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '2021-03-14T03:00:00-07:00',
+ );
+ });
});
describe('time zone offset', () => {
- it.skip('should update dateTimeText when date changes and set setTzOffsetInMinutes to 0', async () => {
- // skip for now, there is a bug on android https://github.com/react-native-datetimepicker/datetimepicker/issues/528
- await expect(getDateText()).toHaveText('11/13/2021');
- await expect(getTimeText()).toHaveText('11:00');
+ it('should update dateTimeText when date changes and set setTzOffsetInMinutes to 0', async () => {
+ await expect(element(by.id('utcTime'))).toHaveText(
+ '2021-11-13T01:00:00Z',
+ );
+ await expect(element(by.id('deviceTime'))).toHaveText(
+ '2021-11-13T02:00:00+01:00',
+ );
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '2021-11-13T02:00:00+01:00',
+ );
+
+ let tzOffsetPreset = '0 mins';
+
if (isIOS()) {
await userOpensPicker({
mode: 'date',
display: 'spinner',
- tzOffsetPreset: 'setTzOffsetToZero',
+ tzOffsetPreset,
});
- const testElement = getDateTimePickerIOS();
- await testElement.setColumnToValue(0, 'November');
- await testElement.setColumnToValue(1, '14');
- await testElement.setColumnToValue(2, '2021');
} else {
+ tzOffsetPreset = tzOffsetPreset.toUpperCase();
await userOpensPicker({
mode: 'date',
display: 'default',
- tzOffsetPreset: 'setTzOffsetToZero',
+ tzOffsetPreset,
});
await userTapsOkButtonAndroid();
}
- await expect(getDateText()).toHaveText('11/14/2021');
- await expect(getTimeText()).toHaveText('11:00');
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '2021-11-13T01:00:00Z',
+ );
});
it('setTz should change time text when setTzOffsetInMinutes is 120 minutes', async () => {
await elementById('DateTimePickerScrollView').scrollTo('bottom');
+
+ let tzOffsetPreset = '+120 mins';
+ if (isAndroid()) {
+ tzOffsetPreset = tzOffsetPreset.toUpperCase();
+ }
+
await userOpensPicker({
mode: 'time',
display: getPickerDisplay(),
- tzOffsetPreset: 'setTzOffset',
+ tzOffsetPreset,
});
if (isIOS()) {
@@ -192,7 +338,15 @@ describe('e2e tests', () => {
await userChangesTimeValue({hours: '7', minutes: '30'});
await userTapsOkButtonAndroid();
}
- await expect(getTimeText()).toHaveText('06:30');
+ await expect(element(by.id('utcTime'))).toHaveText(
+ '2021-11-13T05:30:00Z',
+ );
+ await expect(element(by.id('deviceTime'))).toHaveText(
+ '2021-11-13T06:30:00+01:00',
+ );
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '2021-11-13T07:30:00+02:00',
+ );
});
it('should let you pick tomorrow but not yesterday when setting min/max', async () => {
@@ -203,12 +357,15 @@ describe('e2e tests', () => {
const testElement = getDateTimePickerControlIOS();
// Ensure you can't select yesterday (iOS)
- await testElement.setDatePickerDate('2021-11-12', 'yyyy-MM-dd');
- await expect(getDateText()).toHaveText('11/13/2021');
+ await testElement.setDatePickerDate('2021-11-12T01:00:00Z', 'ISO8601');
+
+ await expect(element(by.id('utcTime'))).toHaveText(
+ '2021-11-13T00:00:00Z',
+ );
// Ensure you can select tomorrow (iOS)
await userOpensPicker({mode: 'date', display: getPickerDisplay()});
- await testElement.setDatePickerDate('2021-11-14', 'yyyy-MM-dd');
+ await testElement.setDatePickerDate('2021-11-14T01:00:00Z', 'ISO8601');
} else {
const uiDevice = device.getUiDevice();
@@ -218,13 +375,22 @@ describe('e2e tests', () => {
await uiDevice.pressDPadDown();
}
for (let i = 0; i < 3; i++) {
- await uiDevice.pressDPadLeft();
+ await uiDevice.pressDPadRight();
}
};
await focusTwelethOfNovemberInCalendar();
await uiDevice.pressEnter();
await userTapsOkButtonAndroid();
- await expect(getDateText()).toHaveText('11/13/2021');
+
+ await expect(element(by.id('utcTime'))).toHaveText(
+ '2021-11-13T01:00:00Z',
+ );
+ await expect(element(by.id('deviceTime'))).toHaveText(
+ '2021-11-13T02:00:00+01:00',
+ );
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '2021-11-13T01:00:00Z',
+ );
// Ensure you can select tomorrow (Android)
await userOpensPicker({mode: 'date', display: getPickerDisplay()});
@@ -241,7 +407,15 @@ describe('e2e tests', () => {
await userTapsOkButtonAndroid();
}
- await expect(getDateText()).toHaveText('11/14/2021');
+ await expect(element(by.id('utcTime'))).toHaveText(
+ '2021-11-14T01:00:00Z',
+ );
+ await expect(element(by.id('deviceTime'))).toHaveText(
+ '2021-11-14T02:00:00+01:00',
+ );
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '2021-11-14T01:00:00Z',
+ );
});
});
@@ -250,8 +424,13 @@ describe('e2e tests', () => {
await userOpensPicker({mode: 'time', display: 'default'});
await elementByText('clear').tap();
- const dateText = getDateText();
- await expect(dateText).toHaveText('01/01/1970');
+ await expect(element(by.id('utcTime'))).toHaveText('1970-01-01T00:00:00Z');
+ await expect(element(by.id('deviceTime'))).toHaveText(
+ '1970-01-01T01:00:00+01:00',
+ );
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '1970-01-01T01:00:00+01:00',
+ );
});
it(':android: when component unmounts, dialog is dismissed', async () => {
@@ -264,23 +443,33 @@ describe('e2e tests', () => {
describe('given 5-minute interval', () => {
it(':android: clock picker should correct 18-minute selection to 20-minute one', async () => {
- try {
- await userOpensPicker({mode: 'time', display: 'clock', interval: 5});
+ await userOpensPicker({mode: 'time', display: 'clock', interval: 5});
- await userChangesTimeValue({hours: '23', minutes: '18'});
+ await userChangesTimeValue({hours: '23', minutes: '18'});
- await userTapsOkButtonAndroid();
+ await userTapsOkButtonAndroid();
- await expect(getTimeText()).toHaveText('23:20');
- } catch (err) {
- console.error(err);
- }
+ await expect(element(by.id('utcTime'))).toHaveText(
+ '2021-11-13T22:20:00Z',
+ );
+ await expect(element(by.id('deviceTime'))).toHaveText(
+ '2021-11-13T23:20:00+01:00',
+ );
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '2021-11-13T23:20:00+01:00',
+ );
});
it(':android: when the picker is shown as "spinner", swiping it down changes selected time', async () => {
- const timeText = getTimeText();
-
- await expect(timeText).toHaveText('11:00');
+ await expect(element(by.id('utcTime'))).toHaveText(
+ '2021-11-13T01:00:00Z',
+ );
+ await expect(element(by.id('deviceTime'))).toHaveText(
+ '2021-11-13T02:00:00+01:00',
+ );
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '2021-11-13T02:00:00+01:00',
+ );
await userOpensPicker({mode: 'time', display: 'spinner', interval: 5});
@@ -290,7 +479,15 @@ describe('e2e tests', () => {
await minutePicker.swipe('up', 'slow', 0.33);
await userTapsOkButtonAndroid();
- await expect(timeText).toHaveText('11:15');
+ await expect(element(by.id('utcTime'))).toHaveText(
+ '2021-11-13T01:15:00Z',
+ );
+ await expect(element(by.id('deviceTime'))).toHaveText(
+ '2021-11-13T02:15:00+01:00',
+ );
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '2021-11-13T02:15:00+01:00',
+ );
});
it(':ios: picker should offer only options divisible by 5 (0, 5, 10,...)', async () => {
@@ -300,9 +497,16 @@ describe('e2e tests', () => {
await testElement.setColumnToValue(0, '2');
await testElement.setColumnToValue(1, '15');
await testElement.setColumnToValue(2, 'PM');
- const timeText = getTimeText();
- await expect(timeText).toHaveText('14:15');
+ await expect(element(by.id('utcTime'))).toHaveText(
+ '2021-11-13T13:15:00Z',
+ );
+ await expect(element(by.id('deviceTime'))).toHaveText(
+ '2021-11-13T14:15:00+01:00',
+ );
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '2021-11-13T14:15:00+01:00',
+ );
const valueThatShouldNotBePresented = '18';
try {
@@ -320,7 +524,15 @@ describe('e2e tests', () => {
}
}
- await expect(timeText).toHaveText('14:45');
+ await expect(element(by.id('utcTime'))).toHaveText(
+ '2021-11-13T13:45:00Z',
+ );
+ await expect(element(by.id('deviceTime'))).toHaveText(
+ '2021-11-13T14:45:00+01:00',
+ );
+ await expect(element(by.id('overriddenTime'))).toHaveText(
+ '2021-11-13T14:45:00+01:00',
+ );
});
});
});
diff --git a/example/e2e/utils/actions.js b/example/e2e/utils/actions.js
index 0203d50e..cca2b178 100644
--- a/example/e2e/utils/actions.js
+++ b/example/e2e/utils/actions.js
@@ -30,7 +30,7 @@ async function userOpensPicker({mode, display, interval, tzOffsetPreset}) {
await element(by.text(String(interval))).tap();
}
if (tzOffsetPreset) {
- await element(by.id(tzOffsetPreset)).tap();
+ await element(by.text(tzOffsetPreset)).tap();
}
await element(by.id('showPickerButton')).tap();
}
diff --git a/example/e2e/utils/matchers.js b/example/e2e/utils/matchers.js
index 239f9d45..02cf20c0 100644
--- a/example/e2e/utils/matchers.js
+++ b/example/e2e/utils/matchers.js
@@ -1,5 +1,3 @@
-const getTimeText = () => element(by.id('timeText'));
-const getDateText = () => element(by.id('dateText'));
const elementById = (id) => element(by.id(id));
const elementByText = (text) => element(by.text(text));
@@ -13,8 +11,6 @@ const getDateTimePickerControlIOS = () => element(by.type('UIDatePicker'));
const getDatePickerAndroid = () => element(by.id('dateTimePicker'));
module.exports = {
- getTimeText,
- getDateText,
elementById,
elementByText,
getDateTimePickerIOS,
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock
index 86b783d3..edee7c7d 100644
--- a/example/ios/Podfile.lock
+++ b/example/ios/Podfile.lock
@@ -22,11 +22,6 @@ PODS:
- DoubleConversion
- fmt (~> 6.2.1)
- glog
- - RCT-Folly/Fabric (2021.07.22.00):
- - boost
- - DoubleConversion
- - fmt (~> 6.2.1)
- - glog
- RCTRequired (0.71.10)
- RCTTypeSafety (0.71.10):
- FBLazyVector (= 0.71.10)
@@ -52,11 +47,9 @@ PODS:
- RCTRequired
- RCTTypeSafety
- React-Core
- - React-graphics
- React-jsc
- React-jsi
- React-jsiexecutor
- - React-rncore
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- React-Core (0.71.10):
@@ -220,326 +213,6 @@ PODS:
- React-logger (= 0.71.10)
- React-perflogger (= 0.71.10)
- React-runtimeexecutor (= 0.71.10)
- - React-Fabric (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-Fabric/animations (= 0.71.10)
- - React-Fabric/attributedstring (= 0.71.10)
- - React-Fabric/butter (= 0.71.10)
- - React-Fabric/componentregistry (= 0.71.10)
- - React-Fabric/componentregistrynative (= 0.71.10)
- - React-Fabric/components (= 0.71.10)
- - React-Fabric/config (= 0.71.10)
- - React-Fabric/core (= 0.71.10)
- - React-Fabric/debug_core (= 0.71.10)
- - React-Fabric/debug_renderer (= 0.71.10)
- - React-Fabric/imagemanager (= 0.71.10)
- - React-Fabric/leakchecker (= 0.71.10)
- - React-Fabric/mapbuffer (= 0.71.10)
- - React-Fabric/mounting (= 0.71.10)
- - React-Fabric/runtimescheduler (= 0.71.10)
- - React-Fabric/scheduler (= 0.71.10)
- - React-Fabric/telemetry (= 0.71.10)
- - React-Fabric/templateprocessor (= 0.71.10)
- - React-Fabric/textlayoutmanager (= 0.71.10)
- - React-Fabric/uimanager (= 0.71.10)
- - React-Fabric/utils (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/animations (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/attributedstring (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/butter (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/componentregistry (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/componentregistrynative (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/components (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-Fabric/components/activityindicator (= 0.71.10)
- - React-Fabric/components/image (= 0.71.10)
- - React-Fabric/components/inputaccessory (= 0.71.10)
- - React-Fabric/components/legacyviewmanagerinterop (= 0.71.10)
- - React-Fabric/components/modal (= 0.71.10)
- - React-Fabric/components/root (= 0.71.10)
- - React-Fabric/components/safeareaview (= 0.71.10)
- - React-Fabric/components/scrollview (= 0.71.10)
- - React-Fabric/components/slider (= 0.71.10)
- - React-Fabric/components/text (= 0.71.10)
- - React-Fabric/components/textinput (= 0.71.10)
- - React-Fabric/components/unimplementedview (= 0.71.10)
- - React-Fabric/components/view (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/components/activityindicator (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/components/image (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/components/inputaccessory (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/components/legacyviewmanagerinterop (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/components/modal (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/components/root (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/components/safeareaview (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/components/scrollview (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/components/slider (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/components/text (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/components/textinput (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/components/unimplementedview (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/components/view (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - Yoga
- - React-Fabric/config (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/core (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/debug_core (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/debug_renderer (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/imagemanager (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - React-RCTImage (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/leakchecker (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/mapbuffer (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/mounting (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/runtimescheduler (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/scheduler (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/telemetry (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/templateprocessor (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/textlayoutmanager (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-Fabric/uimanager
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/uimanager (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-Fabric/utils (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - RCTRequired (= 0.71.10)
- - RCTTypeSafety (= 0.71.10)
- - React-graphics (= 0.71.10)
- - React-jsi (= 0.71.10)
- - React-jsiexecutor (= 0.71.10)
- - ReactCommon/turbomodule/core (= 0.71.10)
- - React-graphics (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - React-Core/Default (= 0.71.10)
- React-jsc (0.71.10):
- React-jsc/Fabric (= 0.71.10)
- React-jsi (= 0.71.10)
@@ -577,8 +250,6 @@ PODS:
- RCTRequired
- RCTTypeSafety
- React-Core
- - React-graphics
- - React-RCTFabric
- ReactCommon/turbomodule/core
- React-RCTBlob (0.71.10):
- RCT-Folly (= 2021.07.22.00)
@@ -588,11 +259,6 @@ PODS:
- React-jsi (= 0.71.10)
- React-RCTNetwork (= 0.71.10)
- ReactCommon/turbomodule/core (= 0.71.10)
- - React-RCTFabric (0.71.10):
- - RCT-Folly/Fabric (= 2021.07.22.00)
- - React-Core (= 0.71.10)
- - React-Fabric (= 0.71.10)
- - React-RCTImage (= 0.71.10)
- React-RCTImage (0.71.10):
- RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.71.10)
@@ -628,7 +294,6 @@ PODS:
- React-Core/RCTVibrationHeaders (= 0.71.10)
- React-jsi (= 0.71.10)
- ReactCommon/turbomodule/core (= 0.71.10)
- - React-rncore (0.71.10)
- React-runtimeexecutor (0.71.10):
- React-jsi (= 0.71.10)
- ReactCommon/turbomodule/bridging (0.71.10):
@@ -655,24 +320,10 @@ PODS:
- React-Core
- React-jsi
- ReactTestApp-Resources (1.0.0-dev)
- - RNDateTimePicker (7.2.0):
- - RCT-Folly (= 2021.07.22.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Codegen
+ - RNDateTimePicker (7.4.0):
- React-Core
- - React-RCTFabric
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- RNLocalize (3.0.0):
- - RCT-Folly (= 2021.07.22.00)
- - RCTRequired
- - RCTTypeSafety
- - React-Codegen
- React-Core
- - React-RCTFabric
- - ReactCommon/turbomodule/bridging
- - ReactCommon/turbomodule/core
- Yoga (1.14.0)
DEPENDENCIES:
@@ -682,7 +333,6 @@ DEPENDENCIES:
- FBReactNativeSpec (from `../../node_modules/react-native/React/FBReactNativeSpec`)
- glog (from `../../node_modules/react-native/third-party-podspecs/glog.podspec`)
- RCT-Folly (from `../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- - RCT-Folly/Fabric (from `../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCTRequired (from `../../node_modules/react-native/Libraries/RCTRequired`)
- RCTTypeSafety (from `../../node_modules/react-native/Libraries/TypeSafety`)
- React (from `../../node_modules/react-native/`)
@@ -692,10 +342,7 @@ DEPENDENCIES:
- React-Core/RCTWebSocket (from `../../node_modules/react-native/`)
- React-CoreModules (from `../../node_modules/react-native/React/CoreModules`)
- React-cxxreact (from `../../node_modules/react-native/ReactCommon/cxxreact`)
- - React-Fabric (from `../../node_modules/react-native/ReactCommon`)
- - React-graphics (from `../../node_modules/react-native/ReactCommon/react/renderer/graphics`)
- React-jsc (from `../../node_modules/react-native/ReactCommon/jsc`)
- - React-jsc/Fabric (from `../../node_modules/react-native/ReactCommon/jsc`)
- React-jsi (from `../../node_modules/react-native/ReactCommon/jsi`)
- React-jsiexecutor (from `../../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../../node_modules/react-native/ReactCommon/jsinspector`)
@@ -706,14 +353,12 @@ DEPENDENCIES:
- React-RCTAnimation (from `../../node_modules/react-native/Libraries/NativeAnimation`)
- React-RCTAppDelegate (from `../../node_modules/react-native/Libraries/AppDelegate`)
- React-RCTBlob (from `../../node_modules/react-native/Libraries/Blob`)
- - React-RCTFabric (from `../../node_modules/react-native/React`)
- React-RCTImage (from `../../node_modules/react-native/Libraries/Image`)
- React-RCTLinking (from `../../node_modules/react-native/Libraries/LinkingIOS`)
- React-RCTNetwork (from `../../node_modules/react-native/Libraries/Network`)
- React-RCTSettings (from `../../node_modules/react-native/Libraries/Settings`)
- React-RCTText (from `../../node_modules/react-native/Libraries/Text`)
- React-RCTVibration (from `../../node_modules/react-native/Libraries/Vibration`)
- - React-rncore (from `../../node_modules/react-native/ReactCommon`)
- React-runtimeexecutor (from `../../node_modules/react-native/ReactCommon/runtimeexecutor`)
- ReactCommon/turbomodule/core (from `../../node_modules/react-native/ReactCommon`)
- ReactTestApp-DevSupport (from `../../node_modules/react-native-test-app`)
@@ -755,10 +400,6 @@ EXTERNAL SOURCES:
:path: "../../node_modules/react-native/React/CoreModules"
React-cxxreact:
:path: "../../node_modules/react-native/ReactCommon/cxxreact"
- React-Fabric:
- :path: "../../node_modules/react-native/ReactCommon"
- React-graphics:
- :path: "../../node_modules/react-native/ReactCommon/react/renderer/graphics"
React-jsc:
:path: "../../node_modules/react-native/ReactCommon/jsc"
React-jsi:
@@ -781,8 +422,6 @@ EXTERNAL SOURCES:
:path: "../../node_modules/react-native/Libraries/AppDelegate"
React-RCTBlob:
:path: "../../node_modules/react-native/Libraries/Blob"
- React-RCTFabric:
- :path: "../../node_modules/react-native/React"
React-RCTImage:
:path: "../../node_modules/react-native/Libraries/Image"
React-RCTLinking:
@@ -795,8 +434,6 @@ EXTERNAL SOURCES:
:path: "../../node_modules/react-native/Libraries/Text"
React-RCTVibration:
:path: "../../node_modules/react-native/Libraries/Vibration"
- React-rncore:
- :path: "../../node_modules/react-native/ReactCommon"
React-runtimeexecutor:
:path: "../../node_modules/react-native/ReactCommon/runtimeexecutor"
ReactCommon:
@@ -816,7 +453,7 @@ SPEC CHECKSUMS:
boost: 57d2868c099736d80fcd648bf211b4431e51a558
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
FBLazyVector: ddb55c55295ea51ed98aa7e2e08add2f826309d5
- FBReactNativeSpec: 33a87f65f1a467d5f63d11d0cc106a10d3b0639d
+ FBReactNativeSpec: a11dff868857a8444f7defde274215293413d93a
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
@@ -824,12 +461,10 @@ SPEC CHECKSUMS:
RCTTypeSafety: 5fbddd8eb9242b91ac0d901c01da3673f358b1b7
React: e5d2d559e89d256a1d6da64d51adaecda9c8ddae
React-callinvoker: 352ecbafbdccca5fdf4aed99c98ae5b7fc28e39b
- React-Codegen: 529e89bd96c76c7005571788ca860cc4083a78fe
+ React-Codegen: 522da503ff4f6db2b4596599875ef30b4cae4ebe
React-Core: 4c13dd381dfcb8264e8ee1b981ed4ead79b99b63
React-CoreModules: 63f7f9fda3d4b214040a80e3f47ab4fb9a3e88e6
React-cxxreact: 6a455b69788c129a4f8e0c82488511cfdd9cdfc5
- React-Fabric: a39f481ea7995898a9f153c17e73a7cc2ee330d6
- React-graphics: 757a50b1b1cd76eb443d14d4de463cce24292223
React-jsc: b2dfd6271a8842d445ec85f373c49c574f5a7fcd
React-jsi: d41d5574025585f1d71ced8aa8865c40d463a6cf
React-jsiexecutor: 634df557a683cab436e4b3bc46512a6e519d19e8
@@ -839,24 +474,22 @@ SPEC CHECKSUMS:
React-perflogger: 217095464d5c4bb70df0742fa86bf2a363693468
React-RCTActionSheet: 8deae9b85a4cbc6a2243618ea62a374880a2c614
React-RCTAnimation: 59c62353a8b59ce206044786c5d30e4754bffa64
- React-RCTAppDelegate: 1042986fe63a285eb5924bf5d0c0871585d2994d
+ React-RCTAppDelegate: 0159735c684803f36c17f43443dbe8fb1c3822c4
React-RCTBlob: dcb026643fafac22f257d996a9390db987822f7e
- React-RCTFabric: cdf6ac98e7e2c911eb8f6965eb981a4531213aca
React-RCTImage: 36c0324ff499802b9874d6803ca72026e90434f6
React-RCTLinking: 401aec3a01b18c2c8ed93bf3a6758b87e617c58d
React-RCTNetwork: cb25b9f2737c3aa2cde0fe0bd7ff7fabf7bf9ad0
React-RCTSettings: cb6ae9f656e1c880500c2ecbe8e72861c2262afa
React-RCTText: 7404fd01809244d79d456f92cfe6f9fbadf69209
React-RCTVibration: d13cc2d63286c633393d3a7f6f607cc2a09ec011
- React-rncore: 0ed130c0a368c85d94e4941e675428effb8358e9
React-runtimeexecutor: a9a1cd79996c9a0846e3232ecb25c64e1cc0172e
ReactCommon: 86289421205f793f8b50106aabca54f0b4abb574
ReactTestApp-DevSupport: 1fa43e1284fd97b62238fb34c8bb349a80721b0d
ReactTestApp-Resources: ff5f151e465e890010b417ce65ca6c5de6aeccbb
- RNDateTimePicker: df9e3decb899aa74ae200cc1e892c25b4e0f08de
- RNLocalize: 9c4950ae13d7bfaeaac010ecad97abbe96abdfff
+ RNDateTimePicker: 4cb76f42ef1411f5e0cfc69758b7d9d7a30e12e4
+ RNLocalize: 5944c97d2fe8150913a51ddd5eab4e23a82bd80d
Yoga: e7ea9e590e27460d28911403b894722354d73479
PODFILE CHECKSUM: 8d21f6b1c802d4b966f0ed4d84584d57bd07fc86
-COCOAPODS: 1.12.0
+COCOAPODS: 1.12.1
diff --git a/ios/RNDateTimePicker.m b/ios/RNDateTimePicker.m
index c2a70e73..9c73c8ef 100644
--- a/ios/RNDateTimePicker.m
+++ b/ios/RNDateTimePicker.m
@@ -41,7 +41,7 @@ - (instancetype)initWithFrame:(CGRect)frame
- (void)didChange
{
if (_onChange) {
- _onChange(@{ @"timestamp": @(self.date.timeIntervalSince1970 * 1000.0) });
+ _onChange(@{ @"timestamp": @(self.date.timeIntervalSince1970 * 1000.0), @"utcOffset": @([self.timeZone secondsFromGMTForDate:self.date] / 60 )});
}
}
diff --git a/ios/RNDateTimePickerManager.m b/ios/RNDateTimePickerManager.m
index dbf3a626..dab0f9a7 100644
--- a/ios/RNDateTimePickerManager.m
+++ b/ios/RNDateTimePickerManager.m
@@ -78,7 +78,7 @@ - (UIView *)view
- (RCTShadowView *)shadowView
{
- RNDateTimePickerShadowView* shadowView = [RNDateTimePickerShadowView new];
+ RNDateTimePickerShadowView* shadowView = [RNDateTimePickerShadowView new];
shadowView.picker = _picker;
return shadowView;
}
@@ -104,6 +104,7 @@ + (NSString*) datepickerStyleToString: (UIDatePickerStyle) style API_AVAILABLE(
RCT_EXPORT_SHADOW_PROPERTY(mode, UIDatePickerMode)
RCT_EXPORT_SHADOW_PROPERTY(locale, NSLocale)
RCT_EXPORT_SHADOW_PROPERTY(displayIOS, RNCUIDatePickerStyle)
+RCT_EXPORT_SHADOW_PROPERTY(timeZoneName, NSString)
RCT_EXPORT_VIEW_PROPERTY(date, NSDate)
RCT_EXPORT_VIEW_PROPERTY(locale, NSLocale)
@@ -178,4 +179,17 @@ + (NSString*) datepickerStyleToString: (UIDatePickerStyle) style API_AVAILABLE(
}
}
+RCT_CUSTOM_VIEW_PROPERTY(timeZoneName, NSString, RNDateTimePicker)
+{
+ if (json) {
+ NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:json];
+ if (timeZone != nil) {
+ [view setTimeZone:timeZone];
+ return;
+ }
+ }
+ RCTLogWarn(@"'%@' does not exist in NSTimeZone.knownTimeZoneNames fallback to localTimeZone=%@", json, NSTimeZone.localTimeZone.name);
+ [view setTimeZone:[NSTimeZone localTimeZone]];
+}
+
@end
diff --git a/ios/RNDateTimePickerShadowView.h b/ios/RNDateTimePickerShadowView.h
index 04578ee8..4ce9175d 100644
--- a/ios/RNDateTimePickerShadowView.h
+++ b/ios/RNDateTimePickerShadowView.h
@@ -7,6 +7,8 @@
@property (nonatomic) UIDatePickerMode mode;
@property (nullable, nonatomic, strong) NSDate *date;
@property (nullable, nonatomic, strong) NSLocale *locale;
+@property (nonatomic, assign) NSInteger timeZoneOffsetInMinutes;
+@property (nullable, nonatomic, strong) NSString *timeZoneName;
@property (nonatomic, assign) UIDatePickerStyle displayIOS API_AVAILABLE(ios(13.4));
@end
diff --git a/ios/RNDateTimePickerShadowView.m b/ios/RNDateTimePickerShadowView.m
index ba0273bc..a91527fa 100644
--- a/ios/RNDateTimePickerShadowView.m
+++ b/ios/RNDateTimePickerShadowView.m
@@ -31,6 +31,16 @@ - (void)setDisplayIOS:(UIDatePickerStyle)displayIOS {
YGNodeMarkDirty(self.yogaNode);
}
+- (void)setTimeZoneOffsetInMinutes:(NSInteger)timeZoneOffsetInMinutes {
+ _timeZoneOffsetInMinutes = timeZoneOffsetInMinutes;
+ YGNodeMarkDirty(self.yogaNode);
+}
+
+- (void)setTimeZoneName:(NSString *)timeZoneName {
+ _timeZoneName = timeZoneName;
+ YGNodeMarkDirty(self.yogaNode);
+}
+
static YGSize RNDateTimePickerShadowViewMeasure(YGNodeRef node, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode)
{
RNDateTimePickerShadowView *shadowPickerView = (__bridge RNDateTimePickerShadowView *)YGNodeGetContext(node);
@@ -40,13 +50,24 @@ static YGSize RNDateTimePickerShadowViewMeasure(YGNodeRef node, float width, YGM
[shadowPickerView.picker setDate:shadowPickerView.date];
[shadowPickerView.picker setDatePickerMode:shadowPickerView.mode];
[shadowPickerView.picker setLocale:shadowPickerView.locale];
+ [shadowPickerView.picker setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:shadowPickerView.timeZoneOffsetInMinutes * 60]];
+
+ NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:shadowPickerView.timeZoneName];
+ if (timeZone != nil) {
+ [shadowPickerView.picker setTimeZone: timeZone];
+ } else {
+ RCTLogWarn(@"'%@' does not exist in NSTimeZone.knownTimeZoneNames fallback to localTimeZone=%@", shadowPickerView.timeZoneName, NSTimeZone.localTimeZone.name);
+ [shadowPickerView.picker setTimeZone:[NSTimeZone localTimeZone]];
+ }
+
if (@available(iOS 14.0, *)) {
[shadowPickerView.picker setPreferredDatePickerStyle:shadowPickerView.displayIOS];
}
- size = [shadowPickerView.picker sizeThatFits:UILayoutFittingCompressedSize];
- size.width += 10;
+
+ size = [shadowPickerView.picker sizeThatFits:UILayoutFittingCompressedSize];
+ size.width += 10;
});
-
+
return (YGSize){
RCTYogaFloatFromCoreGraphicsFloat(size.width),
RCTYogaFloatFromCoreGraphicsFloat(size.height)
diff --git a/ios/fabric/RNDateTimePickerComponentView.mm b/ios/fabric/RNDateTimePickerComponentView.mm
index 4d2ac597..22c077db 100644
--- a/ios/fabric/RNDateTimePickerComponentView.mm
+++ b/ios/fabric/RNDateTimePickerComponentView.mm
@@ -36,20 +36,20 @@ - (instancetype)initWithFrame:(CGRect)frame
if (self = [super initWithFrame:frame]) {
static const auto defaultProps = std::make_shared();
_props = defaultProps;
-
+
_picker = [RNDateTimePicker new];
_dummyPicker = [RNDateTimePicker new];
-
+
[_picker addTarget:self action:@selector(onChange:) forControlEvents:UIControlEventValueChanged];
[_picker addTarget:self action:@selector(onDismiss:) forControlEvents:UIControlEventEditingDidEnd];
-
+
// Default Picker mode
_picker.datePickerMode = UIDatePickerModeDate;
_dummyPicker.datePickerMode = UIDatePickerModeDate;
-
+
self.contentView = _picker;
}
-
+
return self;
}
@@ -68,13 +68,13 @@ -(void)onChange:(RNDateTimePicker *)sender
if (!_eventEmitter) {
return;
}
-
+
NSTimeInterval timestamp = [sender.date timeIntervalSince1970];
RNDateTimePickerEventEmitter::OnChange event = {
// Sending time in milliseconds
.timestamp = timestamp * 1000
};
-
+
std::dynamic_pointer_cast(_eventEmitter)
->onChange(event);
}
@@ -135,7 +135,7 @@ -(void)updateTextColorForPicker:(UIDatePicker *)picker color:(UIColor *)color
*/
- (void)updateState:(const State::Shared &)state oldState:(const State::Shared &)oldState {
_state = std::static_pointer_cast(state);
-
+
if (oldState == nullptr) {
// Calculate the initial picker measurements
[self updateMeasurements];
@@ -149,24 +149,24 @@ - (void)updateState:(const State::Shared &)state oldState:(const State::Shared &
* Props that will to update measurements: date, locale, mode, displayIOS.
*/
- (Boolean)updatePropsForPicker:(UIDatePicker *)picker props:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps {
-
+
const auto &oldPickerProps = *std::static_pointer_cast(_props);
const auto &newPickerProps = *std::static_pointer_cast(props);
Boolean needsToUpdateMeasurements = false;
-
+
if (oldPickerProps.date != newPickerProps.date) {
picker.date = convertJSTimeToDate(newPickerProps.date);
needsToUpdateMeasurements = true;
}
-
+
if (oldPickerProps.minimumDate != newPickerProps.minimumDate) {
picker.minimumDate = convertJSTimeToDate(newPickerProps.minimumDate);
}
-
+
if (oldPickerProps.maximumDate != newPickerProps.maximumDate) {
picker.maximumDate = convertJSTimeToDate(newPickerProps.maximumDate);
}
-
+
if (oldPickerProps.locale != newPickerProps.locale) {
NSString *convertedLocale = RCTNSStringFromString(newPickerProps.locale);
NSLocale *locale = [[NSLocale alloc] initWithLocaleIdentifier:convertedLocale];
@@ -174,7 +174,7 @@ - (Boolean)updatePropsForPicker:(UIDatePicker *)picker props:(Props::Shared cons
picker.locale = locale;
needsToUpdateMeasurements = true;
}
-
+
if (oldPickerProps.mode != newPickerProps.mode) {
switch(newPickerProps.mode) {
case RNDateTimePickerMode::Time:
@@ -191,7 +191,7 @@ - (Boolean)updatePropsForPicker:(UIDatePicker *)picker props:(Props::Shared cons
}
needsToUpdateMeasurements = true;
}
-
+
if (@available(iOS 14.0, *)) {
if (oldPickerProps.displayIOS != newPickerProps.displayIOS) {
switch(newPickerProps.displayIOS) {
@@ -210,19 +210,30 @@ - (Boolean)updatePropsForPicker:(UIDatePicker *)picker props:(Props::Shared cons
needsToUpdateMeasurements = true;
}
}
-
+
if (oldPickerProps.minuteInterval != newPickerProps.minuteInterval) {
picker.minuteInterval = newPickerProps.minuteInterval;
}
-
+
if (oldPickerProps.timeZoneOffsetInMinutes != newPickerProps.timeZoneOffsetInMinutes) {
// JS standard for time zones is minutes.
picker.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:newPickerProps.timeZoneOffsetInMinutes * 60.0];
}
-
+
+ if (oldPickerProps.timeZoneName != newPickerProps.timeZoneName) {
+ NSString *timeZoneName = [NSString stringWithUTF8String:newPickerProps.timeZoneName.c_str()];
+ NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:timeZoneName];
+ if (timeZone != nil) {
+ picker.timeZone = timeZone;
+ } else {
+ RCTLogWarn(@"'%@' does not exist in NSTimeZone.knownTimeZoneNames fallback to localTimeZone=%@", timeZoneName, NSTimeZone.localTimeZone.name);
+ picker.timeZone = [NSTimeZone localTimeZone];
+ }
+ }
+
if (oldPickerProps.accentColor != newPickerProps.accentColor) {
UIColor *color = RCTUIColorFromSharedColor(newPickerProps.accentColor);
-
+
if (color != nil) {
[picker setTintColor:color];
} else {
@@ -233,11 +244,11 @@ - (Boolean)updatePropsForPicker:(UIDatePicker *)picker props:(Props::Shared cons
}
}
}
-
+
if (oldPickerProps.textColor != newPickerProps.textColor) {
[self updateTextColorForPicker:picker color:RCTUIColorFromSharedColor(newPickerProps.textColor)];
}
-
+
if (@available(iOS 13.0, *)) {
if (oldPickerProps.themeVariant != newPickerProps.themeVariant) {
switch (newPickerProps.themeVariant) {
@@ -252,11 +263,11 @@ - (Boolean)updatePropsForPicker:(UIDatePicker *)picker props:(Props::Shared cons
}
}
}
-
+
if (oldPickerProps.enabled != newPickerProps.enabled) {
picker.enabled = newPickerProps.enabled;
}
-
+
return needsToUpdateMeasurements;
}
@@ -264,13 +275,13 @@ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &
{
// Updating the dummy first to check if we need to update measurements
Boolean needsToUpdateMeasurements = [self updatePropsForPicker:_dummyPicker props:props oldProps:oldProps];
-
+
if (needsToUpdateMeasurements) {
[self updateMeasurements];
}
-
+
[self updatePropsForPicker:_picker props:props oldProps:oldProps];
-
+
[super updateProps:props oldProps:oldProps];
}
diff --git a/jest/index.js b/jest/index.js
index 1e44a6df..78e2821c 100644
--- a/jest/index.js
+++ b/jest/index.js
@@ -12,19 +12,12 @@ export const mockAndroidDialogDateChange = (datePickedByUser: Date) => {
value: timestampFromPickerValueProp,
}) {
const pickedDate = new Date(timestampFromPickerValueProp);
- pickedDate.setFullYear(
- datePickedByUser.getFullYear(),
- datePickedByUser.getMonth(),
- datePickedByUser.getDate(),
- );
+ pickedDate.setTime(datePickedByUser.getTime());
return {
action: DATE_SET_ACTION,
- year: pickedDate.getFullYear(),
- month: pickedDate.getMonth(),
- day: pickedDate.getDate(),
- hour: pickedDate.getHours(),
- minute: pickedDate.getMinutes(),
+ timestamp: pickedDate.getTime(),
+ utcOffset: 0,
};
}
return (fakeDateTimePickerAndroidOpener: PresentPickerCallback);
diff --git a/package.json b/package.json
index 6b8e6af4..d14cdfb6 100644
--- a/package.json
+++ b/package.json
@@ -81,6 +81,7 @@
"jest": "^29.5.0",
"metro-react-native-babel-preset": "0.73.9",
"moment": "^2.24.0",
+ "moment-timezone": "^0.5.41",
"patch-package": "^6.4.7",
"postinstall-postinstall": "^2.1.0",
"prettier": "^2.8.8",
diff --git a/src/DateTimePickerAndroid.android.js b/src/DateTimePickerAndroid.android.js
index 9f720521..46fba34c 100644
--- a/src/DateTimePickerAndroid.android.js
+++ b/src/DateTimePickerAndroid.android.js
@@ -13,11 +13,7 @@ import {
import invariant from 'invariant';
import type {AndroidNativeProps} from './types';
-import {
- getOpenPicker,
- timeZoneOffsetDateSetter,
- validateAndroidProps,
-} from './androidUtils';
+import {getOpenPicker, validateAndroidProps} from './androidUtils';
import pickers from './picker';
import {
createDateTimeSetEvtParams,
@@ -36,6 +32,7 @@ function open(props: AndroidNativeProps) {
maximumDate,
minuteInterval,
timeZoneOffsetInMinutes,
+ timeZoneName,
onChange,
onError,
positiveButton,
@@ -76,7 +73,7 @@ function open(props: AndroidNativeProps) {
display === ANDROID_DISPLAY.spinner
? ANDROID_DISPLAY.spinner
: ANDROID_DISPLAY.default;
- const {action, day, month, year, minute, hour} = await openPicker({
+ const {action, timestamp, utcOffset} = await openPicker({
value: valueTimestamp,
display: displayOverride,
is24Hour,
@@ -84,26 +81,18 @@ function open(props: AndroidNativeProps) {
maximumDate,
minuteInterval,
timeZoneOffsetInMinutes,
+ timeZoneName,
dialogButtons,
testID,
});
switch (action) {
- case DATE_SET_ACTION: {
- let date = new Date(valueTimestamp);
- date.setFullYear(year, month, day);
- date = timeZoneOffsetDateSetter(date, timeZoneOffsetInMinutes);
- const [event] = createDateTimeSetEvtParams(date);
- onChange?.(event, date);
- break;
- }
-
+ case DATE_SET_ACTION:
case TIME_SET_ACTION: {
- let date = new Date(valueTimestamp);
- date.setHours(hour, minute);
- date = timeZoneOffsetDateSetter(date, timeZoneOffsetInMinutes);
+ let date = new Date(timestamp);
const [event] = createDateTimeSetEvtParams(date);
- onChange?.(event, date);
+ event.nativeEvent.utcOffset = utcOffset;
+ onChange?.(event, new Date(timestamp));
break;
}
diff --git a/src/androidUtils.js b/src/androidUtils.js
index 2e422507..9442cf6f 100644
--- a/src/androidUtils.js
+++ b/src/androidUtils.js
@@ -2,7 +2,7 @@
* @format
* @flow strict-local
*/
-import {ANDROID_DISPLAY, ANDROID_MODE, MIN_MS} from './constants';
+import {ANDROID_DISPLAY, ANDROID_MODE} from './constants';
import pickers from './picker';
import type {AndroidNativeProps, DateTimePickerResult} from './types';
import {sharedPropsValidation} from './utils';
@@ -24,6 +24,7 @@ type OpenParams = {
maximumDate: AndroidNativeProps['maximumDate'],
minuteInterval: AndroidNativeProps['minuteInterval'],
timeZoneOffsetInMinutes: AndroidNativeProps['timeZoneOffsetInMinutes'],
+ timeZoneName: AndroidNativeProps['timeZoneName'],
testID: AndroidNativeProps['testID'],
dialogButtons: {
positive: ProcessedButton,
@@ -46,6 +47,7 @@ function getOpenPicker(
is24Hour,
minuteInterval,
timeZoneOffsetInMinutes,
+ timeZoneName,
dialogButtons,
}: OpenParams) =>
// $FlowFixMe - `AbstractComponent` [1] is not an instance type.
@@ -55,6 +57,7 @@ function getOpenPicker(
minuteInterval,
is24Hour,
timeZoneOffsetInMinutes,
+ timeZoneName,
dialogButtons,
});
default:
@@ -64,6 +67,7 @@ function getOpenPicker(
minimumDate,
maximumDate,
timeZoneOffsetInMinutes,
+ timeZoneName,
dialogButtons,
testID,
}: OpenParams) =>
@@ -74,26 +78,13 @@ function getOpenPicker(
minimumDate,
maximumDate,
timeZoneOffsetInMinutes,
+ timeZoneName,
dialogButtons,
testID,
});
}
}
-function timeZoneOffsetDateSetter(
- date: Date,
- timeZoneOffsetInMinutes: ?number,
-): Date {
- if (typeof timeZoneOffsetInMinutes === 'number') {
- // FIXME this causes a bug. repro: set tz offset to zero, and then keep opening and closing the calendar picker
- // https://github.com/react-native-datetimepicker/datetimepicker/issues/528
- const offset = date.getTimezoneOffset() + timeZoneOffsetInMinutes;
- const shiftedDate = new Date(date.getTime() - offset * MIN_MS);
- return shiftedDate;
- }
- return date;
-}
-
function validateAndroidProps(props: AndroidNativeProps) {
sharedPropsValidation({value: props?.value});
const {mode, display} = props;
@@ -113,4 +104,4 @@ function validateAndroidProps(props: AndroidNativeProps) {
);
}
}
-export {getOpenPicker, timeZoneOffsetDateSetter, validateAndroidProps};
+export {getOpenPicker, validateAndroidProps};
diff --git a/src/datetimepicker.android.js b/src/datetimepicker.android.js
index 5fc83f19..c530d70e 100644
--- a/src/datetimepicker.android.js
+++ b/src/datetimepicker.android.js
@@ -24,6 +24,7 @@ export default function RNDateTimePickerAndroid(
minuteInterval,
onError,
timeZoneOffsetInMinutes,
+ timeZoneName,
positiveButton,
negativeButton,
neutralButton,
@@ -51,6 +52,7 @@ export default function RNDateTimePickerAndroid(
maximumDate,
minuteInterval,
timeZoneOffsetInMinutes,
+ timeZoneName,
onError,
onChange,
positiveButton,
diff --git a/src/datetimepicker.ios.js b/src/datetimepicker.ios.js
index af363e2b..c5f71158 100644
--- a/src/datetimepicker.ios.js
+++ b/src/datetimepicker.ios.js
@@ -50,6 +50,7 @@ export default function Picker({
minimumDate,
minuteInterval,
timeZoneOffsetInMinutes,
+ timeZoneName,
textColor,
accentColor,
themeVariant,
@@ -59,7 +60,7 @@ export default function Picker({
disabled = false,
...other
}: IOSNativeProps): React.Node {
- sharedPropsValidation({value});
+ sharedPropsValidation({value, timeZoneOffsetInMinutes, timeZoneName});
const display = getDisplaySafe(providedDisplay);
@@ -99,6 +100,7 @@ export default function Picker({
mode={mode}
minuteInterval={minuteInterval}
timeZoneOffsetInMinutes={timeZoneOffsetInMinutes}
+ timeZoneName={timeZoneName}
onChange={_onChange}
onPickerDismiss={onDismiss}
textColor={textColor}
diff --git a/src/index.d.ts b/src/index.d.ts
index c421c3a1..cd4214d3 100644
--- a/src/index.d.ts
+++ b/src/index.d.ts
@@ -63,7 +63,15 @@ type TimeOptions = Readonly<
}
>;
-export type BaseProps = Readonly & DateOptions>;
+export type BaseProps = Readonly<
+ Omit &
+ DateOptions & {
+ /**
+ * The tz database name in https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
+ */
+ timeZoneName?: string;
+ }
+>;
export type IOSNativeProps = Readonly<
BaseProps & {
diff --git a/src/specs/NativeComponentDateTimePicker.js b/src/specs/NativeComponentDateTimePicker.js
index ce28e22a..526dbc8f 100644
--- a/src/specs/NativeComponentDateTimePicker.js
+++ b/src/specs/NativeComponentDateTimePicker.js
@@ -13,6 +13,7 @@ import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNati
type DateTimePickerEvent = $ReadOnly<{|
timestamp: Double,
+ utcOffset: Int32,
|}>;
type NativeProps = $ReadOnly<{|
@@ -26,6 +27,7 @@ type NativeProps = $ReadOnly<{|
minuteInterval?: ?Int32,
mode?: WithDefault<'date' | 'time' | 'datetime' | 'countdown', 'date'>,
timeZoneOffsetInMinutes?: ?Double,
+ timeZoneName?: ?string,
textColor?: ?ColorValue,
accentColor?: ?ColorValue,
themeVariant?: WithDefault<'dark' | 'light' | 'unspecified', 'unspecified'>,
diff --git a/src/types.js b/src/types.js
index aff89406..d0595fdd 100644
--- a/src/types.js
+++ b/src/types.js
@@ -29,13 +29,15 @@ type MinuteInterval = ?(1 | 2 | 3 | 4 | 5 | 6 | 10 | 12 | 15 | 20 | 30);
export type NativeEventIOS = SyntheticEvent<
$ReadOnly<{|
timestamp: number,
+ utcOffset: number,
|}>,
>;
export type DateTimePickerEvent = {
type: AndroidEvtTypes,
nativeEvent: $ReadOnly<{
- timestamp?: number,
+ timestamp: number,
+ utcOffset?: number,
...
}>,
...
@@ -92,6 +94,13 @@ type ViewPropsWithoutChildren = $Diff<
export type BaseProps = $ReadOnly<{|
...ViewPropsWithoutChildren,
...DateOptions,
+ /**
+ * Timezone in database name.
+ *
+ * By default, the date picker will use the device's timezone. With this
+ * parameter, it is possible to force a certain timezone based on IANA
+ */
+ timeZoneName?: ?string,
|}>;
export type IOSNativeProps = $ReadOnly<{|
@@ -175,6 +184,7 @@ export type AndroidNativeProps = $ReadOnly<{|
* instance, to show times in Pacific Standard Time, pass -7 * 60.
*/
timeZoneOffsetInMinutes?: ?number,
+
/**
* The interval at which minutes can be selected.
*/
@@ -211,11 +221,8 @@ export type TimePickerOptions = {|
export type DateTimePickerResult = $ReadOnly<{|
action: 'timeSetAction' | 'dateSetAction' | 'dismissedAction',
- year: number,
- month: number,
- day: number,
- hour: number,
- minute: number,
+ timestamp: number,
+ utcOffset: number,
|}>;
export type RCTDateTimePickerNative = Class>;
diff --git a/src/utils.js b/src/utils.js
index 4df40d25..79a2e5fe 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -30,10 +30,25 @@ export function dateToMilliseconds(date: ?Date): ?number {
return date.getTime();
}
-export function sharedPropsValidation({value}: {value: ?Date}) {
+export function sharedPropsValidation({
+ value,
+ timeZoneName,
+ timeZoneOffsetInMinutes,
+}: {
+ value: ?Date,
+ timeZoneName?: ?string,
+ timeZoneOffsetInMinutes?: ?number,
+}) {
invariant(value, 'A date or time must be specified as `value` prop');
invariant(
value instanceof Date,
'`value` prop must be an instance of Date object',
);
+ invariant(
+ timeZoneName == null ||
+ timeZoneName === undefined ||
+ timeZoneOffsetInMinutes == null ||
+ timeZoneOffsetInMinutes === undefined,
+ '`timeZoneName` and `timeZoneOffsetInMinutes` cannot be specified at the same time',
+ );
}
diff --git a/yarn.lock b/yarn.lock
index 8b0df076..2331f18b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7615,7 +7615,14 @@ module-details-from-path@^1.0.3:
resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b"
integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==
-moment@^2.19.3, moment@^2.24.0:
+moment-timezone@^0.5.41:
+ version "0.5.43"
+ resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.43.tgz#3dd7f3d0c67f78c23cd1906b9b2137a09b3c4790"
+ integrity sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==
+ dependencies:
+ moment "^2.29.4"
+
+moment@^2.19.3, moment@^2.24.0, moment@^2.29.4:
version "2.29.4"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
From 89c787881dc9b2dfbaad779d94d354d74c254efd Mon Sep 17 00:00:00 2001
From: Vojtech Novak
Date: Thu, 24 Aug 2023 21:12:20 +0200
Subject: [PATCH 2/7] code review
---
.../RNDatePickerDialogFragment.java | 9 +-
example/e2e/detoxTest.spec.js | 12 +-
example/ios/Podfile.lock | 377 +++++++++++++++++-
ios/RNDateTimePickerManager.m | 18 +-
ios/RNDateTimePickerShadowView.m | 2 +-
ios/fabric/RNDateTimePickerComponentView.mm | 16 +-
src/DateTimePickerAndroid.android.js | 11 +-
src/datetimepicker.ios.js | 1 +
src/eventCreators.js | 6 +
src/types.js | 2 +-
src/utils.js | 7 +-
test/userlandTestExamples.test.js | 2 +-
12 files changed, 421 insertions(+), 42 deletions(-)
diff --git a/android/src/main/java/com/reactcommunity/rndatetimepicker/RNDatePickerDialogFragment.java b/android/src/main/java/com/reactcommunity/rndatetimepicker/RNDatePickerDialogFragment.java
index fd55a382..72de7cbb 100644
--- a/android/src/main/java/com/reactcommunity/rndatetimepicker/RNDatePickerDialogFragment.java
+++ b/android/src/main/java/com/reactcommunity/rndatetimepicker/RNDatePickerDialogFragment.java
@@ -111,13 +111,16 @@ private DatePickerDialog createDialog(Bundle args) {
if (args.containsKey(RNConstants.ARG_MINDATE)) {
datePicker.setMinDate(minDate);
+ } else {
+ // This is to work around a bug in DatePickerDialog where it doesn't display a title showing
+ // the date under certain conditions.
+ datePicker.setMinDate(RNConstants.DEFAULT_MIN_DATE);
}
if (args.containsKey(RNConstants.ARG_MAXDATE)) {
datePicker.setMaxDate(maxDate);
}
- if (args != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
- && (args.containsKey(RNConstants.ARG_MAXDATE) || args.containsKey(RNConstants.ARG_MINDATE))) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && (args.containsKey(RNConstants.ARG_MAXDATE) || args.containsKey(RNConstants.ARG_MINDATE))) {
datePicker.setOnDateChangedListener((view, year, monthOfYear, dayOfMonth) -> {
Calendar calendar = Calendar.getInstance(Common.getTimeZone(args));
calendar.set(year, monthOfYear, dayOfMonth, 0, 0, 0);
@@ -129,7 +132,7 @@ private DatePickerDialog createDialog(Bundle args) {
});
}
- if (args != null && args.containsKey(RNConstants.ARG_TESTID)) {
+ if (args.containsKey(RNConstants.ARG_TESTID)) {
datePicker.setTag(args.getString(RNConstants.ARG_TESTID));
}
diff --git a/example/e2e/detoxTest.spec.js b/example/e2e/detoxTest.spec.js
index f43d69d4..a5dc3884 100644
--- a/example/e2e/detoxTest.spec.js
+++ b/example/e2e/detoxTest.spec.js
@@ -40,17 +40,15 @@ describe('e2e tests', () => {
});
it('timeInfo heading has expected content', async () => {
- await expect(element(by.id('utcTime'))).toHaveText('2021-11-13T01:00:00Z');
- await expect(element(by.id('deviceTime'))).toHaveText(
+ await expect(elementById('utcTime')).toHaveText('2021-11-13T01:00:00Z');
+ await expect(elementById('deviceTime')).toHaveText(
'2021-11-13T02:00:00+01:00',
);
- await expect(element(by.id('deviceTzName'))).toHaveText('Europe/Prague');
- await expect(element(by.id('overriddenTime'))).toHaveText(
+ await expect(elementById('deviceTzName')).toHaveText('Europe/Prague');
+ await expect(elementById('overriddenTime')).toHaveText(
'2021-11-13T02:00:00+01:00',
);
- await expect(element(by.id('overriddenTzName'))).toHaveText(
- 'Europe/Prague',
- );
+ await expect(elementById('overriddenTzName')).toHaveText('Europe/Prague');
});
it('should show date picker after tapping datePicker button', async () => {
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock
index edee7c7d..18de3802 100644
--- a/example/ios/Podfile.lock
+++ b/example/ios/Podfile.lock
@@ -22,6 +22,11 @@ PODS:
- DoubleConversion
- fmt (~> 6.2.1)
- glog
+ - RCT-Folly/Fabric (2021.07.22.00):
+ - boost
+ - DoubleConversion
+ - fmt (~> 6.2.1)
+ - glog
- RCTRequired (0.71.10)
- RCTTypeSafety (0.71.10):
- FBLazyVector (= 0.71.10)
@@ -47,9 +52,11 @@ PODS:
- RCTRequired
- RCTTypeSafety
- React-Core
+ - React-graphics
- React-jsc
- React-jsi
- React-jsiexecutor
+ - React-rncore
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- React-Core (0.71.10):
@@ -213,6 +220,326 @@ PODS:
- React-logger (= 0.71.10)
- React-perflogger (= 0.71.10)
- React-runtimeexecutor (= 0.71.10)
+ - React-Fabric (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-Fabric/animations (= 0.71.10)
+ - React-Fabric/attributedstring (= 0.71.10)
+ - React-Fabric/butter (= 0.71.10)
+ - React-Fabric/componentregistry (= 0.71.10)
+ - React-Fabric/componentregistrynative (= 0.71.10)
+ - React-Fabric/components (= 0.71.10)
+ - React-Fabric/config (= 0.71.10)
+ - React-Fabric/core (= 0.71.10)
+ - React-Fabric/debug_core (= 0.71.10)
+ - React-Fabric/debug_renderer (= 0.71.10)
+ - React-Fabric/imagemanager (= 0.71.10)
+ - React-Fabric/leakchecker (= 0.71.10)
+ - React-Fabric/mapbuffer (= 0.71.10)
+ - React-Fabric/mounting (= 0.71.10)
+ - React-Fabric/runtimescheduler (= 0.71.10)
+ - React-Fabric/scheduler (= 0.71.10)
+ - React-Fabric/telemetry (= 0.71.10)
+ - React-Fabric/templateprocessor (= 0.71.10)
+ - React-Fabric/textlayoutmanager (= 0.71.10)
+ - React-Fabric/uimanager (= 0.71.10)
+ - React-Fabric/utils (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/animations (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/attributedstring (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/butter (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/componentregistry (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/componentregistrynative (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/components (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-Fabric/components/activityindicator (= 0.71.10)
+ - React-Fabric/components/image (= 0.71.10)
+ - React-Fabric/components/inputaccessory (= 0.71.10)
+ - React-Fabric/components/legacyviewmanagerinterop (= 0.71.10)
+ - React-Fabric/components/modal (= 0.71.10)
+ - React-Fabric/components/root (= 0.71.10)
+ - React-Fabric/components/safeareaview (= 0.71.10)
+ - React-Fabric/components/scrollview (= 0.71.10)
+ - React-Fabric/components/slider (= 0.71.10)
+ - React-Fabric/components/text (= 0.71.10)
+ - React-Fabric/components/textinput (= 0.71.10)
+ - React-Fabric/components/unimplementedview (= 0.71.10)
+ - React-Fabric/components/view (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/components/activityindicator (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/components/image (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/components/inputaccessory (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/components/legacyviewmanagerinterop (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/components/modal (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/components/root (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/components/safeareaview (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/components/scrollview (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/components/slider (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/components/text (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/components/textinput (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/components/unimplementedview (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/components/view (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - Yoga
+ - React-Fabric/config (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/core (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/debug_core (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/debug_renderer (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/imagemanager (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - React-RCTImage (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/leakchecker (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/mapbuffer (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/mounting (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/runtimescheduler (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/scheduler (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/telemetry (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/templateprocessor (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/textlayoutmanager (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-Fabric/uimanager
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/uimanager (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-Fabric/utils (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - RCTRequired (= 0.71.10)
+ - RCTTypeSafety (= 0.71.10)
+ - React-graphics (= 0.71.10)
+ - React-jsi (= 0.71.10)
+ - React-jsiexecutor (= 0.71.10)
+ - ReactCommon/turbomodule/core (= 0.71.10)
+ - React-graphics (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - React-Core/Default (= 0.71.10)
- React-jsc (0.71.10):
- React-jsc/Fabric (= 0.71.10)
- React-jsi (= 0.71.10)
@@ -250,6 +577,8 @@ PODS:
- RCTRequired
- RCTTypeSafety
- React-Core
+ - React-graphics
+ - React-RCTFabric
- ReactCommon/turbomodule/core
- React-RCTBlob (0.71.10):
- RCT-Folly (= 2021.07.22.00)
@@ -259,6 +588,11 @@ PODS:
- React-jsi (= 0.71.10)
- React-RCTNetwork (= 0.71.10)
- ReactCommon/turbomodule/core (= 0.71.10)
+ - React-RCTFabric (0.71.10):
+ - RCT-Folly/Fabric (= 2021.07.22.00)
+ - React-Core (= 0.71.10)
+ - React-Fabric (= 0.71.10)
+ - React-RCTImage (= 0.71.10)
- React-RCTImage (0.71.10):
- RCT-Folly (= 2021.07.22.00)
- RCTTypeSafety (= 0.71.10)
@@ -294,6 +628,7 @@ PODS:
- React-Core/RCTVibrationHeaders (= 0.71.10)
- React-jsi (= 0.71.10)
- ReactCommon/turbomodule/core (= 0.71.10)
+ - React-rncore (0.71.10)
- React-runtimeexecutor (0.71.10):
- React-jsi (= 0.71.10)
- ReactCommon/turbomodule/bridging (0.71.10):
@@ -321,9 +656,23 @@ PODS:
- React-jsi
- ReactTestApp-Resources (1.0.0-dev)
- RNDateTimePicker (7.4.0):
+ - RCT-Folly (= 2021.07.22.00)
+ - RCTRequired
+ - RCTTypeSafety
+ - React-Codegen
- React-Core
+ - React-RCTFabric
+ - ReactCommon/turbomodule/bridging
+ - ReactCommon/turbomodule/core
- RNLocalize (3.0.0):
+ - RCT-Folly (= 2021.07.22.00)
+ - RCTRequired
+ - RCTTypeSafety
+ - React-Codegen
- React-Core
+ - React-RCTFabric
+ - ReactCommon/turbomodule/bridging
+ - ReactCommon/turbomodule/core
- Yoga (1.14.0)
DEPENDENCIES:
@@ -333,6 +682,7 @@ DEPENDENCIES:
- FBReactNativeSpec (from `../../node_modules/react-native/React/FBReactNativeSpec`)
- glog (from `../../node_modules/react-native/third-party-podspecs/glog.podspec`)
- RCT-Folly (from `../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
+ - RCT-Folly/Fabric (from `../../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCTRequired (from `../../node_modules/react-native/Libraries/RCTRequired`)
- RCTTypeSafety (from `../../node_modules/react-native/Libraries/TypeSafety`)
- React (from `../../node_modules/react-native/`)
@@ -342,7 +692,10 @@ DEPENDENCIES:
- React-Core/RCTWebSocket (from `../../node_modules/react-native/`)
- React-CoreModules (from `../../node_modules/react-native/React/CoreModules`)
- React-cxxreact (from `../../node_modules/react-native/ReactCommon/cxxreact`)
+ - React-Fabric (from `../../node_modules/react-native/ReactCommon`)
+ - React-graphics (from `../../node_modules/react-native/ReactCommon/react/renderer/graphics`)
- React-jsc (from `../../node_modules/react-native/ReactCommon/jsc`)
+ - React-jsc/Fabric (from `../../node_modules/react-native/ReactCommon/jsc`)
- React-jsi (from `../../node_modules/react-native/ReactCommon/jsi`)
- React-jsiexecutor (from `../../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../../node_modules/react-native/ReactCommon/jsinspector`)
@@ -353,12 +706,14 @@ DEPENDENCIES:
- React-RCTAnimation (from `../../node_modules/react-native/Libraries/NativeAnimation`)
- React-RCTAppDelegate (from `../../node_modules/react-native/Libraries/AppDelegate`)
- React-RCTBlob (from `../../node_modules/react-native/Libraries/Blob`)
+ - React-RCTFabric (from `../../node_modules/react-native/React`)
- React-RCTImage (from `../../node_modules/react-native/Libraries/Image`)
- React-RCTLinking (from `../../node_modules/react-native/Libraries/LinkingIOS`)
- React-RCTNetwork (from `../../node_modules/react-native/Libraries/Network`)
- React-RCTSettings (from `../../node_modules/react-native/Libraries/Settings`)
- React-RCTText (from `../../node_modules/react-native/Libraries/Text`)
- React-RCTVibration (from `../../node_modules/react-native/Libraries/Vibration`)
+ - React-rncore (from `../../node_modules/react-native/ReactCommon`)
- React-runtimeexecutor (from `../../node_modules/react-native/ReactCommon/runtimeexecutor`)
- ReactCommon/turbomodule/core (from `../../node_modules/react-native/ReactCommon`)
- ReactTestApp-DevSupport (from `../../node_modules/react-native-test-app`)
@@ -400,6 +755,10 @@ EXTERNAL SOURCES:
:path: "../../node_modules/react-native/React/CoreModules"
React-cxxreact:
:path: "../../node_modules/react-native/ReactCommon/cxxreact"
+ React-Fabric:
+ :path: "../../node_modules/react-native/ReactCommon"
+ React-graphics:
+ :path: "../../node_modules/react-native/ReactCommon/react/renderer/graphics"
React-jsc:
:path: "../../node_modules/react-native/ReactCommon/jsc"
React-jsi:
@@ -422,6 +781,8 @@ EXTERNAL SOURCES:
:path: "../../node_modules/react-native/Libraries/AppDelegate"
React-RCTBlob:
:path: "../../node_modules/react-native/Libraries/Blob"
+ React-RCTFabric:
+ :path: "../../node_modules/react-native/React"
React-RCTImage:
:path: "../../node_modules/react-native/Libraries/Image"
React-RCTLinking:
@@ -434,6 +795,8 @@ EXTERNAL SOURCES:
:path: "../../node_modules/react-native/Libraries/Text"
React-RCTVibration:
:path: "../../node_modules/react-native/Libraries/Vibration"
+ React-rncore:
+ :path: "../../node_modules/react-native/ReactCommon"
React-runtimeexecutor:
:path: "../../node_modules/react-native/ReactCommon/runtimeexecutor"
ReactCommon:
@@ -453,7 +816,7 @@ SPEC CHECKSUMS:
boost: 57d2868c099736d80fcd648bf211b4431e51a558
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
FBLazyVector: ddb55c55295ea51ed98aa7e2e08add2f826309d5
- FBReactNativeSpec: a11dff868857a8444f7defde274215293413d93a
+ FBReactNativeSpec: 33a87f65f1a467d5f63d11d0cc106a10d3b0639d
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
@@ -461,10 +824,12 @@ SPEC CHECKSUMS:
RCTTypeSafety: 5fbddd8eb9242b91ac0d901c01da3673f358b1b7
React: e5d2d559e89d256a1d6da64d51adaecda9c8ddae
React-callinvoker: 352ecbafbdccca5fdf4aed99c98ae5b7fc28e39b
- React-Codegen: 522da503ff4f6db2b4596599875ef30b4cae4ebe
+ React-Codegen: 529e89bd96c76c7005571788ca860cc4083a78fe
React-Core: 4c13dd381dfcb8264e8ee1b981ed4ead79b99b63
React-CoreModules: 63f7f9fda3d4b214040a80e3f47ab4fb9a3e88e6
React-cxxreact: 6a455b69788c129a4f8e0c82488511cfdd9cdfc5
+ React-Fabric: a39f481ea7995898a9f153c17e73a7cc2ee330d6
+ React-graphics: 757a50b1b1cd76eb443d14d4de463cce24292223
React-jsc: b2dfd6271a8842d445ec85f373c49c574f5a7fcd
React-jsi: d41d5574025585f1d71ced8aa8865c40d463a6cf
React-jsiexecutor: 634df557a683cab436e4b3bc46512a6e519d19e8
@@ -474,20 +839,22 @@ SPEC CHECKSUMS:
React-perflogger: 217095464d5c4bb70df0742fa86bf2a363693468
React-RCTActionSheet: 8deae9b85a4cbc6a2243618ea62a374880a2c614
React-RCTAnimation: 59c62353a8b59ce206044786c5d30e4754bffa64
- React-RCTAppDelegate: 0159735c684803f36c17f43443dbe8fb1c3822c4
+ React-RCTAppDelegate: 1042986fe63a285eb5924bf5d0c0871585d2994d
React-RCTBlob: dcb026643fafac22f257d996a9390db987822f7e
+ React-RCTFabric: cdf6ac98e7e2c911eb8f6965eb981a4531213aca
React-RCTImage: 36c0324ff499802b9874d6803ca72026e90434f6
React-RCTLinking: 401aec3a01b18c2c8ed93bf3a6758b87e617c58d
React-RCTNetwork: cb25b9f2737c3aa2cde0fe0bd7ff7fabf7bf9ad0
React-RCTSettings: cb6ae9f656e1c880500c2ecbe8e72861c2262afa
React-RCTText: 7404fd01809244d79d456f92cfe6f9fbadf69209
React-RCTVibration: d13cc2d63286c633393d3a7f6f607cc2a09ec011
+ React-rncore: 0ed130c0a368c85d94e4941e675428effb8358e9
React-runtimeexecutor: a9a1cd79996c9a0846e3232ecb25c64e1cc0172e
ReactCommon: 86289421205f793f8b50106aabca54f0b4abb574
ReactTestApp-DevSupport: 1fa43e1284fd97b62238fb34c8bb349a80721b0d
ReactTestApp-Resources: ff5f151e465e890010b417ce65ca6c5de6aeccbb
- RNDateTimePicker: 4cb76f42ef1411f5e0cfc69758b7d9d7a30e12e4
- RNLocalize: 5944c97d2fe8150913a51ddd5eab4e23a82bd80d
+ RNDateTimePicker: 7ba754319710d777f8ed14e83296f87d113b2b53
+ RNLocalize: 9c4950ae13d7bfaeaac010ecad97abbe96abdfff
Yoga: e7ea9e590e27460d28911403b894722354d73479
PODFILE CHECKSUM: 8d21f6b1c802d4b966f0ed4d84584d57bd07fc86
diff --git a/ios/RNDateTimePickerManager.m b/ios/RNDateTimePickerManager.m
index dab0f9a7..686fb534 100644
--- a/ios/RNDateTimePickerManager.m
+++ b/ios/RNDateTimePickerManager.m
@@ -181,15 +181,17 @@ + (NSString*) datepickerStyleToString: (UIDatePickerStyle) style API_AVAILABLE(
RCT_CUSTOM_VIEW_PROPERTY(timeZoneName, NSString, RNDateTimePicker)
{
- if (json) {
- NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:json];
- if (timeZone != nil) {
- [view setTimeZone:timeZone];
- return;
+ if (json) {
+ NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:json];
+ if (timeZone == nil) {
+ RCTLogWarn(@"'%@' does not exist in NSTimeZone.knownTimeZoneNames fallback to localTimeZone=%@", json, NSTimeZone.localTimeZone.name);
+ view.timeZone = NSTimeZone.localTimeZone;
+ } else {
+ view.timeZone = timeZone;
+ }
+ } else {
+ view.timeZone = NSTimeZone.localTimeZone;
}
- }
- RCTLogWarn(@"'%@' does not exist in NSTimeZone.knownTimeZoneNames fallback to localTimeZone=%@", json, NSTimeZone.localTimeZone.name);
- [view setTimeZone:[NSTimeZone localTimeZone]];
}
@end
diff --git a/ios/RNDateTimePickerShadowView.m b/ios/RNDateTimePickerShadowView.m
index a91527fa..4e756aef 100644
--- a/ios/RNDateTimePickerShadowView.m
+++ b/ios/RNDateTimePickerShadowView.m
@@ -57,7 +57,7 @@ static YGSize RNDateTimePickerShadowViewMeasure(YGNodeRef node, float width, YGM
[shadowPickerView.picker setTimeZone: timeZone];
} else {
RCTLogWarn(@"'%@' does not exist in NSTimeZone.knownTimeZoneNames fallback to localTimeZone=%@", shadowPickerView.timeZoneName, NSTimeZone.localTimeZone.name);
- [shadowPickerView.picker setTimeZone:[NSTimeZone localTimeZone]];
+ [shadowPickerView.picker setTimeZone:NSTimeZone.localTimeZone];
}
if (@available(iOS 14.0, *)) {
diff --git a/ios/fabric/RNDateTimePickerComponentView.mm b/ios/fabric/RNDateTimePickerComponentView.mm
index 22c077db..8b9ec18c 100644
--- a/ios/fabric/RNDateTimePickerComponentView.mm
+++ b/ios/fabric/RNDateTimePickerComponentView.mm
@@ -218,17 +218,23 @@ - (Boolean)updatePropsForPicker:(UIDatePicker *)picker props:(Props::Shared cons
if (oldPickerProps.timeZoneOffsetInMinutes != newPickerProps.timeZoneOffsetInMinutes) {
// JS standard for time zones is minutes.
picker.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:newPickerProps.timeZoneOffsetInMinutes * 60.0];
+ needsToUpdateMeasurements = true;
}
if (oldPickerProps.timeZoneName != newPickerProps.timeZoneName) {
NSString *timeZoneName = [NSString stringWithUTF8String:newPickerProps.timeZoneName.c_str()];
- NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:timeZoneName];
- if (timeZone != nil) {
- picker.timeZone = timeZone;
+ if ([@"" isEqualToString:timeZoneName]) {
+ picker.timeZone = NSTimeZone.localTimeZone;
} else {
- RCTLogWarn(@"'%@' does not exist in NSTimeZone.knownTimeZoneNames fallback to localTimeZone=%@", timeZoneName, NSTimeZone.localTimeZone.name);
- picker.timeZone = [NSTimeZone localTimeZone];
+ NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:timeZoneName];
+ if (timeZone != nil) {
+ picker.timeZone = timeZone;
+ } else {
+ RCTLogWarn(@"'%@' does not exist in NSTimeZone.knownTimeZoneNames fallback to localTimeZone=%@", timeZoneName, NSTimeZone.localTimeZone.name);
+ picker.timeZone = NSTimeZone.localTimeZone;
+ }
}
+ needsToUpdateMeasurements = true;
}
if (oldPickerProps.accentColor != newPickerProps.accentColor) {
diff --git a/src/DateTimePickerAndroid.android.js b/src/DateTimePickerAndroid.android.js
index 46fba34c..9b495114 100644
--- a/src/DateTimePickerAndroid.android.js
+++ b/src/DateTimePickerAndroid.android.js
@@ -89,21 +89,20 @@ function open(props: AndroidNativeProps) {
switch (action) {
case DATE_SET_ACTION:
case TIME_SET_ACTION: {
- let date = new Date(timestamp);
- const [event] = createDateTimeSetEvtParams(date);
- event.nativeEvent.utcOffset = utcOffset;
- onChange?.(event, new Date(timestamp));
+ const date = new Date(timestamp);
+ const [event] = createDateTimeSetEvtParams(date, utcOffset);
+ onChange?.(event, date);
break;
}
case NEUTRAL_BUTTON_ACTION: {
- const [event] = createNeutralEvtParams(originalValue);
+ const [event] = createNeutralEvtParams(originalValue, utcOffset);
onChange?.(event, originalValue);
break;
}
case DISMISS_ACTION:
default: {
- const [event] = createDismissEvtParams(originalValue);
+ const [event] = createDismissEvtParams(originalValue, utcOffset);
onChange?.(event, originalValue);
break;
}
diff --git a/src/datetimepicker.ios.js b/src/datetimepicker.ios.js
index c5f71158..e040136b 100644
--- a/src/datetimepicker.ios.js
+++ b/src/datetimepicker.ios.js
@@ -84,6 +84,7 @@ export default function Picker({
type: EVENT_TYPE_DISMISSED,
nativeEvent: {
timestamp: value.getTime(),
+ utcOffset: 0, // TODO vonovak - the dismiss event should not carry any date information
},
},
value,
diff --git a/src/eventCreators.js b/src/eventCreators.js
index 121ee0f2..cd16fbd9 100644
--- a/src/eventCreators.js
+++ b/src/eventCreators.js
@@ -6,12 +6,14 @@ import {ANDROID_EVT_TYPE, EVENT_TYPE_SET} from './constants';
export const createDateTimeSetEvtParams = (
date: Date,
+ utcOffset: number,
): [DateTimePickerEvent, Date] => {
return [
{
type: EVENT_TYPE_SET,
nativeEvent: {
timestamp: date.getTime(),
+ utcOffset,
},
},
date,
@@ -20,12 +22,14 @@ export const createDateTimeSetEvtParams = (
export const createDismissEvtParams = (
date: Date,
+ utcOffset: number,
): [DateTimePickerEvent, Date] => {
return [
{
type: ANDROID_EVT_TYPE.dismissed,
nativeEvent: {
timestamp: date.getTime(),
+ utcOffset,
},
},
date,
@@ -34,12 +38,14 @@ export const createDismissEvtParams = (
export const createNeutralEvtParams = (
date: Date,
+ utcOffset: number,
): [DateTimePickerEvent, Date] => {
return [
{
type: ANDROID_EVT_TYPE.neutralButtonPressed,
nativeEvent: {
timestamp: date.getTime(),
+ utcOffset,
},
},
date,
diff --git a/src/types.js b/src/types.js
index d0595fdd..d2630ed3 100644
--- a/src/types.js
+++ b/src/types.js
@@ -37,7 +37,7 @@ export type DateTimePickerEvent = {
type: AndroidEvtTypes,
nativeEvent: $ReadOnly<{
timestamp: number,
- utcOffset?: number,
+ utcOffset: number,
...
}>,
...
diff --git a/src/utils.js b/src/utils.js
index 79a2e5fe..9166e87f 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -35,7 +35,7 @@ export function sharedPropsValidation({
timeZoneName,
timeZoneOffsetInMinutes,
}: {
- value: ?Date,
+ value: Date,
timeZoneName?: ?string,
timeZoneOffsetInMinutes?: ?number,
}) {
@@ -45,10 +45,7 @@ export function sharedPropsValidation({
'`value` prop must be an instance of Date object',
);
invariant(
- timeZoneName == null ||
- timeZoneName === undefined ||
- timeZoneOffsetInMinutes == null ||
- timeZoneOffsetInMinutes === undefined,
+ timeZoneName == null || timeZoneOffsetInMinutes == null,
'`timeZoneName` and `timeZoneOffsetInMinutes` cannot be specified at the same time',
);
}
diff --git a/test/userlandTestExamples.test.js b/test/userlandTestExamples.test.js
index 9b247a8c..ddcf26dd 100644
--- a/test/userlandTestExamples.test.js
+++ b/test/userlandTestExamples.test.js
@@ -77,7 +77,7 @@ describe('userland tests', () => {
fireEvent(
UNSAFE_getByType(DateTimePicker),
'onChange',
- ...createDateTimeSetEvtParams(date),
+ ...createDateTimeSetEvtParams(date, 0),
);
getByText('1560000000');
});
From d96d64d8969a5a5ea776b022cd1ae2e0a459c4c6 Mon Sep 17 00:00:00 2001
From: Vojtech Novak
Date: Tue, 29 Aug 2023 12:27:47 +0200
Subject: [PATCH 3/7] refactor: e2e review
---
example/App.js | 9 +-
example/e2e/detoxTest.spec.js | 246 ++++++++++----------------------
example/e2e/utils/assertions.js | 18 +++
3 files changed, 98 insertions(+), 175 deletions(-)
create mode 100644 example/e2e/utils/assertions.js
diff --git a/example/App.js b/example/App.js
index 4cad63de..2a591ac0 100644
--- a/example/App.js
+++ b/example/App.js
@@ -161,7 +161,7 @@ export const App = () => {
};
const renderItem = ({item}) => {
- const isNumber = !isNaN(item);
+ const isNumber = typeof item === 'number';
const title = isNumber
? item > 0
? `+${item} mins`
@@ -203,13 +203,6 @@ export const App = () => {
{flex: 1, backgroundColor: isDarkMode ? Colors.black : Colors.white},
]}>
- {global.HermesInternal != null && (
-
-
- Engine: Hermes
-
-
- )}
Example DateTime Picker
diff --git a/example/e2e/detoxTest.spec.js b/example/e2e/detoxTest.spec.js
index a5dc3884..3c6b63b1 100644
--- a/example/e2e/detoxTest.spec.js
+++ b/example/e2e/detoxTest.spec.js
@@ -16,6 +16,10 @@ const {
const {isIOS, isAndroid, wait, Platform} = require('./utils/utils');
const {device} = require('detox');
const {describe} = require('jest-circus');
+const {
+ assertTimeLabels,
+ assertInitialTimeLabels,
+} = require('./utils/assertions');
describe('e2e tests', () => {
const getPickerDisplay = () => {
@@ -40,14 +44,8 @@ describe('e2e tests', () => {
});
it('timeInfo heading has expected content', async () => {
- await expect(elementById('utcTime')).toHaveText('2021-11-13T01:00:00Z');
- await expect(elementById('deviceTime')).toHaveText(
- '2021-11-13T02:00:00+01:00',
- );
+ await assertInitialTimeLabels();
await expect(elementById('deviceTzName')).toHaveText('Europe/Prague');
- await expect(elementById('overriddenTime')).toHaveText(
- '2021-11-13T02:00:00+01:00',
- );
await expect(elementById('overriddenTzName')).toHaveText('Europe/Prague');
});
@@ -61,7 +59,7 @@ describe('e2e tests', () => {
}
});
- it('nothing should happen if picker is dismissed / cancelled', async () => {
+ it('nothing should happen if date picker is dismissed / cancelled', async () => {
await userOpensPicker({mode: 'date', display: 'default'});
if (isIOS()) {
@@ -86,21 +84,16 @@ describe('e2e tests', () => {
}
await elementByText('great').tap();
- await expect(element(by.id('utcTime'))).toHaveText('2021-11-13T01:00:00Z');
- await expect(element(by.id('deviceTime'))).toHaveText(
- '2021-11-13T02:00:00+01:00',
- );
- await expect(element(by.id('overriddenTime'))).toHaveText(
- '2021-11-13T02:00:00+01:00',
- );
+ await assertInitialTimeLabels();
});
it('should update dateTimeText when date changes', async () => {
await userOpensPicker({mode: 'date', display: getPickerDisplay()});
+ const targetDate = '2021-11-02T01:00:00Z';
if (isIOS()) {
const testElement = getDateTimePickerControlIOS();
- await testElement.setDatePickerDate('2021-11-02T01:00:00Z', 'ISO8601');
+ await testElement.setDatePickerDate(targetDate, 'ISO8601');
} else {
const uiDevice = device.getUiDevice();
const focusSecondOfNovemberInCalendar = async () => {
@@ -115,17 +108,14 @@ describe('e2e tests', () => {
await userTapsOkButtonAndroid();
}
- await expect(element(by.id('utcTime'))).toHaveText('2021-11-02T01:00:00Z');
- await expect(element(by.id('deviceTime'))).toHaveText(
- '2021-11-02T02:00:00+01:00',
- );
- await expect(element(by.id('overriddenTime'))).toHaveText(
- '2021-11-02T02:00:00+01:00',
- );
+ await assertTimeLabels({
+ utcTime: targetDate,
+ deviceTime: '2021-11-02T02:00:00+01:00',
+ });
});
it('should show time picker after tapping timePicker button', async () => {
- const display = await Platform.select({
+ const display = Platform.select({
ios: 'inline',
android: 'default',
});
@@ -148,13 +138,7 @@ describe('e2e tests', () => {
await userTapsCancelButtonAndroid();
await elementByText('great').tap();
}
- await expect(element(by.id('utcTime'))).toHaveText('2021-11-13T01:00:00Z');
- await expect(element(by.id('deviceTime'))).toHaveText(
- '2021-11-13T02:00:00+01:00',
- );
- await expect(element(by.id('overriddenTime'))).toHaveText(
- '2021-11-13T02:00:00+01:00',
- );
+ await assertInitialTimeLabels();
});
it('should change time text when time changes', async () => {
@@ -169,33 +153,19 @@ describe('e2e tests', () => {
await userTapsOkButtonAndroid();
}
- await expect(element(by.id('utcTime'))).toHaveText('2021-11-13T14:44:00Z');
- await expect(element(by.id('deviceTime'))).toHaveText(
- '2021-11-13T15:44:00+01:00',
- );
- await expect(element(by.id('overriddenTime'))).toHaveText(
- '2021-11-13T15:44:00+01:00',
- );
+ await assertTimeLabels({
+ utcTime: '2021-11-13T14:44:00Z',
+ deviceTime: '2021-11-13T15:44:00+01:00',
+ });
});
describe('IANA time zone', () => {
it('should show utcTime, deviceTime, overriddenTime correctly', async () => {
- await expect(element(by.id('utcTime'))).toHaveText(
- '2021-11-13T01:00:00Z',
- );
- await expect(element(by.id('deviceTime'))).toHaveText(
- '2021-11-13T02:00:00+01:00',
- );
- await expect(element(by.id('overriddenTime'))).toHaveText(
- '2021-11-13T02:00:00+01:00',
- );
+ await assertInitialTimeLabels();
- await expect(element(by.id('deviceTzName'))).toHaveText('Europe/Prague');
- await expect(element(by.id('overriddenTzName'))).toHaveText(
- 'Europe/Prague',
- );
+ await expect(elementById('overriddenTzName')).toHaveText('Europe/Prague');
- await element(by.id('timezone')).swipe('left', 'fast', 0.5);
+ await elementById('timezone').swipe('left', 'fast', 0.5);
let timeZone = 'America/Vancouver';
if (isAndroid()) {
@@ -206,23 +176,19 @@ describe('e2e tests', () => {
await elementByText(timeZone).tap();
- await expect(element(by.id('utcTime'))).toHaveText(
- '2021-11-13T01:00:00Z',
- );
- await expect(element(by.id('deviceTime'))).toHaveText(
- '2021-11-13T02:00:00+01:00',
- );
- await expect(element(by.id('overriddenTime'))).toHaveText(
- '2021-11-12T17:00:00-08:00',
- );
- await expect(element(by.id('deviceTzName'))).toHaveText('Europe/Prague');
- await expect(element(by.id('overriddenTzName'))).toHaveText(
+ await assertTimeLabels({
+ utcTime: '2021-11-13T01:00:00Z',
+ deviceTime: '2021-11-13T02:00:00+01:00',
+ overriddenTime: '2021-11-12T17:00:00-08:00',
+ });
+
+ await expect(elementById('overriddenTzName')).toHaveText(
'America/Vancouver',
);
});
it('daylight saving should work properly', async () => {
- await element(by.id('timezone')).swipe('left', 'fast', 0.5);
+ await elementById('timezone').swipe('left', 'fast', 0.5);
let timeZone = 'America/Vancouver';
if (isAndroid()) {
@@ -267,29 +233,17 @@ describe('e2e tests', () => {
await userTapsOkButtonAndroid();
}
- await expect(element(by.id('utcTime'))).toHaveText(
- '2021-03-14T10:00:00Z',
- );
- await expect(element(by.id('deviceTime'))).toHaveText(
- '2021-03-14T11:00:00+01:00',
- );
- await expect(element(by.id('overriddenTime'))).toHaveText(
- '2021-03-14T03:00:00-07:00',
- );
+ await assertTimeLabels({
+ utcTime: '2021-03-14T10:00:00Z',
+ deviceTime: '2021-03-14T11:00:00+01:00',
+ overriddenTime: '2021-03-14T03:00:00-07:00',
+ });
});
});
describe('time zone offset', () => {
it('should update dateTimeText when date changes and set setTzOffsetInMinutes to 0', async () => {
- await expect(element(by.id('utcTime'))).toHaveText(
- '2021-11-13T01:00:00Z',
- );
- await expect(element(by.id('deviceTime'))).toHaveText(
- '2021-11-13T02:00:00+01:00',
- );
- await expect(element(by.id('overriddenTime'))).toHaveText(
- '2021-11-13T02:00:00+01:00',
- );
+ await assertInitialTimeLabels();
let tzOffsetPreset = '0 mins';
@@ -308,7 +262,7 @@ describe('e2e tests', () => {
});
await userTapsOkButtonAndroid();
}
- await expect(element(by.id('overriddenTime'))).toHaveText(
+ await expect(elementById('overriddenTime')).toHaveText(
'2021-11-13T01:00:00Z',
);
});
@@ -336,15 +290,11 @@ describe('e2e tests', () => {
await userChangesTimeValue({hours: '7', minutes: '30'});
await userTapsOkButtonAndroid();
}
- await expect(element(by.id('utcTime'))).toHaveText(
- '2021-11-13T05:30:00Z',
- );
- await expect(element(by.id('deviceTime'))).toHaveText(
- '2021-11-13T06:30:00+01:00',
- );
- await expect(element(by.id('overriddenTime'))).toHaveText(
- '2021-11-13T07:30:00+02:00',
- );
+ await assertTimeLabels({
+ utcTime: '2021-11-13T05:30:00Z',
+ deviceTime: '2021-11-13T06:30:00+01:00',
+ overriddenTime: '2021-11-13T07:30:00+02:00',
+ });
});
it('should let you pick tomorrow but not yesterday when setting min/max', async () => {
@@ -357,9 +307,7 @@ describe('e2e tests', () => {
// Ensure you can't select yesterday (iOS)
await testElement.setDatePickerDate('2021-11-12T01:00:00Z', 'ISO8601');
- await expect(element(by.id('utcTime'))).toHaveText(
- '2021-11-13T00:00:00Z',
- );
+ await expect(elementById('utcTime')).toHaveText('2021-11-13T00:00:00Z');
// Ensure you can select tomorrow (iOS)
await userOpensPicker({mode: 'date', display: getPickerDisplay()});
@@ -368,7 +316,7 @@ describe('e2e tests', () => {
const uiDevice = device.getUiDevice();
// Ensure you can't select yesterday (Android)
- const focusTwelethOfNovemberInCalendar = async () => {
+ const focusTwelveOfNovemberInCalendar = async () => {
for (let i = 0; i < 4; i++) {
await uiDevice.pressDPadDown();
}
@@ -376,19 +324,15 @@ describe('e2e tests', () => {
await uiDevice.pressDPadRight();
}
};
- await focusTwelethOfNovemberInCalendar();
+ await focusTwelveOfNovemberInCalendar();
await uiDevice.pressEnter();
await userTapsOkButtonAndroid();
- await expect(element(by.id('utcTime'))).toHaveText(
- '2021-11-13T01:00:00Z',
- );
- await expect(element(by.id('deviceTime'))).toHaveText(
- '2021-11-13T02:00:00+01:00',
- );
- await expect(element(by.id('overriddenTime'))).toHaveText(
- '2021-11-13T01:00:00Z',
- );
+ await assertTimeLabels({
+ utcTime: '2021-11-13T01:00:00Z',
+ deviceTime: '2021-11-13T02:00:00+01:00',
+ overriddenTime: '2021-11-13T01:00:00Z',
+ });
// Ensure you can select tomorrow (Android)
await userOpensPicker({mode: 'date', display: getPickerDisplay()});
@@ -405,15 +349,11 @@ describe('e2e tests', () => {
await userTapsOkButtonAndroid();
}
- await expect(element(by.id('utcTime'))).toHaveText(
- '2021-11-14T01:00:00Z',
- );
- await expect(element(by.id('deviceTime'))).toHaveText(
- '2021-11-14T02:00:00+01:00',
- );
- await expect(element(by.id('overriddenTime'))).toHaveText(
- '2021-11-14T01:00:00Z',
- );
+ await assertTimeLabels({
+ utcTime: '2021-11-14T01:00:00Z',
+ deviceTime: '2021-11-14T02:00:00+01:00',
+ overriddenTime: '2021-11-14T01:00:00Z',
+ });
});
});
@@ -422,13 +362,10 @@ describe('e2e tests', () => {
await userOpensPicker({mode: 'time', display: 'default'});
await elementByText('clear').tap();
- await expect(element(by.id('utcTime'))).toHaveText('1970-01-01T00:00:00Z');
- await expect(element(by.id('deviceTime'))).toHaveText(
- '1970-01-01T01:00:00+01:00',
- );
- await expect(element(by.id('overriddenTime'))).toHaveText(
- '1970-01-01T01:00:00+01:00',
- );
+ await assertTimeLabels({
+ utcTime: '1970-01-01T00:00:00Z',
+ deviceTime: '1970-01-01T01:00:00+01:00',
+ });
});
it(':android: when component unmounts, dialog is dismissed', async () => {
@@ -447,27 +384,17 @@ describe('e2e tests', () => {
await userTapsOkButtonAndroid();
- await expect(element(by.id('utcTime'))).toHaveText(
- '2021-11-13T22:20:00Z',
- );
- await expect(element(by.id('deviceTime'))).toHaveText(
- '2021-11-13T23:20:00+01:00',
- );
- await expect(element(by.id('overriddenTime'))).toHaveText(
- '2021-11-13T23:20:00+01:00',
- );
+ await assertTimeLabels({
+ utcTime: '2021-11-13T22:20:00Z',
+ deviceTime: '2021-11-13T23:20:00+01:00',
+ });
});
it(':android: when the picker is shown as "spinner", swiping it down changes selected time', async () => {
- await expect(element(by.id('utcTime'))).toHaveText(
- '2021-11-13T01:00:00Z',
- );
- await expect(element(by.id('deviceTime'))).toHaveText(
- '2021-11-13T02:00:00+01:00',
- );
- await expect(element(by.id('overriddenTime'))).toHaveText(
- '2021-11-13T02:00:00+01:00',
- );
+ await assertTimeLabels({
+ utcTime: '2021-11-13T01:00:00Z',
+ deviceTime: '2021-11-13T02:00:00+01:00',
+ });
await userOpensPicker({mode: 'time', display: 'spinner', interval: 5});
@@ -477,15 +404,10 @@ describe('e2e tests', () => {
await minutePicker.swipe('up', 'slow', 0.33);
await userTapsOkButtonAndroid();
- await expect(element(by.id('utcTime'))).toHaveText(
- '2021-11-13T01:15:00Z',
- );
- await expect(element(by.id('deviceTime'))).toHaveText(
- '2021-11-13T02:15:00+01:00',
- );
- await expect(element(by.id('overriddenTime'))).toHaveText(
- '2021-11-13T02:15:00+01:00',
- );
+ await assertTimeLabels({
+ utcTime: '2021-11-13T01:15:00Z',
+ deviceTime: '2021-11-13T02:15:00+01:00',
+ });
});
it(':ios: picker should offer only options divisible by 5 (0, 5, 10,...)', async () => {
@@ -496,15 +418,10 @@ describe('e2e tests', () => {
await testElement.setColumnToValue(1, '15');
await testElement.setColumnToValue(2, 'PM');
- await expect(element(by.id('utcTime'))).toHaveText(
- '2021-11-13T13:15:00Z',
- );
- await expect(element(by.id('deviceTime'))).toHaveText(
- '2021-11-13T14:15:00+01:00',
- );
- await expect(element(by.id('overriddenTime'))).toHaveText(
- '2021-11-13T14:15:00+01:00',
- );
+ await assertTimeLabels({
+ utcTime: '2021-11-13T13:15:00Z',
+ deviceTime: '2021-11-13T14:15:00+01:00',
+ });
const valueThatShouldNotBePresented = '18';
try {
@@ -522,15 +439,10 @@ describe('e2e tests', () => {
}
}
- await expect(element(by.id('utcTime'))).toHaveText(
- '2021-11-13T13:45:00Z',
- );
- await expect(element(by.id('deviceTime'))).toHaveText(
- '2021-11-13T14:45:00+01:00',
- );
- await expect(element(by.id('overriddenTime'))).toHaveText(
- '2021-11-13T14:45:00+01:00',
- );
+ await assertTimeLabels({
+ utcTime: '2021-11-13T13:45:00Z',
+ deviceTime: '2021-11-13T14:45:00+01:00',
+ });
});
});
});
diff --git a/example/e2e/utils/assertions.js b/example/e2e/utils/assertions.js
new file mode 100644
index 00000000..5cfab683
--- /dev/null
+++ b/example/e2e/utils/assertions.js
@@ -0,0 +1,18 @@
+const {elementById} = require('./matchers');
+
+async function assertTimeLabels({utcTime, deviceTime, overriddenTime}) {
+ await expect(elementById('utcTime')).toHaveText(utcTime);
+ await expect(elementById('deviceTime')).toHaveText(deviceTime);
+ await expect(elementById('overriddenTime')).toHaveText(
+ overriddenTime ?? deviceTime,
+ );
+}
+
+async function assertInitialTimeLabels() {
+ return await assertTimeLabels({
+ utcTime: '2021-11-13T01:00:00Z',
+ deviceTime: '2021-11-13T02:00:00+01:00',
+ overriddenTime: '2021-11-13T02:00:00+01:00',
+ });
+}
+module.exports = {assertTimeLabels, assertInitialTimeLabels};
From 86ab6b482c987afa5e3f216f1807b72db5e0641c Mon Sep 17 00:00:00 2001
From: Vojtech Novak
Date: Tue, 29 Aug 2023 12:30:49 +0200
Subject: [PATCH 4/7] refactor: fix flow
---
src/datetimepicker.windows.js | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/datetimepicker.windows.js b/src/datetimepicker.windows.js
index 6bc29b22..3a0e7782 100644
--- a/src/datetimepicker.windows.js
+++ b/src/datetimepicker.windows.js
@@ -54,7 +54,11 @@ export default function RNDateTimePickerQWE(
const {onChange} = props;
const unifiedEvent: DateTimePickerEvent = {
...event,
- nativeEvent: {...event.nativeEvent, timestamp: event.nativeEvent.newDate},
+ nativeEvent: {
+ ...event.nativeEvent,
+ timestamp: event.nativeEvent.newDate,
+ utcOffset: 0,
+ },
type: EVENT_TYPE_SET,
};
From ecbe75338576b99323cc647aa2d0f9751bcc9021 Mon Sep 17 00:00:00 2001
From: Vojtech Novak
Date: Tue, 29 Aug 2023 13:54:02 +0200
Subject: [PATCH 5/7] docs: readme
---
README.md | 21 ++++++++++++++++++---
src/index.d.ts | 3 ++-
src/utils.js | 5 +++++
3 files changed, 25 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
index 179829af..47bfebc8 100644
--- a/README.md
+++ b/README.md
@@ -66,6 +66,7 @@ React Native date & time picker component for iOS, Android and Windows.
- [`value` (`required`)](#value-required)
- [`maximumDate` (`optional`)](#maximumdate-optional)
- [`minimumDate` (`optional`)](#minimumdate-optional)
+ - [`timeZoneName` (`optional`, `iOS or Android only`)](#timeZoneName-optional-ios-and-android-only)
- [`timeZoneOffsetInMinutes` (`optional`, `iOS or Android only`)](#timezoneoffsetinminutes-optional-ios-and-android-only)
- [`timeZoneOffsetInSeconds` (`optional`, `Windows only`)](#timezoneoffsetinsecond-optional-windows-only)
- [`dayOfWeekFormat` (`optional`, `Windows only`)](#dayOfWeekFormat-optional-windows-only)
@@ -309,11 +310,13 @@ This is called when the user changes the date or time in the UI. It receives the
It is also called when user dismisses the picker, which you can detect by checking the `event.type` property.
The values can be: `'set' | 'dismissed' | 'neutralButtonPressed'`. (`neutralButtonPressed` is only available on Android).
+The `utcOffset` field is only available on Android and iOS. It is the offset in minutes between the selected date and UTC time.
+
```js
const setDate = (event: DateTimePickerEvent, date: Date) => {
const {
type,
- nativeEvent: {timestamp},
+ nativeEvent: {timestamp, utcOffset},
} = event;
};
@@ -344,10 +347,22 @@ Defines the minimum date that can be selected. Note that on Android, this only w
```
+#### `timeZoneName` (`optional`, `iOS and Android only`)
+
+Allows changing of the time zone of the date picker. By default, it uses the device's time zone.
+Use the time zone name from the IANA (TZDB) database name in https://en.wikipedia.org/wiki/List_of_tz_database_time_zones.
+
+```js
+// GMT+1
+
+```
+
#### `timeZoneOffsetInMinutes` (`optional`, `iOS and Android only`)
-Allows changing of the timeZone of the date picker. By default, it uses the device's time zone.
-We strongly recommend avoiding this prop on android because of known issues in the implementation (eg. [#528](https://github.com/react-native-datetimepicker/datetimepicker/issues/528)).
+Allows changing of the time zone of the date picker. By default, it uses the device's time zone.
+We **strongly** recommend using `timeZoneName` prop instead; this prop has known issues in the android implementation (eg. [#528](https://github.com/react-native-datetimepicker/datetimepicker/issues/528)).
+
+This prop will be removed in a future release.
```js
// GMT+1
diff --git a/src/index.d.ts b/src/index.d.ts
index cd4214d3..850868b0 100644
--- a/src/index.d.ts
+++ b/src/index.d.ts
@@ -19,7 +19,8 @@ export type EvtTypes = 'set' | 'neutralButtonPressed' | 'dismissed';
export type DateTimePickerEvent = {
type: EvtTypes;
nativeEvent: {
- timestamp?: number;
+ timestamp: number;
+ utcOffset: number;
};
};
diff --git a/src/utils.js b/src/utils.js
index 9166e87f..a4dee188 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -48,4 +48,9 @@ export function sharedPropsValidation({
timeZoneName == null || timeZoneOffsetInMinutes == null,
'`timeZoneName` and `timeZoneOffsetInMinutes` cannot be specified at the same time',
);
+ if (timeZoneOffsetInMinutes !== undefined) {
+ console.warn(
+ '`timeZoneOffsetInMinutes` is deprecated and will be removed in a future release. Use `timeZoneName` instead.',
+ );
+ }
}
From 585ce15056f7403ac3f55aa6fa3e60cd881b7196 Mon Sep 17 00:00:00 2001
From: Vojtech Novak
Date: Tue, 29 Aug 2023 14:20:20 +0200
Subject: [PATCH 6/7] docs: readme
---
README.md | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 47bfebc8..c26fd2a6 100644
--- a/README.md
+++ b/README.md
@@ -353,8 +353,7 @@ Allows changing of the time zone of the date picker. By default, it uses the dev
Use the time zone name from the IANA (TZDB) database name in https://en.wikipedia.org/wiki/List_of_tz_database_time_zones.
```js
-// GMT+1
-
+
```
#### `timeZoneOffsetInMinutes` (`optional`, `iOS and Android only`)
From 861396943f52af3a4297cbfb6eda4a6380ceee45 Mon Sep 17 00:00:00 2001
From: Vojtech Novak
Date: Tue, 29 Aug 2023 14:38:27 +0200
Subject: [PATCH 7/7] fix: tz prop handling
---
.../com/reactcommunity/rndatetimepicker/Common.java | 2 +-
ios/RNDateTimePickerShadowView.m | 12 ++++++++----
ios/fabric/RNDateTimePickerComponentView.mm | 2 +-
3 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/android/src/main/java/com/reactcommunity/rndatetimepicker/Common.java b/android/src/main/java/com/reactcommunity/rndatetimepicker/Common.java
index 665000cc..9effd097 100644
--- a/android/src/main/java/com/reactcommunity/rndatetimepicker/Common.java
+++ b/android/src/main/java/com/reactcommunity/rndatetimepicker/Common.java
@@ -156,7 +156,7 @@ public static TimeZone getTimeZone(Bundle args) {
} else if (!"GMT".equals(TimeZone.getTimeZone(timeZoneName).getID())) {
return TimeZone.getTimeZone(timeZoneName);
}
- RNLog.w(null, "'" + timeZoneName + "' does not exist in TimeZone.getAvailableIDs() fallback to TimeZone.getDefault()=" + TimeZone.getDefault().getID());
+ RNLog.w(null, "'" + timeZoneName + "' does not exist in TimeZone.getAvailableIDs(). Falling back to TimeZone.getDefault()=" + TimeZone.getDefault().getID());
}
return TimeZone.getDefault();
diff --git a/ios/RNDateTimePickerShadowView.m b/ios/RNDateTimePickerShadowView.m
index 4e756aef..c139440c 100644
--- a/ios/RNDateTimePickerShadowView.m
+++ b/ios/RNDateTimePickerShadowView.m
@@ -52,11 +52,15 @@ static YGSize RNDateTimePickerShadowViewMeasure(YGNodeRef node, float width, YGM
[shadowPickerView.picker setLocale:shadowPickerView.locale];
[shadowPickerView.picker setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:shadowPickerView.timeZoneOffsetInMinutes * 60]];
- NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:shadowPickerView.timeZoneName];
- if (timeZone != nil) {
- [shadowPickerView.picker setTimeZone: timeZone];
+ if (shadowPickerView.timeZoneName) {
+ NSTimeZone *timeZone = [NSTimeZone timeZoneWithName:shadowPickerView.timeZoneName];
+ if (timeZone != nil) {
+ [shadowPickerView.picker setTimeZone:timeZone];
+ } else {
+ RCTLogWarn(@"'%@' does not exist in NSTimeZone.knownTimeZoneNames. Falling back to localTimeZone=%@", shadowPickerView.timeZoneName, NSTimeZone.localTimeZone.name);
+ [shadowPickerView.picker setTimeZone:NSTimeZone.localTimeZone];
+ }
} else {
- RCTLogWarn(@"'%@' does not exist in NSTimeZone.knownTimeZoneNames fallback to localTimeZone=%@", shadowPickerView.timeZoneName, NSTimeZone.localTimeZone.name);
[shadowPickerView.picker setTimeZone:NSTimeZone.localTimeZone];
}
diff --git a/ios/fabric/RNDateTimePickerComponentView.mm b/ios/fabric/RNDateTimePickerComponentView.mm
index 8b9ec18c..be1762d2 100644
--- a/ios/fabric/RNDateTimePickerComponentView.mm
+++ b/ios/fabric/RNDateTimePickerComponentView.mm
@@ -230,7 +230,7 @@ - (Boolean)updatePropsForPicker:(UIDatePicker *)picker props:(Props::Shared cons
if (timeZone != nil) {
picker.timeZone = timeZone;
} else {
- RCTLogWarn(@"'%@' does not exist in NSTimeZone.knownTimeZoneNames fallback to localTimeZone=%@", timeZoneName, NSTimeZone.localTimeZone.name);
+ RCTLogWarn(@"'%@' does not exist in NSTimeZone.knownTimeZoneNames. Falling back to localTimeZone=%@", timeZoneName, NSTimeZone.localTimeZone.name);
picker.timeZone = NSTimeZone.localTimeZone;
}
}