WEBVTT

1
00:00:00.000 --> 00:00:02.030
<v Jose>Hi, guys, and welcome back.</v>

2
00:00:02.030 --> 00:00:03.360
One of the most common problems

3
00:00:03.360 --> 00:00:05.990
with dates and times are time zones.

4
00:00:05.990 --> 00:00:06.823
So we're gonna look at

5
00:00:06.823 --> 00:00:10.480
how to handle time zones well using pytz,

6
00:00:10.480 --> 00:00:12.850
which is a Python library.

7
00:00:12.850 --> 00:00:15.640
First of all, though, what is a time zone?

8
00:00:15.640 --> 00:00:18.760
It is a region where the same standard time is used.

9
00:00:18.760 --> 00:00:21.180
And this is often geographical regions,

10
00:00:21.180 --> 00:00:22.200
but they don't have to be.

11
00:00:22.200 --> 00:00:24.600
So it is a little bit complicated.

12
00:00:24.600 --> 00:00:27.090
But the key thing is that, for different computers,

13
00:00:27.090 --> 00:00:29.850
right now the current time means a different time

14
00:00:29.850 --> 00:00:31.180
and potentially a different date.

15
00:00:31.180 --> 00:00:32.660
I'm sure you're all aware of this,

16
00:00:32.660 --> 00:00:34.630
but, for example, if we've got a map here,

17
00:00:34.630 --> 00:00:39.350
a computer in the US might think it's 11:47 AM on May 14th,

18
00:00:39.350 --> 00:00:41.960
and at the same time a computer in Japan

19
00:00:41.960 --> 00:00:45.890
may think it's 48 minutes past midnight on May 15th.

20
00:00:45.890 --> 00:00:48.600
So, clearly, different times, different dates,

21
00:00:48.600 --> 00:00:52.270
but it's actually the same actual point in time.

22
00:00:52.270 --> 00:00:54.810
So this can get quite complicated,

23
00:00:54.810 --> 00:00:57.180
especially when you start taking into account

24
00:00:57.180 --> 00:01:00.810
that some times of the year Japan

25
00:01:00.810 --> 00:01:02.480
may actually be on the same day

26
00:01:02.480 --> 00:01:05.910
because it may be 10 minutes to midnight on May 14th

27
00:01:05.910 --> 00:01:08.130
if they don't have daylight savings enabled.

28
00:01:08.130 --> 00:01:11.610
And, similarly, the US might go on to a different time.

29
00:01:11.610 --> 00:01:14.720
So this all gets quite tricky.

30
00:01:14.720 --> 00:01:17.950
And, fortunately, we don't have to handle this

31
00:01:17.950 --> 00:01:19.180
in our code ourselves.

32
00:01:19.180 --> 00:01:22.733
That's what pytz is for as well as a few other libraries.

33
00:01:23.890 --> 00:01:26.340
The important part of this, though, is that

34
00:01:26.340 --> 00:01:28.070
there is a central time zone,

35
00:01:28.070 --> 00:01:31.320
and that's UTC, Universal Time Coordinated.

36
00:01:31.320 --> 00:01:32.750
So this is the central time,

37
00:01:32.750 --> 00:01:35.420
and all other time zones can be described

38
00:01:35.420 --> 00:01:37.400
with UTC as a reference.

39
00:01:37.400 --> 00:01:40.140
Well, really, any time zone can be described

40
00:01:40.140 --> 00:01:41.790
with any other time zone as a reference.

41
00:01:41.790 --> 00:01:44.190
But UTC is the central one,

42
00:01:44.190 --> 00:01:46.740
doesn't have daylight savings enabled,

43
00:01:46.740 --> 00:01:49.310
so it's always the same time.

44
00:01:49.310 --> 00:01:51.970
CET, for example, the Central European time,

45
00:01:51.970 --> 00:01:56.970
is UTC +1 and you write that as +01:00.

46
00:01:57.420 --> 00:02:01.860
Whenever you see +01:00 at the end of a datetime,

47
00:02:01.860 --> 00:02:06.463
that means that that datetime is one hour ahead of CET.

48
00:02:07.860 --> 00:02:12.280
That means that datetime is one hour ahead of UTC.

49
00:02:12.280 --> 00:02:14.950
For PST, for example, Pacific Standard Time,

50
00:02:14.950 --> 00:02:16.370
you've got -6 hours,

51
00:02:16.370 --> 00:02:18.243
so you would write -06:00.

52
00:02:19.370 --> 00:02:23.760
Note that some time zones don't use :00.

53
00:02:23.760 --> 00:02:26.730
They have 30 minutes more of an offset

54
00:02:26.730 --> 00:02:30.140
over UTC than other time zones, or even 45 minutes.

55
00:02:30.140 --> 00:02:32.630
So you can't assume that all time zones

56
00:02:32.630 --> 00:02:34.750
are gonna be very regular, and so on.

57
00:02:34.750 --> 00:02:35.980
Also, time zones can change

58
00:02:35.980 --> 00:02:38.590
for any number of reasons, including political reasons.

59
00:02:38.590 --> 00:02:41.670
So there is a central time zone database

60
00:02:41.670 --> 00:02:44.540
that is generally kept up-to-date quite well,

61
00:02:44.540 --> 00:02:46.020
and that is what we're gonna be using

62
00:02:46.020 --> 00:02:48.980
to handle these time-zone transitions.

63
00:02:48.980 --> 00:02:52.540
pytz uses the central time zone database,

64
00:02:52.540 --> 00:02:56.380
so we are covered in that regard.

65
00:02:56.380 --> 00:03:00.220
Let's look at how to get the current date in UTC.

66
00:03:00.220 --> 00:03:01.830
The reason for this is because

67
00:03:01.830 --> 00:03:04.540
if we are always working in UTC,

68
00:03:04.540 --> 00:03:06.010
that makes our life a little bit easier.

69
00:03:06.010 --> 00:03:08.060
We don't have to worry about whether,

70
00:03:08.060 --> 00:03:09.820
when we are comparing two dates,

71
00:03:09.820 --> 00:03:12.160
one of them is actually less than the other

72
00:03:12.160 --> 00:03:13.510
even though it looks like it's more

73
00:03:13.510 --> 00:03:16.040
because of daylight savings, and so forth.

74
00:03:16.040 --> 00:03:18.460
So here's how we get the current date in UTC.

75
00:03:18.460 --> 00:03:20.900
We simply do datetime.datetime.now,

76
00:03:20.900 --> 00:03:22.580
we print the date today,

77
00:03:22.580 --> 00:03:24.570
and we know that this gives us

78
00:03:24.570 --> 00:03:28.100
the current date and time of your computer.

79
00:03:28.100 --> 00:03:31.090
And notice that at the end of that printout

80
00:03:31.090 --> 00:03:33.890
we don't have plus anything.

81
00:03:33.890 --> 00:03:36.920
So there's no datetime information.

82
00:03:36.920 --> 00:03:41.083
That means that we're working with a naive datetime object.

83
00:03:42.060 --> 00:03:44.740
So if your datetime object knows the timezone

84
00:03:44.740 --> 00:03:47.030
and the datetime time it represents,

85
00:03:47.030 --> 00:03:49.550
then we call that an aware datetime object.

86
00:03:49.550 --> 00:03:51.190
And if it doesn't have a time zone information,

87
00:03:51.190 --> 00:03:53.890
we call that a naive object.

88
00:03:53.890 --> 00:03:55.710
Here we've got an aware object.

89
00:03:55.710 --> 00:03:57.760
We are again creating the datetime

90
00:03:57.760 --> 00:03:59.820
using datetime.datetime.now,

91
00:03:59.820 --> 00:04:04.820
but now we're passing the time zone datetime.timezone.utc.

92
00:04:05.230 --> 00:04:06.720
And now you can see when we print it out

93
00:04:06.720 --> 00:04:11.040
we have +00:00 at the end.

94
00:04:11.040 --> 00:04:13.670
Also, Python knows that in my computer

95
00:04:13.670 --> 00:04:15.100
we are in BST at the moment,

96
00:04:15.100 --> 00:04:19.300
which is a +1 time zone or one hour ahead of UTC.

97
00:04:19.300 --> 00:04:20.860
So when we're converting to UTC,

98
00:04:20.860 --> 00:04:23.410
we go one hour backwards to five o'clock,

99
00:04:23.410 --> 00:04:25.000
well, seven minutes past five.

100
00:04:25.000 --> 00:04:29.090
So aware objects represent specific points in time.

101
00:04:29.090 --> 00:04:34.090
Naive objects only make the suggestion of a point in time.

102
00:04:34.150 --> 00:04:35.630
The reason for that is because

103
00:04:35.630 --> 00:04:38.870
if I give you a naive datetime object,

104
00:04:38.870 --> 00:04:40.910
you don't know where I generated it

105
00:04:40.910 --> 00:04:42.270
and what time zone it represents.

106
00:04:42.270 --> 00:04:45.090
So, therefore, it could mean yesterday

107
00:04:45.090 --> 00:04:46.230
if you were in the US

108
00:04:46.230 --> 00:04:48.260
or it could mean tomorrow if you were in Japan.

109
00:04:48.260 --> 00:04:51.720
So naive data objects, not all that useful.

110
00:04:51.720 --> 00:04:55.330
But aware datetime objects do have a lot of use.

111
00:04:55.330 --> 00:04:57.550
Working in your code with naive datetime objects

112
00:04:57.550 --> 00:04:59.620
can be dangerous because different parts

113
00:04:59.620 --> 00:05:02.557
of your code base may think that, "Oh, if it doesn't have

114
00:05:02.557 --> 00:05:05.320
"a time zone, it represents UTC time."

115
00:05:05.320 --> 00:05:06.680
But some other parts might think

116
00:05:06.680 --> 00:05:08.500
that it represents PST time.

117
00:05:08.500 --> 00:05:10.510
And you'll end up with code that does different things

118
00:05:10.510 --> 00:05:12.310
with that datetime.

119
00:05:12.310 --> 00:05:13.390
It's almost always a good idea

120
00:05:13.390 --> 00:05:17.010
to store time zone information in your datetime objects.

121
00:05:17.010 --> 00:05:20.390
So here's how we can convert between time zones with pytz.

122
00:05:20.390 --> 00:05:22.953
The pytz module comes with a whole database of time zones

123
00:05:22.953 --> 00:05:25.970
that is accessible by their official names.

124
00:05:25.970 --> 00:05:29.260
So here we are importing datetime and pytz

125
00:05:29.260 --> 00:05:32.580
and then we are creating or getting the time zone

126
00:05:32.580 --> 00:05:34.270
using US/Eastern.

127
00:05:34.270 --> 00:05:35.830
That's the official name of the time zone,

128
00:05:35.830 --> 00:05:37.490
and I'm gonna leave a link in the resources section

129
00:05:37.490 --> 00:05:40.280
of this video linking you to the whole database.

130
00:05:40.280 --> 00:05:43.820
So what does is essentially it let's pytz know

131
00:05:43.820 --> 00:05:47.020
what we're working with in terms of a time zone.

132
00:05:47.020 --> 00:05:50.280
Now we then go ahead and grab the current time

133
00:05:50.280 --> 00:05:52.560
without any time zone information.

134
00:05:52.560 --> 00:05:55.940
But this datetime object, assuming that my computer

135
00:05:55.940 --> 00:05:59.060
is currently in US/Eastern time zone,

136
00:05:59.060 --> 00:06:02.120
does represent the current US/Eastern time

137
00:06:02.120 --> 00:06:04.170
because it's the current time of my computer.

138
00:06:04.170 --> 00:06:05.560
So then what we'll do is we'll

139
00:06:05.560 --> 00:06:08.830
convert the naive datetime to an aware one.

140
00:06:08.830 --> 00:06:10.670
But that doesn't change the time.

141
00:06:10.670 --> 00:06:12.950
It just adds the time zone information.

142
00:06:12.950 --> 00:06:15.560
So you can see here, we grabbed the local time,

143
00:06:15.560 --> 00:06:18.930
and we printed it out, and that's 11:47.

144
00:06:18.930 --> 00:06:21.370
And then we did eastern.localize.

145
00:06:21.370 --> 00:06:24.120
So that's the time zone we created .localise.

146
00:06:24.120 --> 00:06:25.550
And we pass in the local_time.

147
00:06:25.550 --> 00:06:27.210
And that gives us the exact same time,

148
00:06:27.210 --> 00:06:29.480
but now it has this minus five there.

149
00:06:29.480 --> 00:06:33.520
So, again, if my computer is currently in this time zone,

150
00:06:33.520 --> 00:06:37.970
then this is how we convert a local time to that time zone.

151
00:06:37.970 --> 00:06:40.940
The very important part, of course, is to make sure

152
00:06:40.940 --> 00:06:43.450
that my computer is using this time zone.

153
00:06:43.450 --> 00:06:45.380
The way to do that is to ask users

154
00:06:45.380 --> 00:06:46.760
what time zone they were using

155
00:06:46.760 --> 00:06:49.310
or potentially to store that in a database

156
00:06:49.310 --> 00:06:50.580
beside the user information

157
00:06:50.580 --> 00:06:53.440
or maybe a config file, et cetera.

158
00:06:53.440 --> 00:06:57.660
Now that we have the localised date and time,

159
00:06:57.660 --> 00:06:59.760
we can convert between time zones.

160
00:06:59.760 --> 00:07:01.630
So here we've got more or less the same code.

161
00:07:01.630 --> 00:07:03.600
We've got the Eastern time zone there.

162
00:07:03.600 --> 00:07:05.070
We're grabbing the local time.

163
00:07:05.070 --> 00:07:06.400
We're converting it to Eastern,

164
00:07:06.400 --> 00:07:08.180
and we've got our Eastern time zone.

165
00:07:08.180 --> 00:07:10.160
Now if we have another time zone,

166
00:07:10.160 --> 00:07:14.040
for example, Europe/Amsterdam, we can do Eastern time,

167
00:07:14.040 --> 00:07:16.110
that is, this time here .astimezone

168
00:07:17.220 --> 00:07:19.230
and pass in the Amsterdam time zone.

169
00:07:19.230 --> 00:07:20.770
And this is all part of pytz.

170
00:07:20.770 --> 00:07:23.670
And what it's gonna do is it's going to change

171
00:07:23.670 --> 00:07:27.450
the date and time so that this point in time, Eastern time,

172
00:07:27.450 --> 00:07:29.270
is the same as it would be

173
00:07:29.270 --> 00:07:30.960
if it had the Amsterdam time zone.

174
00:07:30.960 --> 00:07:34.930
So it's gonna essentially go forward by six hours.

175
00:07:34.930 --> 00:07:38.560
Note that this deals with daylight savings changes for you.

176
00:07:38.560 --> 00:07:42.070
So if one of these time zones was using daylight savings

177
00:07:42.070 --> 00:07:43.950
and the other one wasn't, for example,

178
00:07:43.950 --> 00:07:46.300
then it's gonna take care of that.

179
00:07:46.300 --> 00:07:48.420
If this looks a little bit complicated, don't worry.

180
00:07:48.420 --> 00:07:51.950
We're gonna simplify it right for you in just a moment.

181
00:07:51.950 --> 00:07:54.340
But first of all, let's look at how to use pytz

182
00:07:54.340 --> 00:07:56.233
to get the UTC time.

183
00:07:57.180 --> 00:07:58.600
We can do something like this.

184
00:07:58.600 --> 00:08:00.650
We can grab datetime.datetime.now,

185
00:08:00.650 --> 00:08:03.490
and that is the local time.

186
00:08:03.490 --> 00:08:05.400
Or we can do datetime.datetime.now,

187
00:08:05.400 --> 00:08:06.920
and then pass in a keyword argument,

188
00:08:06.920 --> 00:08:11.570
tz for time zone, =pytz.utc.

189
00:08:11.570 --> 00:08:15.230
And that is going to bring the time back one hour,

190
00:08:15.230 --> 00:08:18.080
because at the moment I am in BST, as I mentioned.

191
00:08:18.080 --> 00:08:21.500
So this is a way to get the current UTC time.

192
00:08:21.500 --> 00:08:25.970
So you've got, really, two main things to think about.

193
00:08:25.970 --> 00:08:29.450
The current user's time and time zone

194
00:08:29.450 --> 00:08:32.800
and the central UTC time and time zone.

195
00:08:32.800 --> 00:08:34.350
And here's how I recommend you work

196
00:08:34.350 --> 00:08:37.010
with time zones in your apps.

197
00:08:37.010 --> 00:08:40.280
First, you ask users for their local time,

198
00:08:40.280 --> 00:08:42.820
or you use datetime.datetime.now.

199
00:08:42.820 --> 00:08:45.223
And you also ask them for their time zone.

200
00:08:46.090 --> 00:08:49.000
Then you use their time zone to localise the local time

201
00:08:49.000 --> 00:08:52.230
so that you know, essentially, what point in time

202
00:08:52.230 --> 00:08:54.630
that datetime is representing.

203
00:08:54.630 --> 00:08:56.460
And then whenever you're working with a database,

204
00:08:56.460 --> 00:08:58.900
what you're gonna do is you're gonna convert

205
00:08:58.900 --> 00:09:02.320
that localised time into UTC.

206
00:09:02.320 --> 00:09:05.020
And you're always gonna store UTC in your database.

207
00:09:05.020 --> 00:09:09.520
So your database will always have UTC dates in it,

208
00:09:09.520 --> 00:09:12.933
and you'll know that they are +00:00.

209
00:09:13.940 --> 00:09:17.430
Then whenever you wanna go back and show the user a date,

210
00:09:17.430 --> 00:09:19.040
you grab their local time,

211
00:09:19.040 --> 00:09:22.830
and you convert the UTC time in your database to local time,

212
00:09:22.830 --> 00:09:24.740
and you print it out and show them.

213
00:09:24.740 --> 00:09:27.180
That way you only have to worry about time zones

214
00:09:27.180 --> 00:09:29.740
when you are showing the user something

215
00:09:29.740 --> 00:09:31.380
or getting something from them.

216
00:09:31.380 --> 00:09:33.730
And you never have to worry about time zones

217
00:09:33.730 --> 00:09:38.040
within your code because you'll always be working with UTC.

218
00:09:38.040 --> 00:09:41.380
So here's how you can do this.

219
00:09:41.380 --> 00:09:43.070
Assuming you've got user's time zone,

220
00:09:43.070 --> 00:09:46.120
which, again, might be in a database or a config file,

221
00:09:46.120 --> 00:09:50.340
you grab the datetime and you localise it into Eastern time

222
00:09:50.340 --> 00:09:54.469
and you do user_date.astimezone with pytz.utc.

223
00:09:54.469 --> 00:09:56.030
And that's gonna take their local time,

224
00:09:56.030 --> 00:09:57.800
convert it into UTC,

225
00:09:57.800 --> 00:09:59.930
and then you might save that into your database.

226
00:09:59.930 --> 00:10:01.100
And to go the other way round,

227
00:10:01.100 --> 00:10:02.360
well, you would do exactly the same,

228
00:10:02.360 --> 00:10:06.160
but you would do user_date.astimezone eastern.

229
00:10:06.160 --> 00:10:08.600
And that would give you the Eastern time equivalent.

230
00:10:08.600 --> 00:10:10.310
Thank you, guys, for joining me in this video.

231
00:10:10.310 --> 00:10:11.500
I hope this makes sense.

232
00:10:11.500 --> 00:10:14.750
Again, we've got the ebook that has all of this covered

233
00:10:14.750 --> 00:10:17.260
as well with the code examples and everything.

234
00:10:17.260 --> 00:10:20.380
So you can read that and review it if anything's unclear.

235
00:10:20.380 --> 00:10:21.320
Thank you, guys, for joining me.

236
00:10:21.320 --> 00:10:22.490
Thanks for watching this video,

237
00:10:22.490 --> 00:10:24.140
and I'll see you in the next one.

