Completed
Branch BUG-9951-10331-8793-pue-fixes (40e696)
by
unknown
27:52 queued 13:57
created

EEH_DTT_Helper::tomorrow()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
if (! defined('EVENT_ESPRESSO_VERSION')) {
3
    exit('NO direct script access allowed');
4
}
5
6
/**
7
 * Event Espresso
8
 * Event Registration and Management Plugin for Wordpress
9
 *
10
 * @package         Event Espresso
11
 * @author          Seth Shoultes
12
 * @copyright    (c)2009-2012 Event Espresso All Rights Reserved.
13
 * @license         http://eventespresso.com/support/terms-conditions/  ** see Plugin Licensing **
14
 * @link            http://www.eventespresso.com
15
 * @version         4.0
16
 *                  ------------------------------------------------------------------------
17
 *                  EEH_DTT_Helper
18
 *                  This is a helper utility class containing a variety for date time formatting helpers for Event
19
 *                  Espresso.
20
 * @package         Event Espresso
21
 * @subpackage      /helpers/EEH_DTT_Helper.helper.php
22
 * @author          Darren Ethier
23
 *                  ------------------------------------------------------------------------
24
 */
25
class EEH_DTT_Helper
26
{
27
28
29
    /**
30
     * return the timezone set for the WP install
31
     *
32
     * @return string valid timezone string for PHP DateTimeZone() class
33
     */
34
    public static function get_timezone()
35
    {
36
        return EEH_DTT_Helper::get_valid_timezone_string();
37
    }
38
39
40
    /**
41
     * get_valid_timezone_string
42
     *    ensures that a valid timezone string is returned
43
     *
44
     * @access protected
45
     * @param string $timezone_string
46
     * @return string
47
     * @throws \EE_Error
48
     */
49
    public static function get_valid_timezone_string($timezone_string = '')
50
    {
51
        // if passed a value, then use that, else get WP option
52
        $timezone_string = ! empty($timezone_string) ? $timezone_string : get_option('timezone_string');
53
        // value from above exists, use that, else get timezone string from gmt_offset
54
        $timezone_string = ! empty($timezone_string) ? $timezone_string : EEH_DTT_Helper::get_timezone_string_from_gmt_offset();
55
        EEH_DTT_Helper::validate_timezone($timezone_string);
56
        return $timezone_string;
57
    }
58
59
60
    /**
61
     * This only purpose for this static method is to validate that the incoming timezone is a valid php timezone.
62
     *
63
     * @static
64
     * @access public
65
     * @param  string $timezone_string Timezone string to check
66
     * @param bool    $throw_error
67
     * @return bool
68
     * @throws \EE_Error
69
     */
70
    public static function validate_timezone($timezone_string, $throw_error = true)
71
    {
72
        // easiest way to test a timezone string is just see if it throws an error when you try to create a DateTimeZone object with it
73
        try {
74
            new DateTimeZone($timezone_string);
75
        } catch (Exception $e) {
76
            // sometimes we take exception to exceptions
77
            if (! $throw_error) {
78
                return false;
79
            }
80
            throw new EE_Error(
81
                sprintf(
82
                    __('The timezone given (%1$s), is invalid, please check with %2$sthis list%3$s for what valid timezones can be used',
83
                        'event_espresso'),
84
                    $timezone_string,
85
                    '<a href="http://www.php.net/manual/en/timezones.php">',
86
                    '</a>'
87
                )
88
            );
89
        }
90
        return true;
91
    }
92
93
94
    /**
95
     * _create_timezone_object_from_timezone_name
96
     *
97
     * @access protected
98
     * @param string $gmt_offset
99
     * @return string
100
     */
101
    public static function get_timezone_string_from_gmt_offset($gmt_offset = '')
102
    {
103
        $timezone_string = 'UTC';
104
        $gmt_offset      = ! empty($gmt_offset) ? $gmt_offset : get_option('gmt_offset');
105
        if ($gmt_offset !== '') {
106
            // convert GMT offset to seconds
107
            $gmt_offset = $gmt_offset * HOUR_IN_SECONDS;
108
            // account for WP offsets that aren't valid UTC
109
            $gmt_offset = EEH_DTT_Helper::adjust_invalid_gmt_offsets($gmt_offset);
110
            // although we don't know the TZ abbreviation, we know the UTC offset
111
            $timezone_string = timezone_name_from_abbr(null, $gmt_offset);
112
        }
113
        // better have a valid timezone string by now, but if not, sigh... loop thru  the timezone_abbreviations_list()...
114
        $timezone_string = $timezone_string !== false
115
            ? $timezone_string
116
            : EEH_DTT_Helper::get_timezone_string_from_abbreviations_list($gmt_offset);
117
        return $timezone_string;
118
    }
119
120
    /**
121
     * Gets the site's GMT offset based on either the timezone string
122
     * (in which case teh gmt offset will vary depending on the location's
123
     * observance of daylight savings time) or the gmt_offset wp option
124
     *
125
     * @return int seconds offset
126
     */
127
    public static function get_site_timezone_gmt_offset()
128
    {
129
        $timezone_string = get_option('timezone_string');
130
        if ($timezone_string) {
131
            try {
132
                $timezone = new DateTimeZone($timezone_string);
133
                return $timezone->getOffset(new DateTime()); //in WordPress DateTime defaults to UTC
134
            } catch (Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
135
            }
136
        }
137
        $offset = get_option('gmt_offset');
138
        return (int)($offset * HOUR_IN_SECONDS);
139
    }
140
141
142
    /**
143
     * _create_timezone_object_from_timezone_name
144
     *
145
     * @access public
146
     * @param int $gmt_offset
147
     * @return int
148
     */
149
    public static function adjust_invalid_gmt_offsets($gmt_offset = 0)
150
    {
151
        //make sure $gmt_offset is int
152
        $gmt_offset = (int)$gmt_offset;
153
        switch ($gmt_offset) {
154
155
            //			case -30600 :
156
            //				$gmt_offset = -28800;
157
            //				break;
158
159
            case -27000 :
160
                $gmt_offset = -25200;
161
                break;
162
163
            case -23400 :
164
                $gmt_offset = -21600;
165
                break;
166
167
            case -19800 :
168
                $gmt_offset = -18000;
169
                break;
170
171
            case -9000 :
172
                $gmt_offset = -7200;
173
                break;
174
175
            case -5400 :
176
                $gmt_offset = -3600;
177
                break;
178
179
            case -1800 :
180
                $gmt_offset = 0;
181
                break;
182
183
            case 1800 :
184
                $gmt_offset = 3600;
185
                break;
186
187
            case 49500 :
188
                $gmt_offset = 50400;
189
                break;
190
191
        }
192
        return $gmt_offset;
193
    }
194
195
196
    /**
197
     * get_timezone_string_from_abbreviations_list
198
     *
199
     * @access public
200
     * @param int $gmt_offset
201
     * @return string
202
     * @throws \EE_Error
203
     */
204
    public static function get_timezone_string_from_abbreviations_list($gmt_offset = 0)
205
    {
206
        $abbreviations = timezone_abbreviations_list();
207
        foreach ($abbreviations as $abbreviation) {
208
            foreach ($abbreviation as $city) {
209
                if ($city['offset'] === $gmt_offset && $city['dst'] === false) {
210
                    // check if the timezone is valid but don't throw any errors if it isn't
211
                    if (EEH_DTT_Helper::validate_timezone($city['timezone_id'], false)) {
212
                        return $city['timezone_id'];
213
                    }
214
                }
215
            }
216
        }
217
        throw new EE_Error(
218
            sprintf(
219
                __('The provided GMT offset (%1$s), is invalid, please check with %2$sthis list%3$s for what valid timezones can be used',
220
                    'event_espresso'),
221
                $gmt_offset,
222
                '<a href="http://www.php.net/manual/en/timezones.php">',
223
                '</a>'
224
            )
225
        );
226
    }
227
228
229
    /**
230
     * @access public
231
     * @param string $timezone_string
232
     */
233
    public static function timezone_select_input($timezone_string = '')
234
    {
235
        // get WP date time format
236
        $datetime_format = get_option('date_format') . ' ' . get_option('time_format');
237
        // if passed a value, then use that, else get WP option
238
        $timezone_string = ! empty($timezone_string) ? $timezone_string : get_option('timezone_string');
239
        // check if the timezone is valid but don't throw any errors if it isn't
240
        $timezone_string = EEH_DTT_Helper::validate_timezone($timezone_string, false);
241
        $gmt_offset      = get_option('gmt_offset');
242
243
        $check_zone_info = true;
244
        if (empty($timezone_string)) {
245
            // Create a UTC+- zone if no timezone string exists
246
            $check_zone_info = false;
247
            if ($gmt_offset > 0) {
248
                $timezone_string = 'UTC+' . $gmt_offset;
249
            } elseif ($gmt_offset < 0) {
250
                $timezone_string = 'UTC' . $gmt_offset;
251
            } else {
252
                $timezone_string = 'UTC';
253
            }
254
        }
255
        ?>
256
257
        <p>
258
            <label for="timezone_string"><?php _e('timezone'); ?></label>
259
            <select id="timezone_string" name="timezone_string">
260
                <?php echo wp_timezone_choice($timezone_string); ?>
261
            </select>
262
            <br/>
263
            <span class="description"><?php _e('Choose a city in the same timezone as the event.'); ?></span>
264
        </p>
265
266
        <p>
267
        <span><?php
268
            printf(
269
                __('%1$sUTC%2$s time is %3$s'),
270
                '<abbr title="Coordinated Universal Time">',
271
                '</abbr>',
272
                '<code>' . date_i18n($datetime_format, false, true) . '</code>'
273
            );
274
            ?></span>
275
        <?php if (! empty($timezone_string) || ! empty($gmt_offset)) : ?>
276
        <br/><span><?php printf(__('Local time is %1$s'), '<code>' . date_i18n($datetime_format) . '</code>'); ?></span>
277
    <?php endif; ?>
278
279
        <?php if ($check_zone_info && $timezone_string) : ?>
280
        <br/>
281
        <span>
282
					<?php
283
                    // Set TZ so localtime works.
284
                    date_default_timezone_set($timezone_string);
285
                    $now = localtime(time(), true);
286
                    if ($now['tm_isdst']) {
287
                        _e('This timezone is currently in daylight saving time.');
288
                    } else {
289
                        _e('This timezone is currently in standard time.');
290
                    }
291
                    ?>
292
            <br/>
293
            <?php
294
            if (function_exists('timezone_transitions_get')) {
295
                $found                   = false;
296
                $date_time_zone_selected = new DateTimeZone($timezone_string);
297
                $tz_offset               = timezone_offset_get($date_time_zone_selected, date_create());
298
                $right_now               = time();
299
                $tr['isdst']             = false;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$tr was never initialized. Although not strictly required by PHP, it is generally a good practice to add $tr = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
300
                foreach (timezone_transitions_get($date_time_zone_selected) as $tr) {
301
                    if ($tr['ts'] > $right_now) {
302
                        $found = true;
303
                        break;
304
                    }
305
                }
306
307
                if ($found) {
308
                    $message = $tr['isdst'] ?
309
                        __(' Daylight saving time begins on: %s.') :
310
                        __(' Standard time begins  on: %s.');
311
                    // Add the difference between the current offset and the new offset to ts to get the correct transition time from date_i18n().
312
                    printf($message,
313
                        '<code >' . date_i18n($datetime_format, $tr['ts'] + ($tz_offset - $tr['offset'])) . '</code >');
314
                } else {
315
                    _e('This timezone does not observe daylight saving time.');
316
                }
317
            }
318
            // Set back to UTC.
319
            date_default_timezone_set('UTC');
320
            ?>
321
				</span></p>
322
        <?php
323
    endif;
324
    }
325
326
327
    /**
328
     * This method will take an incoming unix timestamp and add the offset to it for the given timezone_string.
329
     * If no unix timestamp is given then time() is used.  If no timezone is given then the set timezone string for
330
     * the site is used.
331
     * This is used typically when using a Unix timestamp any core WP functions that expect their specially
332
     * computed timestamp (i.e. date_i18n() )
333
     *
334
     * @param int    $unix_timestamp                  if 0, then time() will be used.
335
     * @param string $timezone_string                 timezone_string. If empty, then the current set timezone for the
336
     *                                                site will be used.
337
     * @return int      $unix_timestamp with the offset applied for the given timezone.
338
     */
339
    public static function get_timestamp_with_offset($unix_timestamp = 0, $timezone_string = '')
340
    {
341
        $unix_timestamp  = $unix_timestamp === 0 ? time() : (int)$unix_timestamp;
342
        $timezone_string = self::get_valid_timezone_string($timezone_string);
343
        $TimeZone        = new DateTimeZone($timezone_string);
344
345
        $DateTime = new DateTime('@' . $unix_timestamp, $TimeZone);
346
        $offset   = timezone_offset_get($TimeZone, $DateTime);
347
        return (int)$DateTime->format('U') + (int)$offset;
348
    }
349
350
351
    /**
352
     *    _set_date_time_field
353
     *    modifies EE_Base_Class EE_Datetime_Field objects
354
     *
355
     * @param  EE_Base_Class $obj                 EE_Base_Class object
356
     * @param    DateTime    $DateTime            PHP DateTime object
357
     * @param  string        $datetime_field_name the datetime fieldname to be manipulated
358
     * @return    EE_Base_Class
359
     */
360
    protected static function _set_date_time_field(EE_Base_Class $obj, DateTime $DateTime, $datetime_field_name)
361
    {
362
        // grab current datetime format
363
        $current_format = $obj->get_format();
364
        // set new full timestamp format
365
        $obj->set_date_format(EE_Datetime_Field::mysql_date_format);
366
        $obj->set_time_format(EE_Datetime_Field::mysql_time_format);
367
        // set the new date value using a full timestamp format so that no data is lost
368
        $obj->set($datetime_field_name, $DateTime->format(EE_Datetime_Field::mysql_timestamp_format));
369
        // reset datetime formats
370
        $obj->set_date_format($current_format[0]);
371
        $obj->set_time_format($current_format[1]);
372
        return $obj;
373
    }
374
375
376
    /**
377
     *    date_time_add
378
     *    helper for doing simple datetime calculations on a given datetime from EE_Base_Class
379
     *    and modifying it IN the EE_Base_Class so you don't have to do anything else.
380
     *
381
     * @param  EE_Base_Class $obj                 EE_Base_Class object
382
     * @param  string        $datetime_field_name name of the EE_Datetime_Filed datatype db column to be manipulated
383
     * @param  string        $period              what you are adding. The options are (years, months, days, hours,
384
     *                                            minutes, seconds) defaults to years
385
     * @param  integer       $value               what you want to increment the time by
386
     * @return EE_Base_Class           return the EE_Base_Class object so right away you can do something with it
387
     *                                 (chaining)
388
     */
389 View Code Duplication
    public static function date_time_add(EE_Base_Class $obj, $datetime_field_name, $period = 'years', $value = 1)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
390
    {
391
        //get the raw UTC date.
392
        $DateTime = $obj->get_DateTime_object($datetime_field_name);
393
        $DateTime = EEH_DTT_Helper::calc_date($DateTime, $period, $value);
394
        return EEH_DTT_Helper::_set_date_time_field($obj, $DateTime, $datetime_field_name);
395
    }
396
397
398
    /**
399
     *    date_time_subtract
400
     *    same as date_time_add except subtracting value instead of adding.
401
     *
402
     * @param \EE_Base_Class $obj
403
     * @param  string        $datetime_field_name name of the EE_Datetime_Filed datatype db column to be manipulated
404
     * @param string         $period
405
     * @param int            $value
406
     * @return \EE_Base_Class
407
     */
408 View Code Duplication
    public static function date_time_subtract(EE_Base_Class $obj, $datetime_field_name, $period = 'years', $value = 1)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
409
    {
410
        //get the raw UTC date
411
        $DateTime = $obj->get_DateTime_object($datetime_field_name);
412
        $DateTime = EEH_DTT_Helper::calc_date($DateTime, $period, $value, '-');
413
        return EEH_DTT_Helper::_set_date_time_field($obj, $DateTime, $datetime_field_name);
414
    }
415
416
417
    /**
418
     * Simply takes an incoming DateTime object and does calculations on it based on the incoming parameters
419
     *
420
     * @param  DateTime $DateTime DateTime object
421
     * @param  string   $period   a value to indicate what interval is being used in the calculation. The options are
422
     *                            'years', 'months', 'days', 'hours', 'minutes', 'seconds'. Defaults to years.
423
     * @param  integer  $value    What you want to increment the date by
424
     * @param  string   $operand  What operand you wish to use for the calculation
425
     * @return \DateTime return whatever type came in.
426
     * @throws \EE_Error
427
     */
428
    protected static function _modify_datetime_object(DateTime $DateTime, $period = 'years', $value = 1, $operand = '+')
429
    {
430
        if (! $DateTime instanceof DateTime) {
431
            throw new EE_Error(
432
                sprintf(
433
                    __('Expected a PHP DateTime object, but instead received %1$s', 'event_espresso'),
434
                    print_r($DateTime, true)
435
                )
436
            );
437
        }
438
        switch ($period) {
439
            case 'years' :
440
                $value = 'P' . $value . 'Y';
441
                break;
442
            case 'months' :
443
                $value = 'P' . $value . 'M';
444
                break;
445
            case 'weeks' :
446
                $value = 'P' . $value . 'W';
447
                break;
448
            case 'days' :
449
                $value = 'P' . $value . 'D';
450
                break;
451
            case 'hours' :
452
                $value = 'PT' . $value . 'H';
453
                break;
454
            case 'minutes' :
455
                $value = 'PT' . $value . 'M';
456
                break;
457
            case 'seconds' :
458
                $value = 'PT' . $value . 'S';
459
                break;
460
        }
461
        switch ($operand) {
462
            case '+':
463
                $DateTime->add(new DateInterval($value));
464
                break;
465
            case '-':
466
                $DateTime->sub(new DateInterval($value));
467
                break;
468
        }
469
        return $DateTime;
470
    }
471
472
473
    /**
474
     * Simply takes an incoming Unix timestamp and does calculations on it based on the incoming parameters
475
     *
476
     * @param  int     $timestamp Unix timestamp
477
     * @param  string  $period    a value to indicate what interval is being used in the calculation. The options are
478
     *                            'years', 'months', 'days', 'hours', 'minutes', 'seconds'. Defaults to years.
479
     * @param  integer $value     What you want to increment the date by
480
     * @param  string  $operand   What operand you wish to use for the calculation
481
     * @return \DateTime return whatever type came in.
482
     * @throws \EE_Error
483
     */
484
    protected static function _modify_timestamp($timestamp, $period = 'years', $value = 1, $operand = '+')
485
    {
486
        if (! preg_match(EE_Datetime_Field::unix_timestamp_regex, $timestamp)) {
487
            throw new EE_Error(
488
                sprintf(
489
                    __('Expected a Unix timestamp, but instead received %1$s', 'event_espresso'),
490
                    print_r($timestamp, true)
491
                )
492
            );
493
        }
494
        switch ($period) {
495
            case 'years' :
496
                $value = YEAR_IN_SECONDS * $value;
497
                break;
498
            case 'months' :
499
                $value = YEAR_IN_SECONDS / 12 * $value;
500
                break;
501
            case 'weeks' :
502
                $value = WEEK_IN_SECONDS * $value;
503
                break;
504
            case 'days' :
505
                $value = DAY_IN_SECONDS * $value;
506
                break;
507
            case 'hours' :
508
                $value = HOUR_IN_SECONDS * $value;
509
                break;
510
            case 'minutes' :
511
                $value = MINUTE_IN_SECONDS * $value;
512
                break;
513
        }
514
        switch ($operand) {
515
            case '+':
516
                $timestamp += $value;
517
                break;
518
            case '-':
519
                $timestamp -= $value;
520
                break;
521
        }
522
        return $timestamp;
523
    }
524
525
526
    /**
527
     * Simply takes an incoming UTC timestamp or DateTime object and does calculations on it based on the incoming
528
     * parameters and returns the new timestamp or DateTime.
529
     *
530
     * @param  int | DateTime $DateTime_or_timestamp DateTime object or Unix timestamp
531
     * @param  string         $period                a value to indicate what interval is being used in the
532
     *                                               calculation. The options are 'years', 'months', 'days', 'hours',
533
     *                                               'minutes', 'seconds'. Defaults to years.
534
     * @param  integer        $value                 What you want to increment the date by
535
     * @param  string         $operand               What operand you wish to use for the calculation
536
     * @return mixed string|DateTime          return whatever type came in.
537
     */
538
    public static function calc_date($DateTime_or_timestamp, $period = 'years', $value = 1, $operand = '+')
539
    {
540
        if ($DateTime_or_timestamp instanceof DateTime) {
541
            return EEH_DTT_Helper::_modify_datetime_object($DateTime_or_timestamp, $period, $value, $operand);
542
        } else if (preg_match(EE_Datetime_Field::unix_timestamp_regex, $DateTime_or_timestamp)) {
543
            return EEH_DTT_Helper::_modify_timestamp($DateTime_or_timestamp, $period, $value, $operand);
544
        } else {
545
            //error
546
            return $DateTime_or_timestamp;
547
        }
548
    }
549
550
551
    /**
552
     * The purpose of this helper method is to receive an incoming format string in php date/time format
553
     * and spit out the js and moment.js equivalent formats.
554
     * Note, if no format string is given, then it is assumed the user wants what is set for WP.
555
     * Note, js date and time formats are those used by the jquery-ui datepicker and the jquery-ui date-
556
     * time picker.
557
     *
558
     * @see http://stackoverflow.com/posts/16725290/ for the code inspiration.
559
     * @param null $date_format_string
560
     * @param null $time_format_string
561
     * @return array
562
     *                array(
563
     *                'js' => array (
564
     *                'date' => //date format
565
     *                'time' => //time format
566
     *                ),
567
     *                'moment' => //date and time format.
568
     *                )
569
     */
570
    public static function convert_php_to_js_and_moment_date_formats(
571
        $date_format_string = null,
572
        $time_format_string = null
573
    ) {
574
        if ($date_format_string === null) {
575
            $date_format_string = get_option('date_format');
576
        }
577
578
        if ($time_format_string === null) {
579
            $time_format_string = get_option('time_format');
580
        }
581
582
        $date_format = self::_php_to_js_moment_converter($date_format_string);
583
        $time_format = self::_php_to_js_moment_converter($time_format_string);
584
585
        return array(
586
            'js'     => array(
587
                'date' => $date_format['js'],
588
                'time' => $time_format['js'],
589
            ),
590
            'moment' => $date_format['moment'] . ' ' . $time_format['moment'],
591
        );
592
    }
593
594
595
    /**
596
     * This converts incoming format string into js and moment variations.
597
     *
598
     * @param string $format_string incoming php format string
599
     * @return array js and moment formats.
600
     */
601
    protected static function _php_to_js_moment_converter($format_string)
602
    {
603
        /**
604
         * This is a map of symbols for formats.
605
         * The index is the php symbol, the equivalent values are in the array.
606
         *
607
         * @var array
608
         */
609
        $symbols_map      = array(
610
            // Day
611
            //01
612
            'd' => array(
613
                'js'     => 'dd',
614
                'moment' => 'DD',
615
            ),
616
            //Mon
617
            'D' => array(
618
                'js'     => 'D',
619
                'moment' => 'ddd',
620
            ),
621
            //1,2,...31
622
            'j' => array(
623
                'js'     => 'd',
624
                'moment' => 'D',
625
            ),
626
            //Monday
627
            'l' => array(
628
                'js'     => 'DD',
629
                'moment' => 'dddd',
630
            ),
631
            //ISO numeric representation of the day of the week (1-6)
632
            'N' => array(
633
                'js'     => '',
634
                'moment' => 'E',
635
            ),
636
            //st,nd.rd
637
            'S' => array(
638
                'js'     => '',
639
                'moment' => 'o',
640
            ),
641
            //numeric representation of day of week (0-6)
642
            'w' => array(
643
                'js'     => '',
644
                'moment' => 'd',
645
            ),
646
            //day of year starting from 0 (0-365)
647
            'z' => array(
648
                'js'     => 'o',
649
                'moment' => 'DDD' //note moment does not start with 0 so will need to modify by subtracting 1
650
            ),
651
            // Week
652
            //ISO-8601 week number of year (weeks starting on monday)
653
            'W' => array(
654
                'js'     => '',
655
                'moment' => 'w',
656
            ),
657
            // Month
658
            // January...December
659
            'F' => array(
660
                'js'     => 'MM',
661
                'moment' => 'MMMM',
662
            ),
663
            //01...12
664
            'm' => array(
665
                'js'     => 'mm',
666
                'moment' => 'MM',
667
            ),
668
            //Jan...Dec
669
            'M' => array(
670
                'js'     => 'M',
671
                'moment' => 'MMM',
672
            ),
673
            //1-12
674
            'n' => array(
675
                'js'     => 'm',
676
                'moment' => 'M',
677
            ),
678
            //number of days in given month
679
            't' => array(
680
                'js'     => '',
681
                'moment' => '',
682
            ),
683
            // Year
684
            //whether leap year or not 1/0
685
            'L' => array(
686
                'js'     => '',
687
                'moment' => '',
688
            ),
689
            //ISO-8601 year number
690
            'o' => array(
691
                'js'     => '',
692
                'moment' => 'GGGG',
693
            ),
694
            //1999...2003
695
            'Y' => array(
696
                'js'     => 'yy',
697
                'moment' => 'YYYY',
698
            ),
699
            //99...03
700
            'y' => array(
701
                'js'     => 'y',
702
                'moment' => 'YY',
703
            ),
704
            // Time
705
            // am/pm
706
            'a' => array(
707
                'js'     => 'tt',
708
                'moment' => 'a',
709
            ),
710
            // AM/PM
711
            'A' => array(
712
                'js'     => 'TT',
713
                'moment' => 'A',
714
            ),
715
            // Swatch Internet Time?!?
716
            'B' => array(
717
                'js'     => '',
718
                'moment' => '',
719
            ),
720
            //1...12
721
            'g' => array(
722
                'js'     => 'h',
723
                'moment' => 'h',
724
            ),
725
            //0...23
726
            'G' => array(
727
                'js'     => 'H',
728
                'moment' => 'H',
729
            ),
730
            //01...12
731
            'h' => array(
732
                'js'     => 'hh',
733
                'moment' => 'hh',
734
            ),
735
            //00...23
736
            'H' => array(
737
                'js'     => 'HH',
738
                'moment' => 'HH',
739
            ),
740
            //00..59
741
            'i' => array(
742
                'js'     => 'mm',
743
                'moment' => 'mm',
744
            ),
745
            //seconds... 00...59
746
            's' => array(
747
                'js'     => 'ss',
748
                'moment' => 'ss',
749
            ),
750
            //microseconds
751
            'u' => array(
752
                'js'     => '',
753
                'moment' => '',
754
            ),
755
        );
756
        $jquery_ui_format = "";
757
        $moment_format    = "";
758
        $escaping         = false;
759
        for ($i = 0; $i < strlen($format_string); $i++) {
760
            $char = $format_string[$i];
761
            if ($char === '\\') { // PHP date format escaping character
762
                $i++;
763
                if ($escaping) {
764
                    $jquery_ui_format .= $format_string[$i];
765
                    $moment_format .= $format_string[$i];
766
                } else {
767
                    $jquery_ui_format .= '\'' . $format_string[$i];
768
                    $moment_format .= $format_string[$i];
769
                }
770
                $escaping = true;
771
            } else {
772
                if ($escaping) {
773
                    $jquery_ui_format .= "'";
774
                    $moment_format .= "'";
775
                    $escaping = false;
776
                }
777
                if (isset($symbols_map[$char])) {
778
                    $jquery_ui_format .= $symbols_map[$char]['js'];
779
                    $moment_format .= $symbols_map[$char]['moment'];
780
                } else {
781
                    $jquery_ui_format .= $char;
782
                    $moment_format .= $char;
783
                }
784
            }
785
        }
786
        return array('js' => $jquery_ui_format, 'moment' => $moment_format);
787
    }
788
789
790
    /**
791
     * This takes an incoming format string and validates it to ensure it will work fine with PHP.
792
     *
793
     * @param string $format_string   Incoming format string for php date().
794
     * @return mixed bool|array  If all is okay then TRUE is returned.  Otherwise an array of validation
795
     *                                errors is returned.  So for client code calling, check for is_array() to
796
     *                                indicate failed validations.
797
     */
798
    public static function validate_format_string($format_string)
799
    {
800
        $error_msg = array();
801
        //time format checks
802
        switch (true) {
803
            case   strpos($format_string, 'h') !== false  :
804
            case   strpos($format_string, 'g') !== false :
805
                /**
806
                 * if the time string has a lowercase 'h' which == 12 hour time format and there
807
                 * is not any ante meridiem format ('a' or 'A').  Then throw an error because its
808
                 * too ambiguous and PHP won't be able to figure out whether 1 = 1pm or 1am.
809
                 */
810
                if (strpos(strtoupper($format_string), 'A') === false) {
811
                    $error_msg[] = __('There is a  time format for 12 hour time but no  "a" or "A" to indicate am/pm.  Without this distinction, PHP is unable to determine if a "1" for the hour value equals "1pm" or "1am".',
812
                        'event_espresso');
813
                }
814
                break;
815
816
        }
817
818
        return empty($error_msg) ? true : $error_msg;
819
    }
820
821
822
    /**
823
     *     If the the first date starts at midnight on one day, and the next date ends at midnight on the
824
     *     very next day then this method will return true.
825
     *    If $date_1 = 2015-12-15 00:00:00 and $date_2 = 2015-12-16 00:00:00 then this function will return true.
826
     *    If $date_1 = 2015-12-15 03:00:00 and $date_2 = 2015-12_16 03:00:00 then this function will return false.
827
     *    If $date_1 = 2015-12-15 00:00:00 and $date_2 = 2015-12-15 00:00:00 then this function will return true.
828
     *
829
     * @param mixed $date_1
830
     * @param mixed $date_2
831
     * @return bool
832
     */
833
    public static function dates_represent_one_24_hour_date($date_1, $date_2)
834
    {
835
836
        if (
837
            (! $date_1 instanceof DateTime || ! $date_2 instanceof DateTime) ||
838
            ($date_1->format(EE_Datetime_Field::mysql_time_format) != '00:00:00' || $date_2->format(EE_Datetime_Field::mysql_time_format) != '00:00:00')
839
        ) {
840
            return false;
841
        }
842
        return $date_2->format('U') - $date_1->format('U') == 86400 ? true : false;
843
    }
844
845
846
    /**
847
     * This returns the appropriate query interval string that can be used in sql queries involving mysql Date
848
     * Functions.
849
     *
850
     * @param string $timezone_string    A timezone string in a valid format to instantiate a DateTimeZone object.
851
     * @param string $field_for_interval The Database field that is the interval is applied to in the query.
852
     * @return string
853
     */
854
    public static function get_sql_query_interval_for_offset($timezone_string, $field_for_interval)
855
    {
856
        try {
857
            /** need to account for timezone offset on the selects */
858
            $DateTimeZone = new DateTimeZone($timezone_string);
859
        } catch (Exception $e) {
860
            $DateTimeZone = null;
861
        }
862
863
        /**
864
         * Note get_option( 'gmt_offset') returns a value in hours, whereas DateTimeZone::getOffset returns values in seconds.
865
         * Hence we do the calc for DateTimeZone::getOffset.
866
         */
867
        $offset         = $DateTimeZone instanceof DateTimeZone ? ($DateTimeZone->getOffset(new DateTime('now'))) / HOUR_IN_SECONDS : get_option('gmt_offset');
868
        $query_interval = $offset < 0
869
            ? 'DATE_SUB(' . $field_for_interval . ', INTERVAL ' . $offset * -1 . ' HOUR)'
870
            : 'DATE_ADD(' . $field_for_interval . ', INTERVAL ' . $offset . ' HOUR)';
871
        return $query_interval;
872
    }
873
874
    /**
875
     * Retrieves the site's default timezone and returns it formatted so it's ready for display
876
     * to users. If you want to customize how its displayed feel free to fetch the 'timezone_string'
877
     * and 'gmt_offset' WordPress options directly; or use the filter
878
     * FHEE__EEH_DTT_Helper__get_timezone_string_for_display
879
     * (although note that we remove any HTML that may be added)
880
     *
881
     * @return string
882
     */
883
    public static function get_timezone_string_for_display()
884
    {
885
        $pretty_timezone = apply_filters('FHEE__EEH_DTT_Helper__get_timezone_string_for_display', '');
886
        if (! empty($pretty_timezone)) {
887
            return esc_html($pretty_timezone);
888
        }
889
        $timezone_string = get_option('timezone_string');
890
        if ($timezone_string) {
891
            static $mo_loaded = false;
892
            // Load translations for continents and cities just like wp_timezone_choice does
893
            if (! $mo_loaded) {
894
                $locale = get_locale();
895
                $mofile = WP_LANG_DIR . '/continents-cities-' . $locale . '.mo';
896
                load_textdomain('continents-cities', $mofile);
897
                $mo_loaded = true;
898
            }
899
            //well that was easy.
900
            $parts = explode('/', $timezone_string);
901
            //remove the continent
902
            unset($parts[0]);
903
            $t_parts = array();
904
            foreach ($parts as $part) {
905
                $t_parts[] = translate(str_replace('_', ' ', $part), 'continents-cities');
906
            }
907
            return implode(' - ', $t_parts);
908
        }
909
        //they haven't set the timezone string, so let's return a string like "UTC+1"
910
        $gmt_offset = get_option('gmt_offset');
911
        if (intval($gmt_offset) >= 0) {
912
            $prefix = '+';
913
        } else {
914
            $prefix = '';
915
        }
916
        $parts = explode('.', (string)$gmt_offset);
917
        if (count($parts) === 1) {
918
            $parts[1] = '00';
919
        } else {
920
            //convert the part after the decimal, eg "5" (from x.5) or "25" (from x.25)
921
            //to minutes, eg 30 or 15, respectively
922
            $hour_fraction = (float)('0.' . $parts[1]);
923
            $parts[1]      = (string)$hour_fraction * 60;
924
        }
925
        return sprintf(__('UTC%1$s', 'event_espresso'), $prefix . implode(':', $parts));
926
    }
927
928
929
930
    /**
931
     * So PHP does this awesome thing where if you are trying to get a timestamp
932
     * for a month using a string like "February" or "February 2017",
933
     * and you don't specify a day as part of your string,
934
     * then PHP will use whatever the current day of the month is.
935
     * IF the current day of the month happens to be the 30th or 31st,
936
     * then PHP gets really confused by a date like February 30,
937
     * so instead of saying
938
     *      "Hey February only has 28 days (this year)...
939
     *      ...you must have meant the last day of the month!"
940
     * PHP does the next most logical thing, and bumps the date up to March 2nd,
941
     * because someone requesting February 30th obviously meant March 1st!
942
     * The way around this is to always set the day to the first,
943
     * so that the month will stay on the month you wanted.
944
     * this method will add that "1" into your date regardless of the format.
945
     *
946
     * @param string $month
947
     * @return string
948
     */
949
    public static function first_of_month_timestamp($month = '')
950
    {
951
        $month = (string)$month;
952
        $year = '';
953
        // check if the incoming string has a year in it or not
954
       if (preg_match('/\b\d{4}\b/', $month, $matches)) {
955
           $year = $matches[0];
956
           // ten remove that from the month string as well as any spaces
957
           $month = trim(str_replace($year, '', $month));
958
           // add a space before the year
959
           $year = " {$year}";
960
        }
961
        // return timestamp for something like "February 1 2017"
962
        return strtotime("{$month} 1{$year}");
963
    }
964
965
	/**
966
     * This simply returns the timestamp for tomorrow (midnight next day) in this sites timezone.  So it may be midnight
967
	* for this sites timezone, but the timestamp could be some other time GMT.
968
    */
969
    public static function tomorrow()
970
	{
971
		//The multiplication of -1 ensures that we switch positive offsets to negative and negative offsets to positive
972
		//before adding to the timestamp.  Why? Because we want tomorrow to be for midnight the next day in THIS timezone
973
		//not an offset from midnight in UTC.  So if we're starting with UTC 00:00:00, then we want to make sure the
974
		//final timestamp is equivalent to midnight in this timezone as represented in GMT.
975
		return strtotime('tomorrow') + (self::get_site_timezone_gmt_offset()*-1);
976
	}
977
978
}// end class EEH_DTT_Helper
979